Skip to content
Merged
57 changes: 29 additions & 28 deletions packages/app/cypress/e2e/top-nav.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
})

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',
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',
},
]

expectedLinks.forEach((link) => {
cy.validateExternalLink(link)
})
})

Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/runs/modals/CreateCloudOrgModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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()"
Expand Down
22 changes: 11 additions & 11 deletions packages/frontend-shared/src/gql-components/HeaderBarContent.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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')
})
Expand Down Expand Up @@ -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')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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')
})
})
9 changes: 9 additions & 0 deletions packages/frontend-shared/src/utils/getUrlWithParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()}`
Expand Down
6 changes: 5 additions & 1 deletion packages/graphql/src/schemaTypes/objectTypes/gql-Mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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('?') ? '&' : '?'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@estrada9166 I made this small change, might as well future proof this for possible other params. @tbiethman I added a comment about the purpose of this, Alejandro showed me that it is actually used.


url = `${args.url}${joinCharacter}port=${process.env.CYPRESS_INTERNAL_GRAPHQL_PORT}`
}

ctx.actions.electron.openExternal(url)
Expand Down
2 changes: 1 addition & 1 deletion packages/launchpad/cypress/e2e/navigation.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
})
})
})
27 changes: 2 additions & 25 deletions packages/server/lib/gui/links.ts
Original file line number Diff line number Diff line change
@@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason we inline the require instead of a top level import?

Copy link
Contributor

@tbiethman tbiethman May 11, 2022

Choose a reason for hiding this comment

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

That was changed as part of #21174.

But good news, upgrading to electron 18 fixes this problem altogether and will allow that extra import to be removed. I'll get that taken out and remove the inline requires when #21418 lands and merges into 10.0-release.

}
23 changes: 0 additions & 23 deletions packages/server/test/unit/gui/links_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
})
})