chore: Adding validateExternalLink command to increase e2e coverage of external links (#19462)

* Adding command to validate presence and action of external links.

* Updating link validation in other tests

* Condensing this a bit

* Don't log the wrapping
This commit is contained in:
Tyler Biethman
2022-01-03 19:05:52 -06:00
committed by GitHub
parent 462925eb76
commit 3f936fbbe7
5 changed files with 119 additions and 32 deletions
@@ -20,7 +20,8 @@ describe('Code Generation', () => {
cy.findByTestId('new-spec-button').click()
cy.findByTestId('create-spec-modal').should('be.visible').within(() => {
cy.contains('Create a new spec').should('be.visible')
cy.get('[data-cy="external"]').should('have.attr', 'href', 'https://on.cypress.io')
cy.validateExternalLink({ name: `${defaultMessages.links.needHelp}?`, href: 'https://on.cypress.io' })
})
cy.contains('Create from component').click()
@@ -1,5 +1,4 @@
import defaultMessages from '@packages/frontend-shared/src/locales/en-US.json'
import type { Interception } from '@packages/net-stubbing/lib/external-types'
describe('Navigation', () => {
before(() => {
@@ -11,11 +10,11 @@ describe('Navigation', () => {
cy.startAppServer()
cy.visitApp()
cy.intercept('mutation-ExternalLink_OpenExternal', { 'data': { 'openExternal': true } }).as('OpenExternal')
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')
cy.validateExternalLink({
name: defaultMessages.topNav.docsMenu.firstTest,
href: 'https://on.cypress.io/writing-first-test?utm_medium=Docs+Menu&utm_content=First+Test',
})
})
})
@@ -124,7 +124,10 @@ describe('App Top Nav Workflows', () => {
cy.startAppServer()
cy.visitApp()
cy.findByTestId('top-nav-cypress-version-current-link').should('have.attr', 'href', 'https://github.com/cypress-io/cypress/releases/tag/v10.0.0')
cy.findByTestId('app-header-bar').validateExternalLink({
name: 'v10.0.0',
href: 'https://github.com/cypress-io/cypress/releases/tag/v10.0.0',
})
})
})
@@ -158,20 +161,22 @@ describe('App Top Nav Workflows', () => {
it('shows dropdown with version info if user version is outdated', () => {
cy.findByTestId('top-nav-version-list').contains('v10.0.0 • Upgrade').click()
cy.findByTestId('update-hint').findByRole('link', { name: '10.1.0' })
.should('have.attr', 'href', 'https://github.com/cypress-io/cypress/releases/tag/v10.1.0')
cy.findByTestId('update-hint').findByText('Latest')
cy.findByTestId('update-hint').within(() => {
cy.validateExternalLink({ name: '10.1.0', href: 'https://github.com/cypress-io/cypress/releases/tag/v10.1.0' })
cy.findByText('Latest').should('be.visible')
})
cy.findByTestId('cypress-update-popover').findByRole('button', { name: 'Update to 10.1.0' })
cy.findByTestId('current-hint').findByRole('link', { name: '10.0.0' })
.should('have.attr', 'href', 'https://github.com/cypress-io/cypress/releases/tag/v10.0.0')
cy.findByTestId('current-hint').within(() => {
cy.validateExternalLink({ name: '10.0.0', href: 'https://github.com/cypress-io/cypress/releases/tag/v10.0.0' })
cy.findByText('Installed').should('be.visible')
})
cy.findByTestId('current-hint').findByText('Installed')
cy.findByTestId('cypress-update-popover').findByRole('link', { name: 'See all releases' })
.should('have.attr', 'href', 'https://github.com/cypress-io/cypress/releases')
cy.findByTestId('cypress-update-popover').validateExternalLink({
name: 'See all releases',
href: 'https://github.com/cypress-io/cypress/releases',
})
})
it('hides dropdown when version in header is clicked', () => {
@@ -217,15 +222,40 @@ describe('App Top Nav Workflows', () => {
cy.findByRole('heading', { name: 'References', level: 2 })
cy.findByRole('heading', { name: 'Run in CI/CD', level: 2 })
cy.findByRole('link', { name: 'Write your first test' }).should('have.attr', 'href', 'https://on.cypress.io/writing-first-test?utm_medium=Docs+Menu&utm_content=First+Test')
cy.findByRole('link', { name: 'Testing your app' }).should('have.attr', 'href', 'https://on.cypress.io/testing-your-app?utm_medium=Docs+Menu&utm_content=Testing+Your+App')
cy.findByRole('link', { name: 'Organizing Tests' }).should('have.attr', 'href', 'https://on.cypress.io/writing-and-organizing-tests?utm_medium=Docs+Menu&utm_content=Organizing+Tests')
cy.validateExternalLink({
name: 'Write your first test',
href: 'https://on.cypress.io/writing-first-test?utm_medium=Docs+Menu&utm_content=First+Test',
})
cy.findByRole('link', { name: 'Best Practices' }).should('have.attr', 'href', 'https://on.cypress.io/best-practices?utm_medium=Docs+Menu&utm_content=Best+Practices')
cy.findByRole('link', { name: 'Configuration' }).should('have.attr', 'href', 'https://on.cypress.io/configuration?utm_medium=Docs+Menu&utm_content=Configuration')
cy.findByRole('link', { name: 'API' }).should('have.attr', 'href', 'https://on.cypress.io/api?utm_medium=Docs+Menu&utm_content=API')
cy.validateExternalLink({
name: 'Testing your app',
href: 'https://on.cypress.io/testing-your-app?utm_medium=Docs+Menu&utm_content=Testing+Your+App',
})
cy.findByRole('link', { name: 'Run tests faster' }).should('have.attr', 'href', 'https://on.cypress.io/parallelization?utm_medium=Docs+Menu&utm_content=Parallelization')
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',
})
cy.validateExternalLink({
name: 'Run tests faster',
href: 'https://on.cypress.io/parallelization?utm_medium=Docs+Menu&utm_content=Parallelization',
})
cy.findByRole('button', { name: 'Set up CI' }).click()
cy.findByText('Configure CI').should('be.visible')
@@ -254,7 +284,11 @@ describe('App Top Nav Workflows', () => {
cy.findByTestId('login-panel').contains('Test User').should('be.visible')
cy.findByTestId('login-panel').contains('test@example.com').should('be.visible')
cy.findByRole('link', { name: 'Profile Settings' }).should('be.visible').and('have.attr', 'href', 'https://on.cypress.io/dashboard/profile')
cy.validateExternalLink({
name: 'Profile Settings',
href: 'https://on.cypress.io/dashboard/profile',
})
cy.intercept('mutation-Logout').as('logout')
@@ -52,6 +52,18 @@ export interface FindBrowsersOptions {
filter?(browser: Browser): boolean
}
export interface ValidateExternalLinkOptions {
/**
* The user-visible descriptor for the link. If omitted, the href
* is assumed to be the name.
*/
name?: string
/**
* The href value of the link to be validated.
*/
href: string
}
declare global {
namespace Cypress {
interface Chainable {
@@ -112,6 +124,11 @@ declare global {
* Mocks the system browser retrieval to return the desired browsers
*/
findBrowsers(options?: FindBrowsersOptions): void
/**
* Finds a link with the provided text and href, either globally or within a chained subject,
* and asserts that it triggers the appropriate mutation when clicked.
*/
validateExternalLink(options: ValidateExternalLinkOptions | string): Chainable<JQuery<HTMLElement>>
}
}
}
@@ -331,6 +348,33 @@ function logInternal<T> (name: string | Partial<Cypress.LogConfig>, cb: (log: Cy
})
}
/**
* Finds a link with the provided text and href, either globally or within a chained subject,
* and asserts that it triggers the appropriate mutation when clicked.
*/
function validateExternalLink (subject, options: ValidateExternalLinkOptions | string): Cypress.Chainable<JQuery<HTMLElement>> {
let name
let href
if (Cypress._.isString(options)) {
name = href = options
} else {
({ name, href } = options)
}
cy.intercept('mutation-ExternalLink_OpenExternal', { 'data': { 'openExternal': true } }).as('OpenExternal')
cy.wrap(subject, { log: false }).findByRole('link', { name: name || href }).as('Link')
.should('have.attr', 'href', href)
.click()
cy.wait('@OpenExternal')
.its('request.body.variables.url')
.should('equal', href)
return cy.get('@Link')
}
Cypress.Commands.add('scaffoldProject', scaffoldProject)
Cypress.Commands.add('addProject', addProject)
Cypress.Commands.add('openGlobalMode', openGlobalMode)
@@ -342,5 +386,6 @@ Cypress.Commands.add('openProject', openProject)
Cypress.Commands.add('withCtx', withCtx)
Cypress.Commands.add('remoteGraphQLIntercept', remoteGraphQLIntercept)
Cypress.Commands.add('findBrowsers', findBrowsers)
Cypress.Commands.add('validateExternalLink', { prevSubject: ['optional', 'element'] }, validateExternalLink)
installCustomPercyCommand()
@@ -31,9 +31,17 @@ describe('Choose a Browser Page', () => {
cy.get('[data-cy="alert-body"]')
.should('contain', 'The specified browser was not found on your system or is not supported by Cypress: doesNotExist')
cy.get('[data-cy="alert-body"] a').eq(1)
.should('have.attr', 'href')
.and('equal', 'https://on.cypress.io/troubleshooting-launching-browsers')
cy.get('[data-cy="alert-body"]').within(() => {
cy.validateExternalLink({
name: 'use a custom browser',
href: 'https://on.cypress.io/customize-browsers',
})
cy.validateExternalLink({
name: 'how to troubleshoot launching browsers',
href: 'https://on.cypress.io/troubleshooting-launching-browsers',
})
})
// Ensure warning can be dismissed
cy.get('[data-cy="alert-suffix-icon"]').click()
@@ -51,10 +59,10 @@ describe('Choose a Browser Page', () => {
cy.get('[data-cy="alert-body"]')
.should('contain', 'We could not identify a known browser at the path you specified: /path/does/not/exist')
.should('contain', 'spawn /path/does/not/exist ENOENT')
cy.get('[data-cy="alert-body"] a')
.should('have.attr', 'href')
.and('equal', 'https://on.cypress.io/troubleshooting-launching-browsers')
.validateExternalLink({
name: 'how to troubleshoot launching browsers',
href: 'https://on.cypress.io/troubleshooting-launching-browsers',
})
// Ensure warning can be dismissed
cy.get('[data-cy="alert-suffix-icon"]').click()