Switching to Cypress from Protractor in Less Than 30 Seconds
If you haven’t heard of Cypress yet, I’m sure you will soon. Cypress is a self-proclaimed “developer-friendly” open source end-to-end testing framework. It offers features such as time travel, dev tool debugging, live reloads, automatic waiting (no waits or sleeps in test code), as well as screenshots and videos of test runs.
But I’m actually not here to tell you about Cypress itself or how to test with it (they have great documentation already). I’m going to show you how I created a schematic that allows you to easily install Cypress with Typescript compatibility out of the box. It also allows you to remove Protractor and its dependencies from your project if you so choose.
Alright, let’s get into some code.
If you’d like to follow along and/or contribute Fork the repo on Github.
To install: npm install -g @briebug/cypress-schematic
& ng g @briebug/cypress-schematic:add
or ng add @briebug/cypress-schematic
Every schematic starts off with a collection.json
file.
This is essentially a list of schematic commands, extending the @angular-devkit collection-schema. For each schematic, the factory
and description
properties are required, but there are several other schematic properties that can be added as well. One of those is the schema
property. This allows us to create a schema.json
file for our individual schematic:
The schema.json
is where you specify the details of your schematic and its properties. Properties are basically the “options” of schematics. For instance, in the @angular/cli
new schematic, you have properties like styleext
where you can set your project’s default style to CSS or one of the supported CSS preprocessors. Here, I’ve inserted a removeProtractor
property of type boolean
, with a CLI prompt asking the user “Would you like to remove Protractor from the project?”
. You can also set certain properties as required
. Just include the property name as a string in the required array.
Next up is the index.ts
file. This is where the magic (logic) happens. Let’s look at each part separately:
From the index.ts
file, you need to export at least one function. Here I’ve chosen to export a default function, but you can use a named function if you like. This function is going to return a Rule
, which “transforms a tree into another tree”. Now you may be asking “what’s a tree?”. A Tree
is an interface containing a bunch of really useful methods for creating, modifying, and removing files and directories from your schematic.
In this default function, I’m returning a chain of Rule
s that will comprise our “transformed tree”. First, I update the package.json
dependencies, then remove any unnecessary files, after that, I add the files needed for Cypress as well as a couple of scripts to get Cypress going.
While updating the package.json
dependencies, I’m first going to check the value of the removeProtractor
options property. If you remember the schema.json
file, it’s the only property we defined for our schematic, and it will prompt the user to enter a boolean value. If removeProtractor
is true, I remove that dependency using a utility method called removePackageJsonDependency
. If you check out the repository, you’ll see a utility directory with three different ts files: dependencies.ts
, json-utils.ts
, and util.ts
. The first two are borrowed from the @angular/cli
schematics, and the util.ts
file is custom. These files contain helper methods for building schematics, interacting with the tree, and traversing/modifying json.
In this schematic, I only remove files if removeProtractor
has a value of true
. Otherwise, I skip right to returning the tree
. Using a tree
method, I delete the entire e2e
directory. Then by modifying the angular.json
file, I remove the e2e
“project”.
Adding files is one of the most powerful things you can do with a schematic. You’ll mainly see this in the @angular/cli
generate schematics, such as ng generate module “module name”
or ng generate component “component name”
. Here, I’m adding a tsconfig.json
file which extends the root tsconfig.json
, as well as two plugin files, a cypress/typescript preprocessor, and an index.js file. These allow you to write your cypress tests in TypeScript.
Finally, I add a couple of scripts to the package.json
file. cypress-open
and cypress-run
. These definitely aren’t required, but allow you to run yarn cypress-open
rather than $(npm bin)/cypress open
.
I think schematics are an incredibly powerful tool, and we’re just seeing the tip of the iceberg as far as what this community will do with them. In this relatively small schematic, I was able to add and remove package.json
dependencies, remove and add files within the project, modify the angular.json
, as well as add scripts to the package.json
.
Thank you for taking the time to read this article, and if you’d like to dig further into schematics, here are a couple of great resources:
https://brianflove.com/2018/12/11/angular-schematics-tutorial/
I’d like to thank Kevin Schuchard for setting up the Schematic Sandbox, and writing the Jest schematic which inspired this schematic. (More info on this sandbox approach is detailed in this blog post kevinschuchard.com/blog/2018–11–20-schematic-sandbox/) Also, thank you to Zahid Mahmood for writing this blog post detailing setting up cypress in an Angular project.