diff --git a/packages/desktop-gui/cypress/integration/login_spec.js b/packages/desktop-gui/cypress/integration/login_spec.js index 5a9ec1865502..82ce041e6c4a 100644 --- a/packages/desktop-gui/cypress/integration/login_spec.js +++ b/packages/desktop-gui/cypress/integration/login_spec.js @@ -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') }) diff --git a/packages/desktop-gui/cypress/integration/runs_list_spec.js b/packages/desktop-gui/cypress/integration/runs_list_spec.js index d62ee612b685..c47ceeade608 100644 --- a/packages/desktop-gui/cypress/integration/runs_list_spec.js +++ b/packages/desktop-gui/cypress/integration/runs_list_spec.js @@ -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 }) }) }) diff --git a/packages/desktop-gui/src/auth/auth-api.js b/packages/desktop-gui/src/auth/auth-api.js index 0da2a4fba17a..44e1e1f4be2e 100644 --- a/packages/desktop-gui/src/auth/auth-api.js +++ b/packages/desktop-gui/src/auth/auth-api.js @@ -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) diff --git a/packages/desktop-gui/src/auth/login-form.jsx b/packages/desktop-gui/src/auth/login-form.jsx index d24ec89cb738..4d159f29084e 100644 --- a/packages/desktop-gui/src/auth/login-form.jsx +++ b/packages/desktop-gui/src/auth/login-form.jsx @@ -115,7 +115,7 @@ class LoginForm extends Component { this.setState({ isLoggingIn: true }) - authApi.login() + authApi.login(this.props.utm) .then(() => { this.props.onSuccess() }) diff --git a/packages/desktop-gui/src/auth/login-modal.jsx b/packages/desktop-gui/src/auth/login-modal.jsx index 058fe388cb05..949213dd8639 100644 --- a/packages/desktop-gui/src/auth/login-modal.jsx +++ b/packages/desktop-gui/src/auth/login-modal.jsx @@ -70,7 +70,7 @@ class LoginContent extends Component { x

Log In

Logging in gives you access to the Cypress Dashboard Service. You can set up projects to be recorded and see test data from your project.

- this.setState({ succeeded: true })} /> + this.setState({ succeeded: true })} /> ) } diff --git a/packages/desktop-gui/src/runs/runs-list.jsx b/packages/desktop-gui/src/runs/runs-list.jsx index 2f8a0fb44c87..28633151f917 100644 --- a/packages/desktop-gui/src/runs/runs-list.jsx +++ b/packages/desktop-gui/src/runs/runs-list.jsx @@ -284,7 +284,7 @@ class RunsList extends Component { - + ) } diff --git a/packages/server/lib/gui/auth.js b/packages/server/lib/gui/auth.js index 70aeaaca51e8..c66062b59327 100644 --- a/packages/server/lib/gui/auth.js +++ b/packages/server/lib/gui/auth.js @@ -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 }) diff --git a/packages/server/lib/gui/events.js b/packages/server/lib/gui/events.js index f58ff0ac9851..e120ead901b9 100644 --- a/packages/server/lib/gui/events.js +++ b/packages/server/lib/gui/events.js @@ -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) diff --git a/packages/server/test/unit/gui/auth_spec.js b/packages/server/test/unit/gui/auth_spec.js index 50ea9a93dda6..646b13b31b14 100644 --- a/packages/server/test/unit/gui/auth_spec.js +++ b/packages/server/test/unit/gui/auth_spec.js @@ -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 () {