feat: distinguish app vs launchpad utm_source when using utm params (#21424)

This commit is contained in:
Mark Noonan
2022-05-12 13:11:00 -04:00
committed by GitHub
parent 841d28802c
commit a218f96223
9 changed files with 59 additions and 91 deletions

View File

@@ -264,34 +264,35 @@ describe('App Top Nav Workflows', () => {
cy.findByRole('heading', { name: 'References', level: 2 })
cy.findByRole('heading', { name: 'Run in CI/CD', level: 2 })
cy.validateExternalLink({
name: 'Write your first test',
href: 'https://on.cypress.io/writing-first-test?utm_medium=Docs+Menu&utm_content=First+Test',
})
const expectedLinks = [
{
name: 'Write your first test',
href: 'https://on.cypress.io/writing-first-test?utm_medium=Docs+Menu&utm_content=First+Test&utm_source=Binary%3A+App',
},
{
name: 'Testing your app',
href: 'https://on.cypress.io/testing-your-app?utm_medium=Docs+Menu&utm_content=Testing+Your+App&utm_source=Binary%3A+App',
},
{
name: 'Organizing Tests',
href: 'https://on.cypress.io/writing-and-organizing-tests?utm_medium=Docs+Menu&utm_content=Organizing+Tests&utm_source=Binary%3A+App',
},
{
name: 'Best Practices',
href: 'https://on.cypress.io/best-practices?utm_medium=Docs+Menu&utm_content=Best+Practices&utm_source=Binary%3A+App',
},
{
name: 'Configuration',
href: 'https://on.cypress.io/configuration?utm_medium=Docs+Menu&utm_content=Configuration&utm_source=Binary%3A+App',
},
{
name: 'API',
href: 'https://on.cypress.io/api?utm_medium=Docs+Menu&utm_content=API&utm_source=Binary%3A+App',
},
]
cy.validateExternalLink({
name: 'Testing your app',
href: 'https://on.cypress.io/testing-your-app?utm_medium=Docs+Menu&utm_content=Testing+Your+App',
})
cy.validateExternalLink({
name: 'Organizing Tests',
href: 'https://on.cypress.io/writing-and-organizing-tests?utm_medium=Docs+Menu&utm_content=Organizing+Tests',
})
cy.validateExternalLink({
name: 'Best Practices',
href: 'https://on.cypress.io/best-practices?utm_medium=Docs+Menu&utm_content=Best+Practices',
})
cy.validateExternalLink({
name: 'Configuration',
href: 'https://on.cypress.io/configuration?utm_medium=Docs+Menu&utm_content=Configuration',
})
cy.validateExternalLink({
name: 'API',
href: 'https://on.cypress.io/api?utm_medium=Docs+Menu&utm_content=API',
expectedLinks.forEach((link) => {
cy.validateExternalLink(link)
})
})

View File

@@ -10,7 +10,7 @@
{{ t('runs.connect.modal.createOrg.description') }}
</p>
<ExternalLink
class="border rounded mx-auto outline-none py-11px px-16px border-indigo-500 bg-indigo-500 text-white inline-block hocus-default max-h-60px"
class="border rounded mx-auto outline-none bg-indigo-500 border-indigo-500 text-white max-h-60px py-11px px-16px inline-block hocus-default"
:href="createOrgUrl"
:include-graphql-port="true"
@click="startWaitingOrgToBeCreated()"

View File

@@ -77,14 +77,14 @@ describe('<HeaderBarContent />', { viewportWidth: 1000, viewportHeight: 750 }, (
// because outside of global mode, those are buttons that trigger popups
// so this way we can assert all links at once.
const expectedDocsLinks = {
[text.docsMenu.firstTest]: 'https://on.cypress.io/writing-first-test?utm_medium=Docs+Menu&utm_content=First+Test',
[text.docsMenu.testingApp]: 'https://on.cypress.io/testing-your-app?utm_medium=Docs+Menu&utm_content=Testing+Your+App',
[text.docsMenu.organizingTests]: 'https://on.cypress.io/writing-and-organizing-tests?utm_medium=Docs+Menu&utm_content=Organizing+Tests',
[text.docsMenu.bestPractices]: 'https://on.cypress.io/best-practices?utm_medium=Docs+Menu&utm_content=Best+Practices',
[text.docsMenu.configuration]: 'https://on.cypress.io/configuration?utm_medium=Docs+Menu&utm_content=Configuration',
[text.docsMenu.api]: 'https://on.cypress.io/api?utm_medium=Docs+Menu&utm_content=API',
[text.docsMenu.ciSetup]: 'https://on.cypress.io/ci?utm_medium=Docs+Menu&utm_content=Set+Up+CI',
[text.docsMenu.fasterTests]: 'https://on.cypress.io/parallelization?utm_medium=Docs+Menu&utm_content=Parallelization',
[text.docsMenu.firstTest]: 'https://on.cypress.io/writing-first-test?utm_medium=Docs+Menu&utm_content=First+Test&utm_source=Binary%3A+Launchpad',
[text.docsMenu.testingApp]: 'https://on.cypress.io/testing-your-app?utm_medium=Docs+Menu&utm_content=Testing+Your+App&utm_source=Binary%3A+Launchpad',
[text.docsMenu.organizingTests]: 'https://on.cypress.io/writing-and-organizing-tests?utm_medium=Docs+Menu&utm_content=Organizing+Tests&utm_source=Binary%3A+Launchpad',
[text.docsMenu.bestPractices]: 'https://on.cypress.io/best-practices?utm_medium=Docs+Menu&utm_content=Best+Practices&utm_source=Binary%3A+Launchpad',
[text.docsMenu.configuration]: 'https://on.cypress.io/configuration?utm_medium=Docs+Menu&utm_content=Configuration&utm_source=Binary%3A+Launchpad',
[text.docsMenu.api]: 'https://on.cypress.io/api?utm_medium=Docs+Menu&utm_content=API&utm_source=Binary%3A+Launchpad',
[text.docsMenu.ciSetup]: 'https://on.cypress.io/ci?utm_medium=Docs+Menu&utm_content=Set+Up+CI&utm_source=Binary%3A+Launchpad',
[text.docsMenu.fasterTests]: 'https://on.cypress.io/parallelization?utm_medium=Docs+Menu&utm_content=Parallelization&utm_source=Binary%3A+Launchpad',
}
cy.contains('button', text.docsMenu.docsHeading).click()
@@ -347,12 +347,12 @@ describe('<HeaderBarContent />', { viewportWidth: 1000, viewportHeight: 750 }, (
mountWithSavedState()
cy.contains(
'a[href="https://on.cypress.io/setup-ci?utm_medium=CI+Prompt+1&utm_campaign=Other&utm_content=Automatic"]',
'a[href="https://on.cypress.io/setup-ci?utm_medium=CI+Prompt+1&utm_campaign=Other&utm_content=Automatic&utm_source=Binary%3A+Launchpad"]',
defaultMessages.topNav.docsMenu.prompts.ci1.seeOtherGuides,
).should('be.visible')
cy.contains(
'a[href="https://on.cypress.io/ci?utm_medium=CI+Prompt+1&utm_campaign=Learn+More"]',
'a[href="https://on.cypress.io/ci?utm_medium=CI+Prompt+1&utm_campaign=Learn+More&utm_source=Binary%3A+Launchpad"]',
defaultMessages.topNav.docsMenu.prompts.ci1.intro,
).should('be.visible')
})
@@ -425,7 +425,7 @@ describe('<HeaderBarContent />', { viewportWidth: 1000, viewportHeight: 750 }, (
it('links to more information with expected utm params', () => {
cy.contains(
'a[href="https://on.cypress.io/smart-orchestration?utm_medium=CI+Prompt+1&utm_campaign=Learn+More"]',
'a[href="https://on.cypress.io/smart-orchestration?utm_medium=CI+Prompt+1&utm_campaign=Learn+More&utm_source=Binary%3A+Launchpad"]',
defaultMessages.topNav.docsMenu.prompts.orchestration1.learnMore,
)
.should('be.visible')

View File

@@ -27,9 +27,9 @@ describe('<PromptContent />', { viewportWidth: 500, viewportHeight: 800 }, () =>
.should('have.length', 6)
.eq(0)
.find('a')
.should('have.attr', 'href', 'https://on.cypress.io/setup-ci-circleci?utm_medium=CI+Prompt+1&utm_campaign=Circle&utm_content=Manual')
.should('have.attr', 'href', 'https://on.cypress.io/setup-ci-circleci?utm_medium=CI+Prompt+1&utm_campaign=Circle&utm_content=Manual&utm_source=Binary%3A+Launchpad')
cy.contains('a', defaultMessages.topNav.docsMenu.prompts.ci1.intro)
.should('have.attr', 'href', 'https://on.cypress.io/ci?utm_medium=CI+Prompt+1&utm_campaign=Learn+More')
.should('have.attr', 'href', 'https://on.cypress.io/ci?utm_medium=CI+Prompt+1&utm_campaign=Learn+More&utm_source=Binary%3A+Launchpad')
})
})

View File

@@ -5,6 +5,15 @@ export type LinkWithParams = {
export const getUrlWithParams = (link: LinkWithParams) => {
let result = link.url
const hasUtmParams = Object.keys(link.params).some((param) => param.startsWith('utm_'))
if (hasUtmParams) {
// __CYPRESS_MODE__ is only set on the window in th browser app -
// checking this allows us to know if links are clicked in the browser app or the launchpad
const utm_source = window.__CYPRESS_MODE__ ? 'Binary: App' : 'Binary: Launchpad'
link.params.utm_source = utm_source
}
if (link.params) {
result += `?${new URLSearchParams(link.params).toString()}`

View File

@@ -93,8 +93,12 @@ export const mutation = mutationType({
resolve: (_, args, ctx) => {
let url = args.url
// the `port` param is included in external links to create a cloud organization
// so that the app can be notified when the org has been created
if (args.includeGraphqlPort && process.env.CYPRESS_INTERNAL_GRAPHQL_PORT) {
url = `${args.url}?port=${process.env.CYPRESS_INTERNAL_GRAPHQL_PORT}`
const joinCharacter = args.url.includes('?') ? '&' : '?'
url = `${args.url}${joinCharacter}port=${process.env.CYPRESS_INTERNAL_GRAPHQL_PORT}`
}
ctx.actions.electron.openExternal(url)

View File

@@ -10,7 +10,7 @@ describe('Navigation', () => {
cy.contains('button', defaultMessages.topNav.docsMenu.docsHeading).click()
cy.contains('a', defaultMessages.topNav.docsMenu.firstTest).click()
cy.wait('@OpenExternal').then((interception: Interception) => {
expect(interception.request.body.variables.url).to.equal('https://on.cypress.io/writing-first-test?utm_medium=Docs+Menu&utm_content=First+Test')
expect(interception.request.body.variables.url).to.equal('https://on.cypress.io/writing-first-test?utm_medium=Docs+Menu&utm_content=First+Test&utm_source=Binary%3A+Launchpad')
})
})
})

View File

@@ -1,29 +1,6 @@
import _ from 'lodash'
import { URL, URLSearchParams } from 'url'
// NOTE: in order for query params to be passed through on links
// forwardQueryParams: true must be set for that slug in the on package
interface OpenExternalOptions {
url: string
params: { [key: string]: string }
}
export const openExternal = (opts: OpenExternalOptions | string) => {
if (_.isString(opts)) {
return require('electron').shell.openExternal(opts)
}
const url = new URL(opts.url)
if (opts.params) {
// just add the utm_source here so we don't have to duplicate it on every link
if (_.find(opts.params, (_val, key) => _.includes(key, 'utm_'))) {
opts.params.utm_source = 'Test Runner'
}
url.search = new URLSearchParams(opts.params).toString()
}
return require('electron').shell.openExternal(url.href)
export const openExternal = (url: string) => {
return require('electron').shell.openExternal(url)
}

View File

@@ -20,27 +20,4 @@ describe('lib/gui/links', () => {
openExternal('https://on.cypress.io/string-link')
expect(shell.openExternal).to.be.calledWith('https://on.cypress.io/string-link')
})
it('appends get parameters', () => {
openExternal({
url: 'https://on.cypress.io/string-link',
params: {
search: 'term',
},
})
expect(shell.openExternal).to.be.calledWith('https://on.cypress.io/string-link?search=term')
})
it('automatically adds utm_source if utm params are present', () => {
openExternal({
url: 'https://on.cypress.io/string-link',
params: {
utm_medium: 'GUI Tab',
utm_campaign: 'Learn More',
},
})
expect(shell.openExternal).to.be.calledWith('https://on.cypress.io/string-link?utm_medium=GUI+Tab&utm_campaign=Learn+More&utm_source=Test+Runner')
})
})