Skip to content

Commit

Permalink
feat: Add Args support and Angular support
Browse files Browse the repository at this point in the history
Fixes #19
Fixes #13
  • Loading branch information
NicholasBoll committed Jan 3, 2021
1 parent c8e2c39 commit 0fa4a66
Show file tree
Hide file tree
Showing 47 changed files with 18,057 additions and 2,733 deletions.
12 changes: 7 additions & 5 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
cypress
stories
cypress.json
.storybook
.github
.vscode
.prettierrc.json
.github

cypress
cypress.json

angular
react
1 change: 0 additions & 1 deletion .storybook/angular/config.js

This file was deleted.

9 changes: 0 additions & 9 deletions .storybook/angular/main.js

This file was deleted.

1 change: 0 additions & 1 deletion .storybook/react/config.js

This file was deleted.

9 changes: 0 additions & 9 deletions .storybook/react/main.js

This file was deleted.

72 changes: 64 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This library contains helper methods to integrate Cypress and Storybook. It cont
npm install cypress-storybook --save-dev
```

Once installed, both Cypress and Storybook need to be configured in order to work. Storybook installation will be based on the type.
Once installed, both Cypress and Storybook need to be configured in order to work. Storybook installation will be based on the framework used (React and Angular are currently supported).

### Cypress

Expand All @@ -19,27 +19,38 @@ The following will add the Cypress commands to be available to Cypress spec file
import 'cypress-storybook/cypress'
```

Make sure your `baseUrl` in the `cypress.json` is pointing to your Storybook. For development, this will most likely be `http://localhost:9001`.
Make sure your `baseUrl` in the `cypress.json` is pointing to your Storybook. For development, this will most likely be `http://localhost:6006`.

```json
{
"baseUrl": "http://localhost:9001"
"baseUrl": "http://localhost:6006"
}
```

If running these tests as part of a CI process, this base url will have to point to whereever the CI can reach the Storybook page.
If running these tests as part of a CI process, this base url will have to point to where ever the CI can reach the Storybook page.

If your project has Cypress tests for both Storybook and true end-to-end, you may have to use separate `cypress.json` files for each environment that you're running. Cypress commands allow you to specify which config file: https://docs.cypress.io/guides/guides/command-line.html#cypress-open. For example, you may need to do something like `cypress open --config-file cypress-storybook.json`. You can alias this in an `npm` script like `npm run cypress:storybook:open`.

### React Storybook
### Storybook

There are adapters for different frameworks. Currently React and Angular are supported.

The following will set up the Storybook app to understand the Cypress commands. It will register hidden functions on the `window` of the iframe Storybook uses for stories:

### React Storybook

```js
// .storybook/config.js (v5) or .storybook/preview.js (v6)
import 'cypress-storybook/react'
```

### Angular Storybook

```js
// .storybook/config.js (v5) or .storybook/preview.js (v6)
import 'cypress-storybook/angular'
```

## Use

Storybook is a great tool for developing UI. It encourages separation of UI development from backend development. It also encourages building smaller components. Cypress can be used to test or specify behavior of these components. Many examples on the web show loading the main Storybook application and using Cypress to click through the navigation to enable the proper story. The issue with this approach is the story is in an iframe, which is much more difficult to work with. Storybook comes with a router that allows you to visit the story directly. If you expand a story to a full screen, you'll see the URL. It contains something like `iframe.html?id=button--text`.
Expand All @@ -48,6 +59,16 @@ This library works by loading the `iframe.html` which is blank since no story ha

This library only works if Stories don't leave behind some global state. It is recommended that your stories provide their own state. If you use a global store like Redux, be sure that each story has its own store provider so that the store is created for each story.

### Controls/Args

Args are supported. It is possible to use Args where all properties are controls. Changing an Arg will automatically update a story. Controls implicitly use the Actions addon (see Actions below).

Example:

```js
cy.changeArg('buttonText', 'New Text Value')
```

### Knobs

