Breaking the Browser Sandbox: A Complete Guide to Cypress Custom Tasks with TypeScript
ON THIS PAGE
Why Do We Need
cy.task?When Should You Use It?
The Critical Difference:
cy.taskvs. Helper FunctionsStep-by-Step Implementation Guide
Step 1:
cypress.config.ts(The Backend Logic)Step 2:
cypress/support/commands.ts(The Command Wrapper)Step 3:
cypress/support/index.d.ts(The TypeScript Definition)How to Use It
Summary
As SDETs, we love Cypress for its speed and stability. However, because Cypress runs directly inside the browser, it follows strict security rules. It is "sandboxed," meaning it cannot reach outside the browser to touch your operating system, file system, or database directly.
This is where cy.task() comes in. It acts as a bridge between the Browser (where your tests run) and the Node.js process (where Cypress is controlled).
Why Do We Need cy.task?
Imagine you are testing a "Export to PDF" feature.
Browser: You click the "Download" button.
Browser: Cypress sees the button click.
Reality: The file is saved to your computer's
Downloadsfolder.Problem: The browser cannot look into your computer's hard drive to confirm the file is there.
cy.task allows you to execute Node.js code to check that file, seed a database, or even query a mail server, and return the result to the browser.
When Should You Use It?
You should use cy.task whenever you need to perform an action that a browser is security-restricted from doing:
Database Seeding: Inserting test data directly into SQL/NoSQL databases before a test (e.g., creating a user).
File System (fs): Checking if a file exists, reading a file's content, or deleting downloaded files.
Server-Side Validation: checking if an email was actually sent (e.g., checking Mailosaur or a local SMTP server).
OS Operations: Running a shell command.
The Critical Difference: cy.task vs. Helper Functions
A common question is: "Why can't I just write a normal TypeScript function in utils.ts?"
Feature | Helper Function (utils.ts) | Cypress Task (cy.task) |
Where it runs | Runs in the Browser | Runs in Node.js (Backend) |
Capabilities | strict JavaScript (Math, formatting strings, API calls via fetch) | Full OS access (File System, Database connections, Shell commands) |
Example |
|
|
Can it use | No (Browser crashes) | Yes |
If your logic is pure JavaScript (like calculating a sum), use a Helper. If your logic needs to touch the system (like reading a file), use a Task.
Step-by-Step Implementation Guide
To implement a task in TypeScript properly, we need to touch three files. Let's create a task that checks if a file exists.
Step 1: cypress.config.ts (The Backend Logic)
This is where the actual Node.js code lives. We use the setupNodeEvents function.
import { defineConfig } from "cypress";
import * as fs from "fs"; // Import Node.js File System module
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
// Register the task here
on("task", {
checkFileExists(filePath: string) {
// This code runs in Node.js
if (fs.existsSync(filePath)) {
return true;
}
return null; // Tasks must return a value or null (not undefined)
},
});
},
},
});
Step 2: cypress/support/commands.ts (The Command Wrapper)
Calling cy.task('checkFileExists', path) every time is verbose. Let's wrap it in a custom command for cleaner code.
Cypress.Commands.add("verifyFileExists", (filePath: string) => {
cy.task("checkFileExists", filePath).then((exists) => {
// We can add an assertion here to make the test cleaner
expect(exists).to.be.true;
});
});
Step 3: cypress/support/index.d.ts (The TypeScript Definition)
Since we are using TypeScript, we must tell the compiler that our new command verifyFileExists exists. Without this, your IDE will show a red error line.
/// <reference types="cypress" />
declare namespace Cypress {
interface Chainable {
/**
* Custom command to check if a file exists on the disk.
* @example cy.verifyFileExists('downloads/invoice.pdf')
*/
verifyFileExists(filePath: string): Chainable<void>;
}
}
How to Use It
Now, you can use your new command in any spec file just like a native Cypress command.
File: cypress/e2e/download-test.cy.ts
describe("File Download Verification", () => {
it("should download the invoice successfully", () => {
// 1. Perform the action in the browser
cy.get(".btn-download-invoice").click();
// 2. Wait briefly for the download to complete
cy.wait(2000);
// 3. Verify the file exists on the OS level
const filePath = "cypress/downloads/invoice.pdf";
cy.verifyFileExists(filePath);
});
});
Summary
Define the Node.js logic in
cypress.config.ts(usingon('task')).Wrap the task in
cypress/support/commands.tsfor ease of use.Type the command in
cypress/support/index.d.tsto keep TypeScript happy.
By mastering cy.task, you unlock the ability to test the entire application lifecycle, not just the frontend UI.
Happy Testing!
#cypress #typescript #sdet #testing #automation

