In my Cypress 10 project, I have the following config files:
cypress/cypress.config.jscypress/config/qa.json(Points at my QA environment).cypress/config/staging.json(Points at my Staging environment).
Everything in the below cypress.config.js file is common across both QA & Staging environments:
const { defineConfig } = require("cypress");
const fs = require('fs-extra')
const readDirRecursive = require('fs-readdir-recursive')
const createBundler = require("@bahmutov/cypress-esbuild-preprocessor")
const preprocessor = require("@badeball/cypress-cucumber-preprocessor")
const createEsbuildPlugin = require("@badeball/cypress-cucumber-preprocessor/esbuild")
const stdLibBrowser = require('node-stdlib-browser')
const plugin = require('node-stdlib-browser/helpers/esbuild/plugin')
const mysql = require('mysql')
function queryTestDb(query, config) {
// creates a new mysql connection using credentials from cypress.json env's
const connection = mysql.createConnection(config.env.db)
// start connection to db
connection.connect()
// exec query + disconnect to db as a Promise
return new Promise((resolve, reject) => {
connection.query(query, (error, results) => {
if (error) reject(error)
else {
connection.end()
return resolve(results)
}
})
})
}
async function setupNodeEvents(on, config) {
await preprocessor.addCucumberPreprocessorPlugin(on, config, {
omitBeforeRunHandler: true
})
on('before:run', () => {
fs.emptyDirSync('./test-results')
preprocessor.beforeRunHandler(config)
})
on(
'file:preprocessor',
createBundler({
inject: [require.resolve('node-stdlib-browser/helpers/esbuild/shim')],
define: {
global: 'global',
process: 'process',
Buffer: 'Buffer'
},
plugins: [plugin(stdLibBrowser), createEsbuildPlugin.default(config)],
})
)
on('task', {
readFolder(path) {
return readDirRecursive(path)
}
})
on('task', {
queryDb: query => {
return queryTestDb(query, config)
}
})
return config
}
module.exports = defineConfig({
defaultCommandTimeout: 30000,
requestTimeout: 30000,
responseTimeout: 60000,
pageLoadTimeout: 90000,
numTestsKeptInMemory: 1,
chromeWebSecurity: false,
experimentalWebKitSupport: false,
screenshotsFolder: 'test-results/screenshots',
videosFolder: 'test-results/videos',
viewportWidth: 1920,
viewportHeight: 1200,
watchForFileChanges: false,
screenshotOnRunFailure: true,
video: false,
videoCompression: 8,
reporter: 'spec',
reporterOptions: {
mochaFile: 'test-results/tests-output/result-[hash].xml',
toConsole: true
},
retries: {
runMode: 1,
openMode: 0
},
e2e: {
setupNodeEvents,
specPattern: 'cypress/tests/**/*.feature',
},
})
Even though a lot of the values will remain the same for QA & Staging, I need to use different config files for them.
Specifically, the e2e.baseUrl & env.db values will be different in these environments. Every other value will be the same in QA & Staging.
Here is what I have tried to do in my qa.json file:
const { defineConfig } = require("cypress");
const baseConfig = require('../../cypress.config.js')
const fs = require('fs-extra')
const readDirRecursive = require('fs-readdir-recursive')
const createBundler = require("@bahmutov/cypress-esbuild-preprocessor")
const preprocessor = require("@badeball/cypress-cucumber-preprocessor")
const createEsbuildPlugin = require("@badeball/cypress-cucumber-preprocessor/esbuild")
const stdLibBrowser = require('node-stdlib-browser')
const plugin = require('node-stdlib-browser/helpers/esbuild/plugin')
const mysql = require('mysql')
const baseUrl = 'https://qa.com'
const env = {
db: {
host: 'myHost',
user: 'myUser',
password: 'myPassowrd',
database: 'myDb',
}
}
function queryTestDb(query, config) {
// creates a new mysql connection using credentials from cypress.json env's
const connection = mysql.createConnection(config.env.db)
// start connection to db
connection.connect()
// exec query + disconnect to db as a Promise
return new Promise((resolve, reject) => {
connection.query(query, (error, results) => {
if (error) reject(error)
else {
connection.end()
return resolve(results)
}
})
})
}
async function setupNodeEvents(on, config) {
await preprocessor.addCucumberPreprocessorPlugin(on, config, {
omitBeforeRunHandler: true
})
on('before:run', () => {
fs.emptyDirSync('./test-results')
preprocessor.beforeRunHandler(config)
})
on(
'file:preprocessor',
createBundler({
inject: [require.resolve('node-stdlib-browser/helpers/esbuild/shim')],
define: {
global: 'global',
process: 'process',
Buffer: 'Buffer'
},
plugins: [plugin(stdLibBrowser), createEsbuildPlugin.default(config)],
})
)
on('task', {
readFolder(path) {
return readDirRecursive(path)
}
})
on('task', {
queryDb: query => {
return queryTestDb(query, config)
}
})
return config
}
module.exports = defineConfig({
defaultCommandTimeout: 30000,
requestTimeout: 30000,
responseTimeout: 60000,
pageLoadTimeout: 90000,
numTestsKeptInMemory: 1,
chromeWebSecurity: false,
experimentalWebKitSupport: false,
screenshotsFolder: 'test-results/screenshots',
videosFolder: 'test-results/videos',
viewportWidth: 1920,
viewportHeight: 1200,
watchForFileChanges: false,
screenshotOnRunFailure: true,
video: false,
videoCompression: 8,
reporter: 'spec',
reporterOptions: {
mochaFile: 'test-results/tests-output/result-[hash].xml',
toConsole: true
},
retries: {
runMode: 1,
openMode: 0
},
e2e: {
setupNodeEvents,
baseUrl: baseUrl,
specPattern: 'cypress/tests/**/*.feature',
},
env: {
...baseConfig, // values from imported file
...env, // add or overwrite with values from above
}
})
As you can see, I've managed to move my baseUrl & env values into qa.json.
However, there are still a lot of duplicate values in cypress.config.js & qa.json.
The command I am using to run the tests is npx cypress open --config-file cypress/config/aqua.js
How can I re-use the queryTestDb() function setupNodeEvents() function & the other config values in cypress.config.js?
There's a package that does pretty much what you want to do.
cypress-extends
In the docs Extending the Cypress Config File
Cypress v10
With the latest Cypress version, config changes from
.jsonto.jsfile.Now you can just import and merge the base.json, but you need to be careful about sub-sections of the comfig
cypress.config.js
Or maybe this is more general
It's more powerful merging in javascript, but also easier to get wrong. Please refer to the Testing Type-Specific Options for a reference to what goes where in the new format.