Gaëtan Redin
Posted on February 13, 2022
Storybook for Angular
Context
Nx 13 — Angular 13 — Storybook 6.5.0-alpha.36
Why?
More your application grows more you risk to introduce bugs. In a recent project, we have to kind of tests: Unit tests (so many) and some integration tests. We work incrementally and add features during sprints and rework older ones with more functional rules. Even if we test all devs with unit test, there is a moment that you realize you couldn’t test everything with unit testing.
We have develop a Storybook which contains all our components / directives. Sometimes, we check some of it during a development and Ho surprise, it’s broken :-(. Yet All our unit tests are okay…
How?
I was looking for a solution to introduce easily some functional tests without testing the interactions with the backend. Our goal was only to test functionally our HMI.
I already heard talking about Cypress and after doing some research I found a perfect solution for us. Nx offer the possibility to include an e2e application with Cypress configuration for testing our Storybook (Miracle).
I install all the requirements and start to write some functional tests. What a surprise, some tests failed whereas all our unit tests and integration tests works… As a lead dev, it was first a deception, because it means I didn’t guarantee the quality of our application. But, on second thought, what a joy to see I can still learn and discover new things! I’m sure many of us have already be in this situation. So let’s quit chatting, this is how to do it.
Installation
I suppose you already have a Nx — Angular project with Storybook. Here we just talk about Cypress installation for Storybook.
nx generate [@nrwl/storybook](http://twitter.com/nrwl/storybook):cypress-project --name={your_app_name} --linter=eslint --no-interactive
My app is called “storybook” and that’s the result of the command:
nx generate [@nrwl/storybook](http://twitter.com/nrwl/storybook):cypress-project --name=storybook --linter=eslint --no-interactive
UPDATE package.json
CREATE apps/storybook-e2e/cypress.json
CREATE apps/storybook-e2e/src/fixtures/example.json
CREATE apps/storybook-e2e/src/support/commands.ts
CREATE apps/storybook-e2e/src/support/index.ts
CREATE apps/storybook-e2e/tsconfig.json
CREATE apps/storybook-e2e/project.json
UPDATE workspace.json
CREATE apps/storybook-e2e/.eslintrc.json
UPDATE nx.json
In the package.json , we can see two more dev dependencies: @nrwl/cypress and cypress , logic.
In the storybook-e2e/tsconfig.json , we have this:
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"sourceMap": _false_,
"outDir": "../../dist/out-tsc",
"allowJs": _true_,
"types": ["cypress", "node"]
},
"include": ["src/ **/*.ts", "src/** /*.js"]
}
Nothing special, except the type “cypress”.
Now let’s have a look into the storybook-e2e/project.json file:
{
"root": "apps/storybook-e2e",
"sourceRoot": "apps/storybook-e2e/src",
"projectType": "application",
"targets": {
"e2e": {
"executor": "@nrwl/cypress:cypress",
"options": {
"cypressConfig": "apps/storybook-e2e/cypress.json",
"devServerTarget": "storybook:storybook"
},
"configurations": {
"ci": {
"devServerTarget": "storybook:storybook:ci"
}
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/storybook-e2e/**/*.{js,ts}"]
}
}
},
"tags": [],
"implicitDependencies": ["storybook"]
}
As we can see, the target server is defined with our storybook app "devServerTarget":"storybook:storybook:ci" which is launch in ci mode. So there is an implicit dependency with our Storybook app "implicitDependencies": ["storybook"] .
You already can to try it:
nx e2e {your_app_name}-e2e
It will failed with something like:
Can't run because no spec files were found.
We searched for any files inside of this folder:
...\apps\storybook-e2e\src\integration
Yes, we don’t have write tests yet. If you don’t know how to write cypress tests, you can read the documentation here which is perfect.
This commands is perfect for the testing in terminal. But some of us prefer to see it in a real window. To open cypress and execute the tests on demand, I suggest you to create another cypress.json like this:
// cypress.local.json
{
"fileServerFolder": ".",
"fixturesFolder": "apps/storybook-e2e/src/fixtures",
"integrationFolder": "apps/storybook-e2e/src/integration",
"modifyObstructiveCode": _false_,
"supportFile": "apps/storybook-e2e/src/support/index.ts",
"pluginsFile": _false_,
"video": _true_,
"videosFolder": "../../dist/cypress/apps/storybook-e2e/videos",
"screenshotsFolder": "../../dist/cypress/apps/storybook-e2e/screenshots",
"chromeWebSecurity": _false_,
"baseUrl": "http://localhost:4400" // set the url of your local Storybook
}
Add this command into your package.json :
"test:storybook:local": "cypress open --browser ~\\chrome-win\\chrome.exe -C apps/storybook-e2e/cypress.local.json"
Here I use chromium as browser and I specify my local cypress config.
Now you can launch your local storybook and your local cypress:
nx run storybook:storybook
npm run test:storybook:local
It will fail until you will have write some tests.
To help writing test for Storybook, I suggest you to install the following library:
npm i -D cypress-storybook
Then import the helper commands into the support index file:
// apps/storybook-e2e/src/support/index.ts
import 'cypress-storybook/cypress'
Then import that in the storybook preview file, it help storybook to understand the cypress commands:
// apps/storybook/.storybook/preview.js
import 'cypress-storybook/angular'
At last, in your test file what you have to do:
describe('MyComponent', () => {
beforeEach(() => {
cy.visitStorybook();
cy.loadStory('title-of-my-story', 'MyStoryName');
});
it('my test', () => {
...
});
});
In the documentation, it’s recommended to wrap the cy.visitStorybook(); in a before and not a beforeEach but when I tried, it does not reset the Story in form cases between each test.
Conclusion
Now you will be more armed to defeat bugs! No excuse, particularly if you use Nx which helps a lot for the configuration. I suggest you to write some commands to simplify writing tests like a fillForm command which get a fixture and a form tag for example. It will make your test more readable.
Thanks for reading.
Learn more
Posted on February 13, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.