Plugins & cy.task¶
What was run¶
A real Node-side task, registered via setupNodeEvents in cypress.config.js, that writes to the actual
terminal and performs a genuine filesystem read/write — both things Cypress's in-browser test code
(Level 4, Lesson 1) cannot do directly, confirmed by actually
running them rather than assuming the capability boundary.
// cypress.config.js
setupNodeEvents(on, config) {
on("task", {
logToRealTerminal(message) {
console.log("REAL TASK LOG:", message);
return null;
},
writeRealFile({ path, contents }) {
fs.writeFileSync(path, contents);
return null;
},
readRealFile(path) {
return fs.readFileSync(path, "utf8");
},
});
return config;
},
it("logs a real message to the actual terminal, not just the browser console", () => {
cy.task("logToRealTerminal", "hello from a real cy.task call");
});
it("writes and reads a real file on disk via Node, not the browser", () => {
const path = "/tmp/real_cy_task_test.txt";
cy.task("writeRealFile", { path, contents: "real content written via cy.task" });
cy.task("readRealFile", path).should("eq", "real content written via cy.task");
});
That REAL TASK LOG: ... line genuinely appeared in the actual shell running cypress run — not inside
Cypress's own command log, not in a browser console. The file write/read round-trip also genuinely
succeeded against the real filesystem, something a sandboxed browser tab (even Cypress's own in-browser
test code) cannot do directly via any cy command.
How It Actually Works¶
setupNodeEvents runs in the real Node.js process behind Cypress — the same real process this course has
confirmed, via process inspection in Lesson 1, sits alongside (but
separate from) the browser's own renderer process. on("task", {...}) registers real Node functions that
cy.task("name", arg) can invoke from a test, crossing from the browser's sandboxed JS context into
genuine, unrestricted Node code — real filesystem access, real database queries, real console.log output
to the actual terminal, anything a plain Node script could do. This is the real, structural reason
cy.task() exists at all: Cypress's own in-browser commands are deliberately sandboxed the same way any
browser tab's JavaScript is, and cy.task() is the one sanctioned bridge back out to the unrestricted
Node environment for the specific, narrow set of things a test genuinely needs that a browser can't do on
its own.
This also explains a real, useful contrast with cy.log(): cy.log() writes to Cypress's own in-browser
command log, visible in the interactive Test Runner's UI, but it never appears in a cypress run terminal
session the way this lesson's cy.task() output does — confirmed throughout this course's earlier
lessons, where cy.log() calls never once showed up in any captured terminal output.
Common mistakes¶
- Reaching for
cy.log()when debugging a headlesscypress runand being confused that nothing prints to the terminal — this lesson's real result showscy.task()with a Node-sideconsole.logis the correct tool for that, notcy.log(). - Putting heavy real logic (complex data transformation, slow real I/O) directly inside
setupNodeEventswithout considering it blocks that one Node process — a real task that's genuinely slow (a large file write, a real database migration) adds that real time to every test that calls it. - Forgetting that
cy.task()arguments and return values must be serializable — functions, DOM nodes, or other non-JSON-safe values can't cross the real boundary between the browser context and the Node process the way this lesson's plain strings and objects did.
Exercise¶
Register a new task, countRealLinesInFile(path), that uses Node's real fs.readFileSync plus
.split("\n").length to count a file's real lines, and call it against this course's own
cypress.config.js. Confirm the real returned count matches what a manual wc -l cypress.config.js in
the terminal reports — directly verifying cy.task()'s real Node-side computation against an independent,
non-Cypress check.