Knobs are supported. It is possible to create a story where all properties are knob imports and change those inputs during a test. Changing a knob will refresh the story clearing any previous changes to the story. Be sure to change knobs at the start of a test.
Expand All @@ -64,11 +85,15 @@ The action addon is supported and will return Sinon Spies. Any assertion that ca

```js
// in a story
export MyStory = () => {
export const MyStory = () => {
return (
<>
<button id="button1" onClick={action('click1')}>Button 1</button>
<button id="button2" onClick={() => action('click2')('foo')}>Button 2</button>
<button id="button1" onClick={action('click1')}>
Button 1
</button>
<button id="button2" onClick={() => action('click2')('foo')}>
Button 2
</button>
</>
)
}
Expand All @@ -83,6 +108,30 @@ it('should trigger the action', () => {
})
```

Actions using Args:

```js
// Story
export default {
title: 'Button',
component: Button,
argTypes: { onClick: { action: 'clicked' } },
}

const Template = (props) => <Button {...props} />

export const Controls = Template.bind({})
Controls.args = {
children: 'Button',
}

// Cypress
it('should trigger the action', () => {
cy.get('button').click()
cy.storyAction('clicked').should('have.been.called')
}
```
An example Cypress file might look like this:
```js
Expand All @@ -108,6 +157,13 @@ describe('Button', () => {
cy.get('button').should('have.text', 'New Text Value')
})

it('should change the Arg', () => {
// first parameter is the name of the Arg
// second parameter is the value of the Arg
cy.changeArg('buttonText', 'New Text Value')
cy.get('button').should('have.text', 'New Text Value')
})

it('should fire the click action', () => {
cy.get('button').click()
// first parameter is the action name - returns a spy for assertions
Expand Down
15 changes: 15 additions & 0 deletions angular.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { forceReRender } from '@storybook/angular'

import { setCurrentStory, changeKnob } from './common'

window.__setCurrentStory = function (categorization, story) {
setCurrentStory(categorization, story)
forceReRender()
}

window.__changeKnob = function (changedKnob) {
changeKnob(changedKnob)

// force story to rerender with updated knob
forceReRender()
}
16 changes: 16 additions & 0 deletions angular/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.ts]
quote_type = single

[*.md]
max_line_length = off
trim_trailing_whitespace = false
46 changes: 46 additions & 0 deletions angular/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.

# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out

# dependencies
/node_modules

# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*

# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings

# System Files
.DS_Store
Thumbs.db
19 changes: 19 additions & 0 deletions angular/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module.exports = {
stories: [
'../stories/**/*.stories.mdx',
'../stories/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-knobs/register',
],

webpackFinal: async (config) => {
config.plugins = config.plugins.filter(
(plugin) => plugin.constructor.name !== 'ProgressPlugin'
)

return config
},
}
9 changes: 9 additions & 0 deletions angular/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { setCompodocJson } from '@storybook/addon-docs/angular'
import docJson from '../documentation.json'
setCompodocJson(docJson)

import '../../angular'

export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
}
20 changes: 20 additions & 0 deletions angular/.storybook/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"extends": "../projects/button/tsconfig.lib.json",
"compilerOptions": {
"types": [
"node"
]
},
"exclude": [
"../src/test.ts",
"../src/**/*.spec.ts",
"../projects/**/*.spec.ts"
],
"include": [
"../src/**/*",
"../projects/**/*"
],
"files": [
"./typings.d.ts"
]
}
4 changes: 4 additions & 0 deletions angular/.storybook/typings.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module '*.md' {
const content: string;
export default content;
}
48 changes: 48 additions & 0 deletions angular/angular.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"button": {
"projectType": "library",
"root": "projects/button",
"sourceRoot": "projects/button/src",
"prefix": "ui",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "projects/button/tsconfig.lib.json",
"project": "projects/button/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "projects/button/tsconfig.lib.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/button/src/test.ts",
"tsConfig": "projects/button/tsconfig.spec.json",
"karmaConfig": "projects/button/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/button/tsconfig.lib.json",
"projects/button/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "button"
}
Loading

0 comments on commit 0fa4a66

Please sign in to comment.