No need for child_process.exec() etc. Pre and post scripts are respected too. Read here on dev.to.

The problem of mine

While working on the cypress-wait-until library, I’d like to upload the test videos on the Cypress Dashboard. Doing that is really simple, I only had to update my package.json dedicated script:

{

"scripts": {

// from

"cy:run": "cypress run" // to

"cy:run": "cypress run --record --key YOUR_SECRET_KEY"

}

}

But I had to face a big problem: the YOUR_SECRET_KEY environment variable was going to be available just in the Travis build, not locally. And if you run it on your local machine, without a defined YOUR_SECRET_KEY, the cypress run command failed.

There are a lot of solutions to the above problem (like child_process.exec)but all of them come with a cost and some effects to be managed. More: I needed to change the way I manage the package.json scripts too.

I’d like to have a transparent solution that allowed me to make the YOUR_SECRET_KEY optional but without changing anything in my scripts management.

Npm as a local dependency

While googling I found an interesting solution: installing Npm locally and leveraging its APIs. How it works:

first of all, install npm with

$ npm install --save-dev npm

then, create a .js file

$ touch index.js && open index.js

import npm

const npm = require("npm");

let npm loading the current project

npm.load();

pass a callback to the npm.load() API and run the script of your

npm.load(() => npm.run("SCRIPT_NAME"));

Some notes about the npm.run() API:

you cannot run it without calling npm.load in advance. If you try to do that you get a “Error: Call npm.load(config, cb) before using this command.” error

the first parameter is the script name, if you need to pass some options you must pass an array of strings

npm.run("test param"); // Error: missing script: test param npm.run("test", "param"); // "param" is passed to the "test" script

pre and post package.json scripts are launched too

I shared a npm-run-programmatically-example repository where you play with it, here a recorded gif of a terminal session of the repository:

How this solution fitted my needs

I solved the original problem this way:

I added one more cy:run-uploading-videos script to be launched instead of cy:run

{

"scripts": {

"cy:run-uploading-videos": "node cypress-run.js",

"cy:run": "cypress run"

}

}

the cypress-run.js file looked like this

const npm = require('npm');

npm.load(() => {

const key = process.env.CYPRESS_RECORD_KEY;

const options = key ? ['--record', '--key', key] : [];

npm.run('cy:run', ...options);

});

So I could launch the usual cy:run script locally and launching cy:run-uploading-videos on Travis 🎉 (without duplicating the cypress run call that could have more parameters in the future).

What could I do with npm.run()?

An idea of mine is nprr, a tool that enhances npm run with autocomplete. Have you ever felt the situation when you do not remember the name of the script you want to run? So you open the package.json file, look for the script name and then run it… Well, nprr solves this problem! Watch it in action:

The possibilities are endless and if you leverage npm programmatically, please leave a comment with your experiments/packages/utilities, etc. 😊