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?
                        
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.jsI wasn't sure what
defineConfigwas returning in my basecypress.config.jsbut it looks like you can use it as a normal object.I used lodash's merge instead of
Object.assignor spread because I wanted to deep merge the config.