mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-11 01:29:59 -06:00
feat: distinguish app vs launchpad utm_source when using utm params (#21424)
This commit is contained in:
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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()"
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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()}`
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user