In my Cypress 10 project, I have the following config files:
cypress/cypress.config.js
cypress/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
?
If you're willing to use lodash (looks like it's a dependency of cypress anyway), this worked for me in cypress 13
cypress.custom.js
Then run
npx cypress open --config-file ./cypress.custom.js
I wasn't sure what
defineConfig
was returning in my basecypress.config.js
but it looks like you can use it as a normal object.I used lodash's merge instead of
Object.assign
or spread because I wanted to deep merge the config.