Skip to content

Commit 39bd9f7

Browse files
authored
Element coverage 214 (#215)
* working test * add recipe to circle list and readme * actually add job to the circle workflow * add wait-on utility if needed * add public/index.html file * ignore deferred.js in one of the recipes * more assertions for failing codepen * wait for url * use Cy 3.1.3 * add manual wait
1 parent bd40fd3 commit 39bd9f7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1890
-33
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,12 @@ Get around the lack of a `.hover()` command.
209209
- Use [`cy.request()`](https://on.cypress.io/api/request) to load a document into test iframe.
210210
- Test [HyperApp.js](https://hyperapp.js.org/) application through the DOM and through actions.
211211

212+
### [Element Coverage](./examples/blogs__element-coverage)
213+
214+
- Blog post [Element coverage](https://www.cypress.io/blog/2018/12/20/element-coverage/)
215+
- Overwrite several built-in Cypress commands like `cy.type` and `cy.click`
216+
- Draw elements after the tests finish
217+
212218
### [Testing Redux Store](./examples/blogs__testing-redux-store)
213219

214220
- control application via DOM and check that Redux store has been properly updated

circle.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ jobs:
5959
<<: *defaults
6060
blogs__e2e-snapshots:
6161
<<: *defaults
62+
blogs__element-coverage:
63+
<<: *defaults
6264
blogs__testing-redux-store:
6365
<<: *defaults
6466
blogs__vue-vuex-rest:
@@ -123,6 +125,9 @@ workflows:
123125
- blogs__e2e-snapshots:
124126
requires:
125127
- build
128+
- blogs__element-coverage:
129+
requires:
130+
- build
126131
- blogs__testing-redux-store:
127132
requires:
128133
- build

examples/blogs__codepen-demo/cypress/integration/spec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/// <reference types="cypress" />
12
/* eslint-env mocha */
23
/* global cy */
34
// our Codepen has top level URL
@@ -18,12 +19,19 @@ describe('HyperApp Counter Codepen', () => {
1819
})
1920
.its('body')
2021
.then(html => {
22+
cy.log('got document from url', iframeUrl)
23+
cy.log('document length', html.length)
24+
cy.wrap(html).its('length').should('be.greaterThan', 2000)
25+
2126
cy.document().then(document => {
2227
cy.log('Writing HTML into test document')
2328
document.write(html)
2429
document.close()
2530
})
2631
})
32+
33+
cy.wait(1000)
34+
cy.url().should('include', 'localhost:')
2735
cy.get('main').should('be.visible')
2836
})
2937

examples/blogs__element-coverage/.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
SKIP_PREFLIGHT_CHECK=true
2+
BROWSER=none
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Element coverage
2+
3+
Code for blog post [Element coverage](https://www.cypress.io/blog/2018/12/20/element-coverage/)
4+
5+
## Shows how to
6+
7+
- overwrite several built-in Cypress commands in [cypress/support/commands.js](cypress/support/commands.js) to keep track of elements the test interacts with
8+
- draw elements after the tests finish
9+
10+
![Elements](img/elements.png)
11+
12+
## Development
13+
14+
To make sure the server starts and builds the app _before_ the tests start on CI, [wait-on](https://github.com/jeffbski/wait-on#readme) utility is used to check on local port 3000 before running Cypress tests. See the NPM scripts in the [package.json](package.json) file.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"baseUrl": "http://localhost:3000",
3+
"ignoreTestFiles": [
4+
"**/*.snap",
5+
"**/__snapshot__/*"
6+
],
7+
"env": {
8+
"cypress-plugin-snapshots": {}
9+
}
10+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"example": "fixture"
3+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/// <reference types="cypress" />
2+
/* eslint-env browser */
3+
beforeEach(() => {
4+
cy.visit('/')
5+
})
6+
it('works', function () {
7+
cy.get('.new-todo').type('first todo{enter}')
8+
cy.get('.new-todo').type('second todo{enter}')
9+
cy.get('.todo-list li').should('have.length', 2)
10+
.first().find(':checkbox').check()
11+
12+
cy.contains('.filters a', 'Active').click()
13+
cy.contains('.filters a', 'Active').should('have.class', 'selected')
14+
15+
cy.contains('.filters a', 'Completed').click()
16+
cy.contains('.filters a', 'Completed').should('have.class', 'selected')
17+
18+
cy.contains('.filters a', 'All').click()
19+
cy.contains('.filters a', 'All').should('have.class', 'selected')
20+
})
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// ***********************************************************
2+
// This example plugins/index.js can be used to load plugins
3+
//
4+
// You can change the location of this file or turn off loading
5+
// the plugins file with the 'pluginsFile' configuration option.
6+
//
7+
// You can read more here:
8+
// https://on.cypress.io/plugins-guide
9+
// ***********************************************************
10+
11+
// This function is called when a project is opened or re-opened (e.g. due to
12+
// the project's config changing)
13+
14+
/* eslint-disable no-unused-vars */
15+
module.exports = (on, config) => {
16+
// `on` is used to hook into various events Cypress emits
17+
// `config` is the resolved Cypress config
18+
}
19+
/* eslint-enable no-unused-vars */
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// ***********************************************
2+
// This example commands.js shows you how to
3+
// create the custom commands: 'createDefaultTodos'
4+
// and 'createTodo'.
5+
//
6+
// The commands.js file is a great place to
7+
// modify existing commands and create custom
8+
// commands for use throughout your tests.
9+
//
10+
// You can read more about custom commands here:
11+
// https://on.cypress.io/commands
12+
// ***********************************************
13+
14+
/* eslint-env browser */
15+
16+
// libraries that compute selectors compared in
17+
// https://github.com/fczbkk/css-selector-generator-benchmark
18+
const finder = require('@medv/finder').default
19+
20+
before(() => {
21+
window.testedSelectors = []
22+
})
23+
24+
after(() => {
25+
const selectors = Cypress._.uniq(window.testedSelectors)
26+
27+
// eslint-disable-next-line no-console
28+
console.log('tested the following selectors:', selectors)
29+
30+
// shortcut to get application's window context
31+
// without going through cy.window() command
32+
const win = cy.state('window')
33+
34+
selectors.forEach((selector) => {
35+
const el = win.document.querySelector(selector)
36+
37+
if (el) {
38+
el.style.opacity = 1
39+
el.style.border = '1px solid magenta'
40+
}
41+
})
42+
43+
// add pause if recording a video
44+
// cy.wait(1000, { log: false })
45+
})
46+
47+
const getSelector = ($el) => {
48+
if ($el.attr('data-cy')) {
49+
return `[data-cy=${$el.attr('data-cy')}]`
50+
}
51+
52+
return finder($el[0], {
53+
// a trick to point "finder" at the application's iframe
54+
root: cy.state('window').document.body,
55+
})
56+
}
57+
58+
const rememberSelector = ($el) => {
59+
const selector = getSelector($el)
60+
61+
window.testedSelectors.push(selector)
62+
}
63+
64+
Cypress.Commands.overwrite('type', function (type, $el, text, options) {
65+
rememberSelector($el)
66+
67+
return type($el, text, options)
68+
})
69+
70+
Cypress.Commands.overwrite('check', function (check, $el, options) {
71+
rememberSelector($el)
72+
73+
return check($el, options)
74+
})
75+
76+
Cypress.Commands.overwrite('click', function (click, $el, options) {
77+
rememberSelector($el)
78+
79+
return click($el, options)
80+
})
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// ***********************************************
2+
// This example defaults.js shows you how to
3+
// customize the internal behavior of Cypress.
4+
//
5+
// The defaults.js file is a great place to
6+
// override defaults used throughout all tests.
7+
//
8+
// ***********************************************
9+
//
10+
// Cypress.Server.defaults({
11+
// delay: 500,
12+
// whitelist: function(xhr){}
13+
// })
14+
15+
// Cypress.Cookies.defaults({
16+
// whitelist: ["session_id", "remember_token"]
17+
// })
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// ***********************************************************
2+
// This example support/index.js is processed and
3+
// loaded automatically before your other test files.
4+
//
5+
// This is a great place to put global configuration and
6+
// behavior that modifies Cypress.
7+
//
8+
// You can change the location of this file or turn off
9+
// automatically serving support files with the
10+
// 'supportFile' configuration option.
11+
//
12+
// You can read more here:
13+
// https://on.cypress.io/guides/configuration#section-global
14+
// ***********************************************************
15+
16+
// Import commands.js and defaults.js
17+
// using ES2015 syntax:
18+
// import "./commands"
19+
// import "./defaults"
20+
21+
// Alternatively you can use CommonJS syntax:
22+
require('./commands')
23+
require('./defaults')
Loading
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "blogs__element-coverage",
3+
"version": "1.0.0",
4+
"description": "Showing elements the test has interacted with",
5+
"private": true,
6+
"scripts": {
7+
"start": "../../node_modules/.bin/react-scripts start",
8+
"wait-on": "../../node_modules/.bin/wait-on http://localhost:3000",
9+
"cypress:run": "npm run wait-on && ../../node_modules/.bin/cypress run",
10+
"cypress:open": "../../node_modules/.bin/cypress open"
11+
},
12+
"browserslist": [
13+
">0.2%",
14+
"not dead",
15+
"not ie <= 11",
16+
"not op_mini all"
17+
]
18+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>Redux TodoMVC Example</title>
7+
</head>
8+
<body>
9+
<div class="todoapp" id="root"></div>
10+
<!--
11+
This HTML file is a template.
12+
If you open it directly in the browser, you will see an empty page.
13+
14+
You can add webfonts, meta tags, or analytics to this file.
15+
The build step will place the bundled scripts into the <body> tag.
16+
17+
To begin the development, run `npm start` in this folder.
18+
To create a production bundle, use `npm run build`.
19+
-->
20+
</body>
21+
</html>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as types from '../constants/ActionTypes'
2+
3+
export const addTodo = text => ({ type: types.ADD_TODO, text })
4+
export const deleteTodo = id => ({ type: types.DELETE_TODO, id })
5+
export const editTodo = (id, text) => ({ type: types.EDIT_TODO, id, text })
6+
export const completeTodo = id => ({ type: types.COMPLETE_TODO, id })
7+
export const completeAllTodos = () => ({ type: types.COMPLETE_ALL_TODOS })
8+
export const clearCompleted = () => ({ type: types.CLEAR_COMPLETED })
9+
export const setVisibilityFilter = filter => ({ type: types.SET_VISIBILITY_FILTER, filter})
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import * as types from '../constants/ActionTypes'
2+
import * as actions from './index'
3+
4+
describe('todo actions', () => {
5+
it('addTodo should create ADD_TODO action', () => {
6+
expect(actions.addTodo('Use Redux')).toEqual({
7+
type: types.ADD_TODO,
8+
text: 'Use Redux'
9+
})
10+
})
11+
12+
it('deleteTodo should create DELETE_TODO action', () => {
13+
expect(actions.deleteTodo(1)).toEqual({
14+
type: types.DELETE_TODO,
15+
id: 1
16+
})
17+
})
18+
19+
it('editTodo should create EDIT_TODO action', () => {
20+
expect(actions.editTodo(1, 'Use Redux everywhere')).toEqual({
21+
type: types.EDIT_TODO,
22+
id: 1,
23+
text: 'Use Redux everywhere'
24+
})
25+
})
26+
27+
it('completeTodo should create COMPLETE_TODO action', () => {
28+
expect(actions.completeTodo(1)).toEqual({
29+
type: types.COMPLETE_TODO,
30+
id: 1
31+
})
32+
})
33+
34+
it('completeAll should create COMPLETE_ALL action', () => {
35+
expect(actions.completeAllTodos()).toEqual({
36+
type: types.COMPLETE_ALL_TODOS
37+
})
38+
})
39+
40+
it('clearCompleted should create CLEAR_COMPLETED action', () => {
41+
expect(actions.clearCompleted()).toEqual({
42+
type: types.CLEAR_COMPLETED
43+
})
44+
})
45+
})
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react'
2+
import Header from '../containers/Header'
3+
import MainSection from '../containers/MainSection'
4+
5+
const App = () => (
6+
<div>
7+
<Header />
8+
<MainSection />
9+
</div>
10+
)
11+
12+
export default App
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react'
2+
import { createRenderer } from 'react-test-renderer/shallow'
3+
import App from './App'
4+
import Header from '../containers/Header'
5+
import MainSection from '../containers/MainSection'
6+
7+
8+
const setup = propOverrides => {
9+
const renderer = createRenderer()
10+
renderer.render(<App />)
11+
const output = renderer.getRenderOutput()
12+
return output
13+
}
14+
15+
describe('components', () => {
16+
describe('Header', () => {
17+
it('should render', () => {
18+
const output = setup()
19+
const [ header ] = output.props.children
20+
expect(header.type).toBe(Header)
21+
})
22+
})
23+
24+
describe('Mainsection', () => {
25+
it('should render', () => {
26+
const output = setup()
27+
const [ , mainSection ] = output.props.children
28+
expect(mainSection.type).toBe(MainSection)
29+
})
30+
})
31+
})

0 commit comments

Comments
 (0)