Skip to content

Commit

Permalink
feat: allow cypress.config.js to be a default config file (#18221)
Browse files Browse the repository at this point in the history
Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
  • Loading branch information
elevatebart and jennifer-shehane authored Sep 29, 2021
1 parent 2caa5de commit 07d4340
Show file tree
Hide file tree
Showing 28 changed files with 754 additions and 617 deletions.
73 changes: 51 additions & 22 deletions packages/desktop-gui/cypress/integration/settings_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,34 +358,52 @@ describe('Settings', () => {
})

describe('project id panel', () => {
beforeEach(function () {
this.openProject.resolve(this.config)
this.projectStatuses[0].id = this.config.projectId
this.getProjectStatus.resolve(this.projectStatuses[0])
context('with json file', () => {
beforeEach(function () {
this.openProject.resolve(this.config)
this.projectStatuses[0].id = this.config.projectId
this.getProjectStatus.resolve(this.projectStatuses[0])

this.goToSettings()
cy.contains('Project ID').click()
})
this.goToSettings()
cy.contains('Project ID').click()
})

it('displays project id section', function () {
cy.contains(this.config.projectId)
cy.percySnapshot()
})
it('displays project id section', function () {
cy.contains(this.config.projectId)
cy.percySnapshot()
})

it('shows tooltip on hover of copy to clipboard', () => {
cy.get('.action-copy').trigger('mouseover')
cy.get('.cy-tooltip').should('contain', 'Copy to clipboard')
})

it('shows tooltip on hover of copy to clipboard', () => {
cy.get('.action-copy').trigger('mouseover')
cy.get('.cy-tooltip').should('contain', 'Copy to clipboard')
it('copies project id config to clipboard', function () {
cy.get('.action-copy').click()
.then(() => {
const expectedJsonConfig = {
projectId: this.config.projectId,
}
const expectedCopyCommand = JSON.stringify(expectedJsonConfig, null, 2)

expect(this.ipc.setClipboardText).to.be.calledWith(expectedCopyCommand)
})
})
})

it('copies project id config to clipboard', function () {
cy.get('.action-copy').click()
.then(() => {
const expectedJsonConfig = {
projectId: this.config.projectId,
}
const expectedCopyCommand = JSON.stringify(expectedJsonConfig, null, 2)
context('with js file', () => {
beforeEach(function () {
this.openProject.resolve({ ...this.config, configFile: 'custom.cypress.js' })
this.projectStatuses[0].id = this.config.projectId
this.getProjectStatus.resolve(this.projectStatuses[0])

this.goToSettings()
cy.contains('Project ID').click()
})

expect(this.ipc.setClipboardText).to.be.calledWith(expectedCopyCommand)
it('displays project id section', function () {
cy.get('[data-cy="project-id"] pre').contains('module.exports = {')
cy.percySnapshot()
})
})
})
Expand Down Expand Up @@ -586,6 +604,17 @@ describe('Settings', () => {
.should('contain', systemNodeVersion)
.should('not.contain', bundledNodeVersion)
})

it('should display an additional line when configFile is not JSON', function () {
const configFile = 'notjson.js'

this.navigateWithConfig({
configFile,
})

cy.contains(`Node.js Version (${bundledNodeVersion})`).click()
cy.get('.node-version li').should('contain', configFile)
})
})

describe('proxy settings panel', () => {
Expand Down
6 changes: 5 additions & 1 deletion packages/desktop-gui/src/lib/config-file-formatted.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ const configFileFormatted = (configFile) => {
return <><code>cypress.json</code> file (currently disabled by <code>--config-file false</code>)</>
}

if (isUndefined(configFile) || configFile === 'cypress.json') {
if (isUndefined(configFile)) {
return <><code>cypress.json</code> file</>
}

if (['cypress.json', 'cypress.config.js'].includes(configFile)) {
return <><code>{configFile}</code> file</>
}

return <>custom config file <code>{configFile}</code></>
}

Expand Down
2 changes: 2 additions & 0 deletions packages/desktop-gui/src/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,5 @@ export function stripSharedDirsFromDir2 (dir1, dir2, osName) {
.join(sep)
.value()
}

export const isFileJSON = (file) => file && /\.json$/.test(file)
5 changes: 5 additions & 0 deletions packages/desktop-gui/src/settings/node-version.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { observer } from 'mobx-react'
import React from 'react'

import ipc from '../lib/ipc'
import { isFileJSON } from '../lib/utils'
import { configFileFormatted } from '../lib/config-file-formatted'

const openHelp = (e) => {
e.preventDefault()
Expand Down Expand Up @@ -89,6 +91,9 @@ const NodeVersion = observer(({ project }) => {
<div className='well text-muted'>
This Node.js version is used to:
<ul>
{isFileJSON(project.configFile)
? undefined
: <li>Execute code in the {configFileFormatted(project.configFile)}.</li>}
<li>Build files in the {formatIntegrationFolder()} folder.</li>
<li>Build files in the <code>cypress/support</code> folder.</li>
<li>Execute code in the {formatPluginsFile() ? formatPluginsFile() : 'plugins'} file.</li>
Expand Down
24 changes: 16 additions & 8 deletions packages/desktop-gui/src/settings/project-id.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react'
import Tooltip from '@cypress/react-tooltip'

import ipc from '../lib/ipc'
import { isFileJSON } from '../lib/utils'
import { configFileFormatted } from '../lib/config-file-formatted'

const openProjectIdHelp = (e) => {
Expand All @@ -19,10 +20,6 @@ const openProjectIdHelp = (e) => {
const ProjectId = observer(({ project }) => {
if (!project.id) return null

const projectIdJsonConfig = {
projectId: project.id,
}

return (
<div data-cy="project-id">
<a href='#' className='learn-more' onClick={openProjectIdHelp}>
Expand All @@ -33,7 +30,7 @@ const ProjectId = observer(({ project }) => {
It identifies your project and should not be changed.
</p>
<pre className='line-nums copy-to-clipboard'>
<a className="action-copy" onClick={() => ipc.setClipboardText(JSON.stringify(projectIdJsonConfig, null, 2))}>
<a className="action-copy" onClick={() => ipc.setClipboardText(document.querySelector('[data-cy="project-id"] pre').innerText)}>
<Tooltip
title='Copy to clipboard'
placement='top'
Expand All @@ -42,9 +39,20 @@ const ProjectId = observer(({ project }) => {
<i className='fas fa-clipboard' />
</Tooltip>
</a>
<span>{'{'}</span>
<span>{` "projectId": "${project.id}"`}</span>
<span>{'}'}</span>
{
isFileJSON(project.configFile) ?
<>
<span>{'{'}</span>
<span>{` "projectId": "${project.id}"`}</span>
<span>{'}'}</span>
</>
:
<>
<span>{'module.exports = {'}</span>
<span>{` projectId: "${project.id}"`}</span>
<span>{'}'}</span>
</>
}
</pre>
</div>
)
Expand Down
10 changes: 10 additions & 0 deletions packages/desktop-gui/src/settings/project-id_spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,14 @@ describe('ProjectId', () => {

cy.get('@externalOpen').should('have.been.called')
})

it('shows a different output when configFile is js', () => {
mount(<ProjectId project={{ ...project, configFile: 'cypress.config.js' }} />, {
stylesheets: '/__root/dist/app.css',
})

cy.get('[data-cy=project-id] pre').then(($pre) => {
expect($pre.text()).to.contain('module.exports = ')
})
})
})
2 changes: 1 addition & 1 deletion packages/server/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import scaffold from './scaffold'
import { fs } from './util/fs'
import keys from './util/keys'
import origin from './util/origin'
import settings from './util/settings'
import * as settings from './util/settings'
import Debug from 'debug'
import pathHelpers from './util/path_helpers'
import findSystemNode from './util/find_system_node'
Expand Down
2 changes: 2 additions & 0 deletions packages/server/lib/configFiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// the first file is the default created file
export const CYPRESS_CONFIG_FILES = ['cypress.json', 'cypress.config.js']
14 changes: 14 additions & 0 deletions packages/server/lib/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,20 @@ const getMsgByType = function (type, arg1 = {}, arg2, arg3) {
${chalk.yellow(arg1.error)}
Learn more at https://on.cypress.io/reporters`
// TODO: update with vetted cypress language
case 'NO_DEFAULT_CONFIG_FILE_FOUND':
return stripIndent`\
Could not find a Cypress configuration file, exiting.
We looked but did not find a default config file in this folder: ${chalk.blue(arg1)}`
// TODO: update with vetted cypress language
case 'CONFIG_FILES_LANGUAGE_CONFLICT':
return stripIndent`
There is both a \`${arg2}\` and a \`${arg3}\` at the location below:
${arg1}
Cypress does not know which one to read for config. Please remove one of the two and try again.
`
case 'CONFIG_FILE_NOT_FOUND':
return stripIndent`\
Could not find a Cypress configuration file, exiting.
Expand Down
15 changes: 15 additions & 0 deletions packages/server/lib/gui/events.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable no-case-declarations */
const _ = require('lodash')
const path = require('path')
const ipc = require('electron').ipcMain
const { clipboard } = require('electron')
const debug = require('debug')('cypress:server:events')
Expand Down Expand Up @@ -323,6 +324,20 @@ const handleEvent = function (options, bus, event, id, type, arg) {
onWarning,
})
}).call('getConfig')
.then((config) => {
if (config.configFile && path.isAbsolute(config.configFile)) {
config.configFile = path.relative(arg, config.configFile)
}

// those two values make no sense to display in
// the GUI
if (config.resolved) {
config.resolved.configFile = undefined
config.resolved.testingType = undefined
}

return config
})
.then(send)
.catch(sendErr)

Expand Down
24 changes: 11 additions & 13 deletions packages/server/lib/modes/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const logSymbols = require('log-symbols')

const recordMode = require('./record')
const errors = require('../errors')
const ProjectStatic = require('../project_static')
const Reporter = require('../reporter')
const browserUtils = require('../browsers')
const { openProject } = require('../open_project')
Expand Down Expand Up @@ -609,22 +608,21 @@ const openProjectCreate = (projectRoot, socketId, args) => {
return openProject.create(projectRoot, args, options)
}

const createAndOpenProject = function (socketId, options) {
const createAndOpenProject = async function (socketId, options) {
const { projectRoot, projectId } = options

return ProjectStatic.ensureExists(projectRoot, options)
.then(() => {
// open this project without
// adding it to the global cache
return openProjectCreate(projectRoot, socketId, options)
})
.call('getProject')
return openProjectCreate(projectRoot, socketId, options)
.then((open_project) => open_project.getProject())
.then((project) => {
return Promise.props({
return Promise.all([
project,
config: project.getConfig(),
projectId: getProjectId(project, projectId),
})
project.getConfig(),
getProjectId(project, projectId),
]).then(([project, config, projectId]) => ({
project,
config,
projectId,
}))
})
}

Expand Down
10 changes: 8 additions & 2 deletions packages/server/lib/open_project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export interface LaunchArgs {
_: [string] // Cypress App binary location
config: Record<string, unknown>
cwd: string
browser: Browser
browser?: Browser['name']
configFile?: string
project: string // projectRoot
projectRoot: string // same as above
Expand All @@ -46,6 +46,12 @@ export interface LaunchArgs {
os: PlatformName

onFocusTests?: () => any
/**
* in run mode, the path of the project run
* path is relative if specified with --project,
* absolute if implied by current working directory
*/
runProject?: string
}

// @see https://github.com/cypress-io/cypress/issues/18094
Expand Down Expand Up @@ -456,7 +462,7 @@ export class OpenProject {
try {
await this.openProject.initializeConfig()
await this.openProject.open()
} catch (err) {
} catch (err: any) {
if (err.isCypressErr && err.portInUse) {
errors.throw(err.type, err.port)
} else {
Expand Down
14 changes: 10 additions & 4 deletions packages/server/lib/project-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ import system from './util/system'
import user from './user'
import { ensureProp } from './util/class-helpers'
import { fs } from './util/fs'
import settings from './util/settings'
import * as settings from './util/settings'
import plugins from './plugins'
import specsUtil from './util/specs'
import Watchers from './watchers'
import devServer from './plugins/dev-server'
import preprocessor from './plugins/preprocessor'
import { SpecsStore } from './specs-store'
import { checkSupportFile } from './project_utils'
import { checkSupportFile, getDefaultConfigFilePath } from './project_utils'
import type { LaunchArgs } from './open_project'

// Cannot just use RuntimeConfigOptions as is because some types are not complete.
Expand All @@ -54,7 +54,7 @@ type WebSocketOptionsCallback = (...args: any[]) => any
export interface OpenProjectLaunchOptions {
args?: LaunchArgs

configFile?: string | boolean
configFile?: string | false
browsers?: Cypress.Browser[]

// Callback to reload the Desktop GUI when cypress.json is changed.
Expand Down Expand Up @@ -516,7 +516,7 @@ export class ProjectBase<TServer extends ServerE2E | ServerCt> extends EE {
projectRoot,
}: {
projectRoot: string
configFile?: string | boolean
configFile?: string | false
onSettingsChanged?: false | (() => void)
}) {
// bail if we havent been told to
Expand Down Expand Up @@ -690,6 +690,12 @@ export class ProjectBase<TServer extends ServerE2E | ServerCt> extends EE {
}

async initializeConfig (): Promise<Cfg> {
// set default for "configFile" if undefined
if (this.options.configFile === undefined
|| this.options.configFile === null) {
this.options.configFile = await getDefaultConfigFilePath(this.projectRoot, !this.options.args?.runProject)
}

let theCfg: Cfg = await config.get(this.projectRoot, this.options)

if (theCfg.browsers) {
Expand Down
Loading

4 comments on commit 07d4340

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 07d4340 Sep 29, 2021

Choose a reason for hiding this comment

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

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/8.6.0/circle-develop-07d4340ce59657ed6bd5d84be98f1a60488be407/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 07d4340 Sep 29, 2021

Choose a reason for hiding this comment

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

AppVeyor has built the win32 ia32 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/8.6.0/appveyor-develop-07d4340ce59657ed6bd5d84be98f1a60488be407/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 07d4340 Sep 29, 2021

Choose a reason for hiding this comment

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

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/8.6.0/circle-develop-07d4340ce59657ed6bd5d84be98f1a60488be407/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 07d4340 Sep 29, 2021

Choose a reason for hiding this comment

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

AppVeyor has built the win32 x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/8.6.0/appveyor-develop-07d4340ce59657ed6bd5d84be98f1a60488be407/cypress.tgz

Please sign in to comment.