9.4 — process: argv, env & exit
How does the SAME test suite run against localhost on your machine, against staging — the team’s private practice copy of the site — in CI, and against the live site itself, without changing a line of code? Meet process: the object through which a Node program reads its own run — its command line, its environment, its exit.
This is where BASE_URL lives, where secrets hide, and where 9.2’s exit code gets set on purpose. Small lesson, permanent plumbing.
// greet.js
console.log(process.argv[2]);
// $ node greet.js Lijas → Lijas
// check-env.js
const base = process.env.BASE_URL ?? "http://localhost:3000";
console.log(`testing against: ${base}`);
// $ node check-env.js
// → testing against: http://localhost:3000
// $ BASE_URL=https://staging.shop.com node check-env.js
// → testing against: https://staging.shop.com
if (!process.env.API_KEY) {
console.error("missing API_KEY");
process.exit(1);
}In Node, one built-in global describes THIS very run of your program: process. Not the file, not the language — this execution, right now: what command started it, what environment surrounds it, how it will end. A program, introspecting itself.
The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”
Everything in process.env is a string — always. PORT=3000 arrives as "3000", and CI=true arrives as "true" (the string!). The 1.9 coercion traps live here in the wild; real configs convert deliberately (Number(process.env.PORT), process.env.CI === "true").
Where do env vars come from? Layered sources: your shell session, the launch line itself (BASE_URL=… node app.js), CI pipeline settings, and — very commonly — a .env file loaded by a small library, with .env listed in .gitignore (8.2’s rule again: local, regenerable, never committed).
Honesty note on process.exit(): it’s an ejector seat — it ends the process immediately, without waiting for pending async work (6.x’s parked jobs are simply abandoned). Guard clauses at startup: perfect. Sprinkled mid-flight: a classic source of half-written files.
Job note: the convention CI=true is set by virtually every CI provider, and real Playwright configs read it to switch behavior — more retries and no headed browser on CI, for example. One env var, two worlds correctly served.
⌨️ the environment-driven script
Model a real suite’s startup: read an input from argv, aim at a target from env (with an honest default), and guard a required secret with an exit code.
requirements:
- Model the run:
argvas["node", "greet.js", "Lijas"]andenvas{ BASE_URL: "https://staging.shop.com" }. - Print the user’s input — remember where inputs start in argv.
- Write
targetUrl(env)returningenv.BASE_URLwith the default"http://localhost:3000"when it’s absent (8.4’s honest default). Print it forenv, then for an empty object. - Write
guard(env)returning the exit code:0whenBASE_URLis present,1when missing. Print it for both environments.
when you press RUN, the console must show exactly:
✏️ Quick check 1
You run: node greet.js Lijas — what is process.argv[2]?
✏️ Quick check 2
PORT=3000 arrives in process.env — what is typeof process.env.PORT?
✏️ Quick check 3
A startup guard finds a required secret missing and calls process.exit(1). What color does the CI pipeline turn?
🗣️ Now teach it back
Explain to a friend how one test suite runs against localhost, staging, and production without code changes — covering argv, env vars (and where secrets live), and the exit-code guard pattern.
Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.