Skip to content

Commit

Permalink
Add UTM parameters to Dashboard login buttons (#7639)
Browse files Browse the repository at this point in the history
* Add UTM parameters to Dashboard login buttons

* Revert "Add UTM parameters to Dashboard login buttons"

This reverts commit 568f12e.

* 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>
  • Loading branch information
panzarino and flotwig committed Jun 19, 2020
1 parent 869bcec commit dc2b50d
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 10 deletions.
6 changes: 6 additions & 0 deletions packages/desktop-gui/cypress/integration/login_spec.js
Expand Up @@ -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')
})
Expand Down
14 changes: 13 additions & 1 deletion packages/desktop-gui/cypress/integration/runs_list_spec.js
Expand Up @@ -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 () {
Expand Down Expand Up @@ -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
})
})
})
Expand Down
4 changes: 2 additions & 2 deletions packages/desktop-gui/src/auth/auth-api.js
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop-gui/src/auth/login-form.jsx
Expand Up @@ -115,7 +115,7 @@ class LoginForm extends Component {

this.setState({ isLoggingIn: true })

authApi.login()
authApi.login(this.props.utm)
.then(() => {
this.props.onSuccess()
})
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop-gui/src/auth/login-modal.jsx
Expand Up @@ -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>
)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop-gui/src/runs/runs-list.jsx
Expand Up @@ -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>
)
}
Expand Down
18 changes: 15 additions & 3 deletions packages/server/lib/gui/auth.js
Expand Up @@ -19,14 +19,15 @@ let authState
let openExternalAttempted = false
let authRedirectReached = false
let server
let utm

const _buildLoginRedirectUrl = (server) => {
const { port } = server.address()

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) {
Expand All @@ -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()
})
}
Expand All @@ -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,
Expand All @@ -68,6 +79,7 @@ const start = (onMessage) => {
})
}

utm = utmCode
authRedirectReached = false

return user.getBaseLoginUrl()
Expand Down Expand Up @@ -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 })

Expand Down
2 changes: 1 addition & 1 deletion packages/server/lib/gui/events.js
Expand Up @@ -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)

Expand Down
8 changes: 8 additions & 0 deletions packages/server/test/unit/gui/auth_spec.js
Expand Up @@ -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(() => {
Expand Down Expand Up @@ -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 () {
Expand Down

4 comments on commit dc2b50d

@cypress-bot

This comment was marked as off-topic.

@cypress-bot

This comment was marked as off-topic.

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on dc2b50d Jun 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AppVeyor has built the win32 ia32 version of the Test Runner.

You can install this pre-release platform-specific build using instructions at https://on.cypress.io/installing-cypress#Install-pre-release-version.

You will need to use custom CYPRESS_INSTALL_BINARY url and install Cypress using an url instead of the version.

Instructions are included below, depending on the shell you are using.

In Command Prompt (cmd.exe):

set CYPRESS_INSTALL_BINARY=https://cdn.cypress.io/beta/binary/4.9.0/win32-ia32/appveyor-develop-dc2b50d3516fbcbadac262586730ec1a152dcbd1-33630659/cypress.zip
npm install https://cdn.cypress.io/beta/npm/4.9.0/appveyor-develop-dc2b50d3516fbcbadac262586730ec1a152dcbd1-33630659/cypress.tgz

In PowerShell:

$env:CYPRESS_INSTALL_BINARY = https://cdn.cypress.io/beta/binary/4.9.0/win32-ia32/appveyor-develop-dc2b50d3516fbcbadac262586730ec1a152dcbd1-33630659/cypress.zip
npm install https://cdn.cypress.io/beta/npm/4.9.0/appveyor-develop-dc2b50d3516fbcbadac262586730ec1a152dcbd1-33630659/cypress.tgz

In Git Bash:

export CYPRESS_INSTALL_BINARY=https://cdn.cypress.io/beta/binary/4.9.0/win32-ia32/appveyor-develop-dc2b50d3516fbcbadac262586730ec1a152dcbd1-33630659/cypress.zip
npm install https://cdn.cypress.io/beta/npm/4.9.0/appveyor-develop-dc2b50d3516fbcbadac262586730ec1a152dcbd1-33630659/cypress.tgz

Using cross-env:

If the above commands do not work for you, you can also try using cross-env:

npm i -g cross-env
cross-env CYPRESS_INSTALL_BINARY=https://cdn.cypress.io/beta/binary/4.9.0/win32-ia32/appveyor-develop-dc2b50d3516fbcbadac262586730ec1a152dcbd1-33630659/cypress.zip npm install https://cdn.cypress.io/beta/npm/4.9.0/appveyor-develop-dc2b50d3516fbcbadac262586730ec1a152dcbd1-33630659/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on dc2b50d Jun 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

You can install this pre-release platform-specific build using instructions at https://on.cypress.io/installing-cypress#Install-pre-release-version.

You will need to use custom CYPRESS_INSTALL_BINARY url and install Cypress using an url instead of the version.

export CYPRESS_INSTALL_BINARY=https://cdn.cypress.io/beta/binary/4.9.0/darwin-x64/circle-develop-dc2b50d3516fbcbadac262586730ec1a152dcbd1-371644/cypress.zip
npm install https://cdn.cypress.io/beta/npm/4.9.0/circle-develop-dc2b50d3516fbcbadac262586730ec1a152dcbd1-371541/cypress.tgz

Please sign in to comment.