mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-26 08:59:03 -06:00
Fix eslint errors
This commit is contained in:
@@ -39,3 +39,5 @@ npm/webpack-preprocessor/examples/use-babelrc/cypress/integration/spec.js
|
||||
**/.history
|
||||
**/.cy
|
||||
**/.git
|
||||
|
||||
/npm/react/bin/*
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -323,4 +323,5 @@ $RECYCLE.BIN/
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
/npm/react/bin/*
|
||||
# End of https://www.gitignore.io/api/osx,git,node,windows,intellij,linux
|
||||
|
||||
43
npm/react/.eslintrc
Normal file
43
npm/react/.eslintrc
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"plugins": [
|
||||
"cypress",
|
||||
"@cypress/dev"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@cypress/dev/general",
|
||||
"plugin:@cypress/dev/tests",
|
||||
"plugin:@cypress/dev/react"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"cypress/globals": true
|
||||
},
|
||||
"globals": {
|
||||
"jest": "readonly"
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "off",
|
||||
"mocha/no-global-tests": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"react/jsx-filename-extension": [
|
||||
"warn",
|
||||
{
|
||||
"extensions": [
|
||||
".js",
|
||||
".jsx",
|
||||
".tsx"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"lib/*"
|
||||
],
|
||||
"rules": {
|
||||
"no-console": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -13,7 +13,10 @@
|
||||
"root": "#cypress-root"
|
||||
}
|
||||
},
|
||||
"ignoreTestFiles": ["**/__snapshots__/*", "**/__image_snapshots__/*"],
|
||||
"ignoreTestFiles": [
|
||||
"**/__snapshots__/*",
|
||||
"**/__image_snapshots__/*"
|
||||
],
|
||||
"experimentalComponentTesting": true,
|
||||
"experimentalFetchPolyfill": true
|
||||
}
|
||||
|
||||
@@ -21,18 +21,19 @@ describe('Component and API tests', () => {
|
||||
// same or similar URL to the one the component is using
|
||||
url: 'https://jsonplaceholder.cypress.io/users?_limit=3',
|
||||
})
|
||||
.its('body')
|
||||
.should('have.length', 3)
|
||||
.its('body')
|
||||
.should('have.length', 3)
|
||||
})
|
||||
|
||||
// another component test
|
||||
it('shows stubbed users', () => {
|
||||
cy.stub(window, 'fetch').resolves({
|
||||
json: cy
|
||||
.stub()
|
||||
.resolves([{ id: 101, name: 'Test User' }])
|
||||
.as('users'),
|
||||
.stub()
|
||||
.resolves([{ id: 101, name: 'Test User' }])
|
||||
.as('users'),
|
||||
})
|
||||
|
||||
// no mocking, just real request to the backend REST endpoint
|
||||
mount(<Users />)
|
||||
cy.get('li').should('have.length', 1)
|
||||
@@ -43,10 +44,10 @@ describe('Component and API tests', () => {
|
||||
cy.api({
|
||||
url: 'https://jsonplaceholder.cypress.io/users/1',
|
||||
})
|
||||
.its('body')
|
||||
.should('include', {
|
||||
id: 1,
|
||||
name: 'Leanne Graham',
|
||||
})
|
||||
.its('body')
|
||||
.should('include', {
|
||||
id: 1,
|
||||
name: 'Leanne Graham',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import React from 'react'
|
||||
|
||||
export class Users extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
users: [],
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount () {
|
||||
fetch('https://jsonplaceholder.cypress.io/users?_limit=3')
|
||||
.then(response => {
|
||||
return response.json()
|
||||
})
|
||||
.then(list => {
|
||||
this.setState({
|
||||
users: list,
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json()
|
||||
})
|
||||
.then((list) => {
|
||||
this.setState({
|
||||
users: list,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
{this.state.users.map(user => (
|
||||
{this.state.users.map((user) => (
|
||||
<li key={user.id}>
|
||||
<strong>{user.id}</strong> - {user.name}
|
||||
</li>
|
||||
|
||||
@@ -8,10 +8,10 @@ describe('Counter with access', () => {
|
||||
it('works', () => {
|
||||
mount(<Counter />)
|
||||
cy.contains('count: 0')
|
||||
.click()
|
||||
.contains('count: 1')
|
||||
.click()
|
||||
.contains('count: 2')
|
||||
.click()
|
||||
.contains('count: 1')
|
||||
.click()
|
||||
.contains('count: 2')
|
||||
})
|
||||
|
||||
it('allows access via reference', () => {
|
||||
@@ -19,15 +19,17 @@ describe('Counter with access', () => {
|
||||
|
||||
// the window.counter was set from the Counter's constructor
|
||||
cy.window()
|
||||
.should('have.property', 'counter')
|
||||
.its('state')
|
||||
.should('deep.equal', { count: 0 })
|
||||
.should('have.property', 'counter')
|
||||
.its('state')
|
||||
.should('deep.equal', { count: 0 })
|
||||
|
||||
// let's change the state of the component
|
||||
cy.window()
|
||||
.its('counter')
|
||||
.invoke('setState', {
|
||||
count: 101,
|
||||
})
|
||||
.its('counter')
|
||||
.invoke('setState', {
|
||||
count: 101,
|
||||
})
|
||||
|
||||
// the UI should update to reflect the new count
|
||||
cy.contains('count: 101').should('be.visible')
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
|
||||
export class Counter extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
count: 0,
|
||||
@@ -15,6 +15,7 @@ export class Counter extends React.Component {
|
||||
'set window.counter to this component in window',
|
||||
window.location.pathname,
|
||||
)
|
||||
|
||||
window.counter = this
|
||||
} else {
|
||||
console.log('running outside Cypress')
|
||||
@@ -27,7 +28,7 @@ export class Counter extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return <p onClick={this.click}>count: {this.state.count}</p>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ThemeContext } from './context'
|
||||
import { Toolbar } from './Toolbar.jsx'
|
||||
|
||||
export default class App extends React.Component {
|
||||
render() {
|
||||
render () {
|
||||
// Use a Provider to pass the current theme to the tree below.
|
||||
// Any component can read it, no matter how deep it is.
|
||||
// In this example, we're passing "dark" as the current value.
|
||||
|
||||
@@ -10,6 +10,7 @@ describe('Mocking context', () => {
|
||||
<Toolbar />
|
||||
</ThemeContext.Provider>,
|
||||
)
|
||||
|
||||
// the label "mocked" was passed through React context
|
||||
cy.contains('button', 'mocked').should('be.visible')
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ThemeContext } from './context'
|
||||
|
||||
// A component in the middle doesn't have to
|
||||
// pass the theme down explicitly anymore.
|
||||
export function Toolbar(props) {
|
||||
export function Toolbar (props) {
|
||||
return (
|
||||
<div>
|
||||
<ThemedButton />
|
||||
@@ -11,7 +11,7 @@ export function Toolbar(props) {
|
||||
)
|
||||
}
|
||||
|
||||
function Button(props) {
|
||||
function Button (props) {
|
||||
return <button>{props.theme}</button>
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class ThemedButton extends React.Component {
|
||||
// React will find the closest theme Provider above and use its value.
|
||||
// In this example, the current theme is "dark".
|
||||
static contextType = ThemeContext
|
||||
render() {
|
||||
render () {
|
||||
return <Button theme={this.context} />
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ Cypress.Commands.add('myMount', () => {
|
||||
|
||||
Cypress.Commands.add('myMount2', () => {
|
||||
const toMount = React.createElement('div', null, ['mount 2'])
|
||||
|
||||
return mount(toMount)
|
||||
})
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ import { mount } from 'cypress-react-unit-test'
|
||||
import Button from './forward-ref.jsx'
|
||||
|
||||
/* eslint-env mocha */
|
||||
describe('Button component', function() {
|
||||
it('works', function() {
|
||||
describe('Button component', function () {
|
||||
it('works', function () {
|
||||
mount(<Button>Hello, World</Button>)
|
||||
cy.contains('Hello, World')
|
||||
})
|
||||
|
||||
it('forwards refs as expected', function() {
|
||||
it('forwards refs as expected', function () {
|
||||
const ref = React.createRef()
|
||||
|
||||
mount(
|
||||
@@ -20,6 +20,7 @@ describe('Button component', function() {
|
||||
Hello, World
|
||||
</Button>,
|
||||
)
|
||||
|
||||
expect(ref).to.have.property('current')
|
||||
// expect(ref.current).not.be.null;
|
||||
})
|
||||
|
||||
@@ -6,23 +6,23 @@ describe('framer-motion', () => {
|
||||
it('Renders component and retries the animation', () => {
|
||||
mount(<Motion />)
|
||||
|
||||
cy.get("[data-testid='motion']").should('have.css', 'border-radius', '50%')
|
||||
cy.get("[data-testid='motion']").should('have.css', 'border-radius', '20%')
|
||||
cy.get('[data-testid=\'motion\']').should('have.css', 'border-radius', '50%')
|
||||
cy.get('[data-testid=\'motion\']').should('have.css', 'border-radius', '20%')
|
||||
})
|
||||
|
||||
// looks like cy.tick issue. Refer to the https://github.com/bahmutov/cypress-react-unit-test/issues/420
|
||||
// NOTE: looks like cy.tick issue. Refer to the https://github.com/bahmutov/cypress-react-unit-test/issues/420
|
||||
it.skip('Mocks setTimeout and requestAnimationFrame', () => {
|
||||
cy.clock()
|
||||
mount(<Motion />)
|
||||
|
||||
// CI is slow, so check only the approximate values
|
||||
cy.tick(800)
|
||||
cy.get("[data-testid='motion']").within(element => {
|
||||
cy.get('[data-testid=\'motion\']').within((element) => {
|
||||
expect(parseInt(element.css('borderRadius'))).to.equal(43)
|
||||
})
|
||||
|
||||
cy.tick(100)
|
||||
cy.get("[data-testid='motion']").within(element => {
|
||||
cy.get('[data-testid=\'motion\']').within((element) => {
|
||||
expect(parseInt(element.css('borderRadius'))).to.equal(48)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function CounterWithHooks({ initialCount = 0 }) {
|
||||
export default function CounterWithHooks ({ initialCount = 0 }) {
|
||||
const [count, setCount] = React.useState(initialCount)
|
||||
|
||||
const handleCountIncrement = React.useCallback(() => {
|
||||
|
||||
@@ -6,8 +6,8 @@ import ReactDom from 'react-dom'
|
||||
import { mount } from 'cypress-react-unit-test'
|
||||
import CounterWithHooks from './counter-with-hooks.jsx'
|
||||
|
||||
describe('CounterWithHooks component', function() {
|
||||
it('works', function() {
|
||||
describe('CounterWithHooks component', function () {
|
||||
it('works', function () {
|
||||
mount(<CounterWithHooks initialCount={3} />, { React, ReactDom })
|
||||
cy.contains('3')
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// example from https://reactjs.org/docs/hooks-overview.html
|
||||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
export default function Counter2WithHooks() {
|
||||
export default function Counter2WithHooks () {
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -5,16 +5,17 @@ import ReactDom from 'react-dom'
|
||||
import { mount } from 'cypress-react-unit-test'
|
||||
import Counter2WithHooks from './counter2-with-hooks.jsx'
|
||||
|
||||
describe('Counter2WithHooks', function() {
|
||||
it('changes document title', function() {
|
||||
describe('Counter2WithHooks', function () {
|
||||
it('changes document title', function () {
|
||||
mount(<Counter2WithHooks />, { React, ReactDom })
|
||||
cy.contains('0')
|
||||
cy.document().should('have.property', 'title', 'You clicked 0 times')
|
||||
|
||||
cy.log('Clicking changes document title')
|
||||
cy.get('#increment')
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
|
||||
cy.document().should('have.property', 'title', 'You clicked 2 times')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
// @ts-check
|
||||
/// <reference types="cypress" />
|
||||
import React, { useState, useCallback } from 'react'
|
||||
// @ts-ignore
|
||||
import { useState, useCallback } from 'react'
|
||||
import { mountHook } from 'cypress-react-unit-test'
|
||||
|
||||
// testing example hook function from
|
||||
// https://dev.to/jooforja/12-recipes-for-testing-react-applications-using-testing-library-1bh2#hooks
|
||||
function useCounter() {
|
||||
function useCounter () {
|
||||
const [count, setCount] = useState(0)
|
||||
const increment = useCallback(() => setCount(x => x + 1), [])
|
||||
const increment = useCallback(() => setCount((x) => x + 1), [])
|
||||
|
||||
return { count, increment }
|
||||
}
|
||||
|
||||
describe('useCounter hook', function() {
|
||||
it('increments the count', function() {
|
||||
mountHook(() => useCounter()).then(result => {
|
||||
describe('useCounter hook', function () {
|
||||
it('increments the count', function () {
|
||||
mountHook(() => useCounter()).then((result) => {
|
||||
expect(result.current.count).to.equal(0)
|
||||
result.current.increment()
|
||||
expect(result.current.count).to.equal(1)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { I18nextProvider } from 'react-i18next'
|
||||
import i18n from './i18n'
|
||||
import { LocalizedComponent } from './LocalizedComponent'
|
||||
|
||||
export function App() {
|
||||
export function App () {
|
||||
return (
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<LocalizedComponent count={15} name="SomeUserName" />
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import * as React from 'react'
|
||||
import { useTranslation, Trans } from 'react-i18next'
|
||||
import { Trans } from 'react-i18next'
|
||||
|
||||
interface LocalizedComponentProps {
|
||||
name: string
|
||||
count: number
|
||||
}
|
||||
|
||||
export function LocalizedComponent({ name, count }: LocalizedComponentProps) {
|
||||
// See ./App.tsx for localization setup
|
||||
const { t } = useTranslation()
|
||||
|
||||
// See ./App.tsx for localization setup
|
||||
export function LocalizedComponent ({ name, count }: LocalizedComponentProps) {
|
||||
return (
|
||||
<Trans
|
||||
i18nKey={count === 1 ? 'userMessagesUnread' : 'userMessagesUnread_plural'}
|
||||
|
||||
@@ -2,32 +2,32 @@ import i18n from 'i18next'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
|
||||
i18n
|
||||
.use(initReactI18next) // passes i18n down to react-i18next
|
||||
.init({
|
||||
resources: {
|
||||
en: {
|
||||
translation: {
|
||||
userMessagesUnread:
|
||||
.use(initReactI18next) // passes i18n down to react-i18next
|
||||
.init({
|
||||
resources: {
|
||||
en: {
|
||||
translation: {
|
||||
userMessagesUnread:
|
||||
'Hello <1>{{name}}</1>, you have {{count}} unread message.',
|
||||
userMessagesUnread_plural:
|
||||
userMessagesUnread_plural:
|
||||
'Hello <1>{{name}}</1>, you have {{count}} unread messages.',
|
||||
},
|
||||
},
|
||||
ru: {
|
||||
translation: {
|
||||
userMessagesUnread:
|
||||
},
|
||||
ru: {
|
||||
translation: {
|
||||
userMessagesUnread:
|
||||
'Привет, <1>{{name}}</1>, y тебя {{count}} непрочитанное сообщение.',
|
||||
userMessagesUnread_plural:
|
||||
userMessagesUnread_plural:
|
||||
'Привет, <1>{{name}}</1>, y тебя {{count}} непрочитанных сообщений.',
|
||||
},
|
||||
},
|
||||
},
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
},
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
})
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
})
|
||||
|
||||
export default i18n
|
||||
|
||||
@@ -4,7 +4,7 @@ import SamoyedImage from './samoyed.jpg'
|
||||
|
||||
interface DogProps {}
|
||||
|
||||
export const Dog: React.FC<DogProps> = ({}) => {
|
||||
export const Dog: React.FC<DogProps> = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1> Your dog is Samoyed: </h1>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import * as React from 'react'
|
||||
|
||||
const LazyDog = React.lazy(() => import(/* webpackChunkName: "Dog" */ './Dog'))
|
||||
|
||||
interface LazyComponentProps {}
|
||||
|
||||
export const LazyComponent: React.FC<LazyComponentProps> = ({}) => {
|
||||
export const LazyComponent: React.FC<LazyComponentProps> = () => {
|
||||
return (
|
||||
<div>
|
||||
Loading a dog:
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as React from 'react'
|
||||
import { LazyComponent } from './LazyComponent'
|
||||
import { mount } from 'cypress-react-unit-test'
|
||||
|
||||
// NOTE: It doesn't work because of chunk splitting issue with webpack
|
||||
describe.skip('React.lazy component with <Suspense />', () => {
|
||||
it('renders and retries till component is loaded', () => {
|
||||
mount(<LazyComponent />)
|
||||
|
||||
@@ -2,8 +2,10 @@ import React from 'react'
|
||||
|
||||
const OtherComponent = React.lazy(() => import('./OtherComponent'))
|
||||
|
||||
export default App = () => (
|
||||
<div className="app">
|
||||
<OtherComponent />
|
||||
</div>
|
||||
)
|
||||
export default function App () {
|
||||
return (
|
||||
<div className="app">
|
||||
<OtherComponent />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
import { mount } from 'cypress-react-unit-test'
|
||||
// import App from './App'
|
||||
import App from './App'
|
||||
|
||||
// https://github.com/bahmutov/cypress-react-unit-test/issues/136
|
||||
// NOTE: https://github.com/bahmutov/cypress-react-unit-test/issues/136
|
||||
describe.skip('App loads', () => {
|
||||
it('renders lazy component', () => {
|
||||
mount(<App />)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// https://github.com/bahmutov/cypress-react-unit-test/issues/136
|
||||
// NOTE: https://github.com/bahmutov/cypress-react-unit-test/issues/136
|
||||
// dynamic imports like this work in example projects, but not inside this repo
|
||||
// probably due to webpack plugins not set up correctly ☹️
|
||||
describe.skip('dynamic import', () => {
|
||||
|
||||
@@ -9,16 +9,18 @@ import TextField from '@material-ui/core/TextField'
|
||||
import Autocomplete from '@material-ui/lab/Autocomplete'
|
||||
import { top100Films } from './top-100-movies'
|
||||
|
||||
export default function ComboBox() {
|
||||
export default function ComboBox () {
|
||||
return (
|
||||
<Autocomplete
|
||||
id="combo-box-demo"
|
||||
options={top100Films}
|
||||
getOptionLabel={option => option.title}
|
||||
getOptionLabel={(option) => option.title}
|
||||
style={{ width: 300 }}
|
||||
renderInput={params => (
|
||||
<TextField {...params} label="Combo box" variant="outlined" fullWidth />
|
||||
)}
|
||||
renderInput={(params) => {
|
||||
return (
|
||||
<TextField {...params} label="Combo box" variant="outlined" fullWidth />
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -29,11 +31,13 @@ it('finds my favorite movie', () => {
|
||||
<Autocomplete
|
||||
id="combo-box-demo"
|
||||
options={top100Films}
|
||||
getOptionLabel={option => option.title}
|
||||
getOptionLabel={(option) => option.title}
|
||||
style={{ width: 300 }}
|
||||
renderInput={params => (
|
||||
<TextField {...params} label="Combo box" variant="outlined" fullWidth />
|
||||
)}
|
||||
renderInput={(params) => {
|
||||
return (
|
||||
<TextField {...params} label="Combo box" variant="outlined" fullWidth />
|
||||
)
|
||||
}}
|
||||
/>,
|
||||
{
|
||||
stylesheets: [
|
||||
@@ -46,8 +50,9 @@ it('finds my favorite movie', () => {
|
||||
cy.get('#combo-box-demo').click()
|
||||
cy.focused().type('god')
|
||||
cy.contains('The Godfather')
|
||||
.should('be.visible')
|
||||
.and('have.class', 'MuiAutocomplete-option')
|
||||
.click()
|
||||
.should('be.visible')
|
||||
.and('have.class', 'MuiAutocomplete-option')
|
||||
.click()
|
||||
|
||||
cy.get('#combo-box-demo').should('have.value', 'The Godfather')
|
||||
})
|
||||
|
||||
@@ -17,9 +17,9 @@ const GreenCheckbox = withStyles({
|
||||
},
|
||||
},
|
||||
checked: {},
|
||||
})(props => <Checkbox color="default" {...props} />)
|
||||
})((props) => <Checkbox color="default" {...props} />)
|
||||
|
||||
export default function CheckboxLabels() {
|
||||
export default function CheckboxLabels () {
|
||||
const [state, setState] = React.useState({
|
||||
checkedA: true,
|
||||
checkedB: true,
|
||||
@@ -27,8 +27,10 @@ export default function CheckboxLabels() {
|
||||
checkedG: true,
|
||||
})
|
||||
|
||||
const handleChange = name => event => {
|
||||
setState({ ...state, [name]: event.target.checked })
|
||||
const handleChange = (name) => {
|
||||
return (event) => {
|
||||
setState({ ...state, [name]: event.target.checked })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -8,19 +8,21 @@ import Divider from '@material-ui/core/Divider'
|
||||
import InboxIcon from '@material-ui/icons/Inbox'
|
||||
import DraftsIcon from '@material-ui/icons/Drafts'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
width: '100%',
|
||||
maxWidth: 360,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
},
|
||||
}))
|
||||
const useStyles = makeStyles((theme) => {
|
||||
return {
|
||||
root: {
|
||||
width: '100%',
|
||||
maxWidth: 360,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
function ListItemLink(props) {
|
||||
function ListItemLink (props) {
|
||||
return <ListItem button component="a" {...props} />
|
||||
}
|
||||
|
||||
export default function SimpleList() {
|
||||
export default function SimpleList () {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
|
||||
@@ -11,6 +11,7 @@ it('renders a list item', () => {
|
||||
<ListItemText primary={'my example list item'} />
|
||||
</ListItem>,
|
||||
)
|
||||
|
||||
cy.contains('my example list item')
|
||||
})
|
||||
|
||||
@@ -19,9 +20,9 @@ it('renders full list', () => {
|
||||
cy.viewport(500, 800)
|
||||
mount(<SimpleList />)
|
||||
cy.contains('Drafts')
|
||||
.click()
|
||||
.wait(1000)
|
||||
.click()
|
||||
.wait(1000)
|
||||
.click()
|
||||
.click()
|
||||
.wait(1000)
|
||||
.click()
|
||||
.wait(1000)
|
||||
.click()
|
||||
})
|
||||
|
||||
@@ -9,27 +9,30 @@ import FormHelperText from '@material-ui/core/FormHelperText'
|
||||
import FormControl from '@material-ui/core/FormControl'
|
||||
import Select from '@material-ui/core/Select'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
formControl: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: 120,
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
}))
|
||||
const useStyles = makeStyles((theme) => {
|
||||
return {
|
||||
formControl: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: 120,
|
||||
},
|
||||
selectEmpty: {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
export default function SimpleSelect() {
|
||||
export default function SimpleSelect () {
|
||||
const classes = useStyles()
|
||||
const [age, setAge] = React.useState('')
|
||||
|
||||
const inputLabel = React.useRef(null)
|
||||
const [labelWidth, setLabelWidth] = React.useState(0)
|
||||
|
||||
React.useEffect(() => {
|
||||
setLabelWidth(inputLabel.current.offsetWidth)
|
||||
}, [])
|
||||
|
||||
const handleChange = event => {
|
||||
const handleChange = (event) => {
|
||||
setAge(event.target.value)
|
||||
}
|
||||
|
||||
@@ -126,7 +129,7 @@ export default function SimpleSelect() {
|
||||
id="demo-simple-select-error"
|
||||
value={age}
|
||||
onChange={handleChange}
|
||||
renderValue={value => `⚠️ - ${value}`}
|
||||
renderValue={(value) => `⚠️ - ${value}`}
|
||||
>
|
||||
<MenuItem value="">
|
||||
<em>None</em>
|
||||
@@ -255,10 +258,12 @@ it('renders selects', () => {
|
||||
'https://fonts.googleapis.com/icon?family=Material+Icons',
|
||||
],
|
||||
})
|
||||
|
||||
cy.get('#demo-simple-select').click()
|
||||
cy.contains('[role=option]', 'Twenty')
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.should('be.visible')
|
||||
.click()
|
||||
|
||||
// check that other select has changed
|
||||
cy.contains('#demo-simple-select-outlined', 'Twenty').should('be.visible')
|
||||
})
|
||||
|
||||
@@ -7,9 +7,8 @@ import React from 'react'
|
||||
import Rating from '@material-ui/lab/Rating'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import Box from '@material-ui/core/Box'
|
||||
import { ExpansionPanelActions } from '@material-ui/core'
|
||||
|
||||
export default function SimpleRating({ onSetRating }) {
|
||||
export default function SimpleRating ({ onSetRating }) {
|
||||
const [value, setValue] = React.useState(2)
|
||||
|
||||
return (
|
||||
@@ -47,15 +46,17 @@ export default function SimpleRating({ onSetRating }) {
|
||||
it('renders simple rating', () => {
|
||||
cy.viewport(300, 400)
|
||||
const onSetRating = cy.stub()
|
||||
|
||||
mount(<SimpleRating onSetRating={onSetRating} />, {
|
||||
stylesheets: [
|
||||
'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap',
|
||||
'https://fonts.googleapis.com/icon?family=Material+Icons',
|
||||
],
|
||||
})
|
||||
|
||||
cy.get('label[for=simple-controlled-4]')
|
||||
.click()
|
||||
.then(() => {
|
||||
expect(onSetRating).to.have.been.calledWith(4)
|
||||
})
|
||||
.click()
|
||||
.then(() => {
|
||||
expect(onSetRating).to.have.been.calledWith(4)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ export const top100Films = [
|
||||
{ title: 'The Godfather: Part II', year: 1974 },
|
||||
{ title: 'The Dark Knight', year: 2008 },
|
||||
{ title: '12 Angry Men', year: 1957 },
|
||||
{ title: "Schindler's List", year: 1993 },
|
||||
{ title: 'Schindler\'s List', year: 1993 },
|
||||
{ title: 'Pulp Fiction', year: 1994 },
|
||||
{ title: 'The Lord of the Rings: The Return of the King', year: 2003 },
|
||||
{ title: 'The Good, the Bad and the Ugly', year: 1966 },
|
||||
@@ -15,7 +15,7 @@ export const top100Films = [
|
||||
{ title: 'Forrest Gump', year: 1994 },
|
||||
{ title: 'Inception', year: 2010 },
|
||||
{ title: 'The Lord of the Rings: The Two Towers', year: 2002 },
|
||||
{ title: "One Flew Over the Cuckoo's Nest", year: 1975 },
|
||||
{ title: 'One Flew Over the Cuckoo\'s Nest', year: 1975 },
|
||||
{ title: 'Goodfellas', year: 1990 },
|
||||
{ title: 'The Matrix', year: 1999 },
|
||||
{ title: 'Seven Samurai', year: 1954 },
|
||||
@@ -23,7 +23,7 @@ export const top100Films = [
|
||||
{ title: 'City of God', year: 2002 },
|
||||
{ title: 'Se7en', year: 1995 },
|
||||
{ title: 'The Silence of the Lambs', year: 1991 },
|
||||
{ title: "It's a Wonderful Life", year: 1946 },
|
||||
{ title: 'It\'s a Wonderful Life', year: 1946 },
|
||||
{ title: 'Life Is Beautiful', year: 1997 },
|
||||
{ title: 'The Usual Suspects', year: 1995 },
|
||||
{ title: 'Léon: The Professional', year: 1994 },
|
||||
@@ -96,7 +96,7 @@ export const top100Films = [
|
||||
{ title: 'Dangal', year: 2016 },
|
||||
{ title: 'The Sting', year: 1973 },
|
||||
{ title: '2001: A Space Odyssey', year: 1968 },
|
||||
{ title: "Singin' in the Rain", year: 1952 },
|
||||
{ title: 'Singin\' in the Rain', year: 1952 },
|
||||
{ title: 'Toy Story', year: 1995 },
|
||||
{ title: 'Bicycle Thieves', year: 1948 },
|
||||
{ title: 'The Kid', year: 1921 },
|
||||
|
||||
@@ -3,11 +3,11 @@ import { makeAutoObservable } from 'mobx'
|
||||
export class Timer {
|
||||
secondsPassed = 0
|
||||
|
||||
constructor() {
|
||||
constructor () {
|
||||
makeAutoObservable(this)
|
||||
}
|
||||
|
||||
increaseTimer() {
|
||||
increaseTimer () {
|
||||
this.secondsPassed += 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,13 @@ describe('MobX v6', { viewportWidth: 200, viewportHeight: 100 }, () => {
|
||||
context('TimerView', () => {
|
||||
it('increments every second', () => {
|
||||
const myTimer = new Timer()
|
||||
|
||||
mount(<TimerView timer={myTimer} />)
|
||||
cy.contains('Seconds passed: 0').then(() => {
|
||||
// we can increment the timer from outside
|
||||
myTimer.increaseTimer()
|
||||
})
|
||||
|
||||
cy.contains('Seconds passed: 1')
|
||||
|
||||
// by wrapping the timer and giving it an alias
|
||||
@@ -30,8 +32,8 @@ describe('MobX v6', { viewportWidth: 200, viewportHeight: 100 }, () => {
|
||||
// we can also ask the timer for the current value
|
||||
cy.get('@timer').invoke('increaseTimer')
|
||||
cy.get('@timer')
|
||||
.its('secondsPassed')
|
||||
.should('equal', 5)
|
||||
.its('secondsPassed')
|
||||
.should('equal', 5)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,14 +10,14 @@ it('renders user data', () => {
|
||||
}
|
||||
|
||||
// window.fetch
|
||||
cy.window().then(win => {
|
||||
cy.window().then((win) => {
|
||||
// Cypress cleans up stubs automatically after each test
|
||||
// https://on.cypress.io/stub
|
||||
cy.stub(win, 'fetch')
|
||||
.withArgs('/123')
|
||||
.resolves({
|
||||
json: () => Promise.resolve(fakeUser),
|
||||
})
|
||||
.withArgs('/123')
|
||||
.resolves({
|
||||
json: () => Promise.resolve(fakeUser),
|
||||
})
|
||||
})
|
||||
|
||||
mount(<User id="123" />)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// example from https://reactjs.org/docs/testing-recipes.html#data-fetching
|
||||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
export default function User(props) {
|
||||
export default function User (props) {
|
||||
const [user, setUser] = useState(null)
|
||||
|
||||
function fetchUserData(id) {
|
||||
fetch('/' + id)
|
||||
.then(response => response.json())
|
||||
.then(setUser)
|
||||
function fetchUserData (id) {
|
||||
fetch(`/${id}`)
|
||||
.then((response) => response.json())
|
||||
.then(setUser)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -4,28 +4,28 @@ import React from 'react'
|
||||
import axios from 'axios'
|
||||
|
||||
export class Users extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
users: [],
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount () {
|
||||
axios
|
||||
.get('https://jsonplaceholder.cypress.io/users?_limit=3')
|
||||
.then(response => {
|
||||
// JSON responses are automatically parsed.
|
||||
this.setState({
|
||||
users: response.data,
|
||||
})
|
||||
.get('https://jsonplaceholder.cypress.io/users?_limit=3')
|
||||
.then((response) => {
|
||||
// JSON responses are automatically parsed.
|
||||
this.setState({
|
||||
users: response.data,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
{this.state.users.map(user => (
|
||||
{this.state.users.map((user) => (
|
||||
<li key={user.id}>
|
||||
<strong>{user.id}</strong> - {user.name}
|
||||
</li>
|
||||
|
||||
@@ -14,15 +14,15 @@ describe('Mocking Axios', () => {
|
||||
// https://github.com/bahmutov/cypress-react-unit-test/issues/338
|
||||
it('mocks axios.get', () => {
|
||||
cy.stub(Axios, 'get')
|
||||
.resolves({
|
||||
data: [
|
||||
{
|
||||
id: 101,
|
||||
name: 'Test User',
|
||||
},
|
||||
],
|
||||
})
|
||||
.as('get')
|
||||
.resolves({
|
||||
data: [
|
||||
{
|
||||
id: 101,
|
||||
name: 'Test User',
|
||||
},
|
||||
],
|
||||
})
|
||||
.as('get')
|
||||
|
||||
mount(<Users />)
|
||||
// only the test user should be shown
|
||||
|
||||
@@ -3,15 +3,15 @@ import React from 'react'
|
||||
import { get } from 'axios'
|
||||
|
||||
export class Users extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
users: [],
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
get('https://jsonplaceholder.cypress.io/users?_limit=3').then(response => {
|
||||
componentDidMount () {
|
||||
get('https://jsonplaceholder.cypress.io/users?_limit=3').then((response) => {
|
||||
// JSON responses are automatically parsed.
|
||||
this.setState({
|
||||
users: response.data,
|
||||
@@ -19,10 +19,10 @@ export class Users extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
{this.state.users.map(user => (
|
||||
{this.state.users.map((user) => (
|
||||
<li key={user.id}>
|
||||
<strong>{user.id}</strong> - {user.name}
|
||||
</li>
|
||||
|
||||
@@ -14,15 +14,15 @@ describe('Mocking Axios named import get', () => {
|
||||
it('mocks get', () => {
|
||||
console.log('Axios', Axios)
|
||||
cy.stub(Axios, 'get')
|
||||
.resolves({
|
||||
data: [
|
||||
{
|
||||
id: 101,
|
||||
name: 'Test User',
|
||||
},
|
||||
],
|
||||
})
|
||||
.as('get')
|
||||
.resolves({
|
||||
data: [
|
||||
{
|
||||
id: 101,
|
||||
name: 'Test User',
|
||||
},
|
||||
],
|
||||
})
|
||||
.as('get')
|
||||
|
||||
mount(<Users />)
|
||||
// only the test user should be shown
|
||||
|
||||
@@ -3,15 +3,15 @@ import React from 'react'
|
||||
import { get } from './axios-api'
|
||||
|
||||
export class Users extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
users: [],
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
get('https://jsonplaceholder.cypress.io/users?_limit=3').then(response => {
|
||||
componentDidMount () {
|
||||
get('https://jsonplaceholder.cypress.io/users?_limit=3').then((response) => {
|
||||
// JSON responses are automatically parsed.
|
||||
this.setState({
|
||||
users: response.data,
|
||||
@@ -19,10 +19,10 @@ export class Users extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
{this.state.users.map(user => (
|
||||
{this.state.users.map((user) => (
|
||||
<li key={user.id}>
|
||||
<strong>{user.id}</strong> - {user.name}
|
||||
</li>
|
||||
|
||||
@@ -13,15 +13,15 @@ describe('Mocking wrapped Axios', () => {
|
||||
it('mocks get', () => {
|
||||
console.log('Axios', Axios)
|
||||
cy.stub(Axios, 'get')
|
||||
.resolves({
|
||||
data: [
|
||||
{
|
||||
id: 101,
|
||||
name: 'Test User',
|
||||
},
|
||||
],
|
||||
})
|
||||
.as('get')
|
||||
.resolves({
|
||||
data: [
|
||||
{
|
||||
id: 101,
|
||||
name: 'Test User',
|
||||
},
|
||||
],
|
||||
})
|
||||
.as('get')
|
||||
|
||||
mount(<Users />)
|
||||
// only the test user should be shown
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
import React from 'react'
|
||||
import Map from './map'
|
||||
|
||||
export default function Contact(props) {
|
||||
export default function Contact (props) {
|
||||
return (
|
||||
<div>
|
||||
<address>
|
||||
Contact {props.name} via{' '}
|
||||
<a data-testid="email" href={'mailto:' + props.email}>
|
||||
<a data-testid="email" href={`mailto:${props.email}`}>
|
||||
email
|
||||
</a>{' '}
|
||||
or on their{' '}
|
||||
|
||||
@@ -3,10 +3,10 @@ import React from 'react'
|
||||
import { GoogleMap, withGoogleMap, withScriptjs } from 'react-google-maps'
|
||||
|
||||
const GMap = withScriptjs(
|
||||
withGoogleMap(props => <GoogleMap id="example-map" center={props.center} />),
|
||||
withGoogleMap((props) => <GoogleMap id="example-map" center={props.center} />),
|
||||
)
|
||||
|
||||
export default function Map(props) {
|
||||
export default function Map (props) {
|
||||
return (
|
||||
<GMap
|
||||
googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyC4R6AN7SmujjPUIGKdyao2Kqitzr1kiRg&v=3.exp&libraries=geometry,drawing,places"
|
||||
|
||||
@@ -2,29 +2,31 @@ import React from 'react'
|
||||
import { mount } from 'cypress-react-unit-test'
|
||||
// Component "Contact" has child component "Map" that is expensive to render
|
||||
import Contact from './contact'
|
||||
import Map from './map'
|
||||
import * as MapModule from './map'
|
||||
|
||||
describe('Mock imported component', () => {
|
||||
// mock Map component used by Contact component
|
||||
// whenever React tries to instantiate using Map constructor
|
||||
// call DummyMap constructor
|
||||
const DummyMap = props => (
|
||||
<div data-testid="map">
|
||||
const DummyMap = (props) => {
|
||||
return (
|
||||
<div data-testid="map">
|
||||
DummyMap {props.center.lat}:{props.center.long}
|
||||
</div>
|
||||
)
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
context('by stubbing React.createElement method', () => {
|
||||
// stubbing like this works, but is less than ideal
|
||||
it('should render contact information', () => {
|
||||
cy.stub(React, 'createElement')
|
||||
.callThrough()
|
||||
.withArgs(Map)
|
||||
.callsFake((constructor, props) => React.createElement(DummyMap, props))
|
||||
.callThrough()
|
||||
.withArgs(Map)
|
||||
.callsFake((constructor, props) => React.createElement(DummyMap, props))
|
||||
|
||||
cy.viewport(500, 500)
|
||||
const center = { lat: 0, long: 0 }
|
||||
|
||||
mount(
|
||||
<Contact
|
||||
name="Joni Baez"
|
||||
@@ -47,6 +49,7 @@ describe('Mock imported component', () => {
|
||||
|
||||
cy.viewport(500, 500)
|
||||
const center = { lat: 0, long: 0 }
|
||||
|
||||
mount(
|
||||
<Contact
|
||||
name="Joni Baez"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react'
|
||||
import { fetchIngredients as defaultFetchIngredients } from './services'
|
||||
|
||||
export default function PizzaProps({ fetchIngredients }) {
|
||||
export default function PizzaProps ({ fetchIngredients }) {
|
||||
const [ingredients, setIngredients] = React.useState([])
|
||||
|
||||
const handleCook = () => {
|
||||
fetchIngredients().then(response => {
|
||||
fetchIngredients().then((response) => {
|
||||
setIngredients(response.args.ingredients)
|
||||
})
|
||||
}
|
||||
@@ -16,9 +16,11 @@ export default function PizzaProps({ fetchIngredients }) {
|
||||
<button onClick={handleCook}>Cook</button>
|
||||
{ingredients.length > 0 && (
|
||||
<ul>
|
||||
{ingredients.map(ingredient => (
|
||||
<li key={ingredient}>{ingredient}</li>
|
||||
))}
|
||||
{ingredients.map((ingredient) => {
|
||||
return (
|
||||
<li key={ingredient}>{ingredient}</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -7,8 +7,9 @@ const ingredients = ['bacon', 'tomato', 'mozzarella', 'pineapples']
|
||||
describe('PizzaProps', () => {
|
||||
it('mocks method in the default props', () => {
|
||||
cy.stub(PizzaProps.defaultProps, 'fetchIngredients')
|
||||
.resolves({ args: { ingredients } })
|
||||
.as('fetchMock')
|
||||
.resolves({ args: { ingredients } })
|
||||
.as('fetchMock')
|
||||
|
||||
mount(<PizzaProps />)
|
||||
cy.contains('button', /cook/i).click()
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react'
|
||||
import { fetchIngredients as defaultFetchIngredients } from './services'
|
||||
|
||||
export default function RemotePizza() {
|
||||
export default function RemotePizza () {
|
||||
const [ingredients, setIngredients] = React.useState([])
|
||||
|
||||
const handleCook = () => {
|
||||
defaultFetchIngredients().then(response => {
|
||||
defaultFetchIngredients().then((response) => {
|
||||
setIngredients(response.args.ingredients)
|
||||
})
|
||||
}
|
||||
@@ -16,9 +16,11 @@ export default function RemotePizza() {
|
||||
<button onClick={handleCook}>Cook</button>
|
||||
{ingredients.length > 0 && (
|
||||
<ul>
|
||||
{ingredients.map(ingredient => (
|
||||
<li key={ingredient}>{ingredient}</li>
|
||||
))}
|
||||
{ingredients.map((ingredient) => {
|
||||
return (
|
||||
<li key={ingredient}>{ingredient}</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -9,8 +9,9 @@ const ingredients = ['bacon', 'tomato', 'mozzarella', 'pineapples']
|
||||
describe('RemotePizza', () => {
|
||||
it('mocks named import from services', () => {
|
||||
cy.stub(services, 'fetchIngredients')
|
||||
.resolves({ args: { ingredients } })
|
||||
.as('fetchMock')
|
||||
.resolves({ args: { ingredients } })
|
||||
.as('fetchMock')
|
||||
|
||||
mount(<RemotePizza />)
|
||||
cy.contains('button', /cook/i).click()
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const fetchIngredients = () =>
|
||||
fetch(
|
||||
export const fetchIngredients = () => {
|
||||
return fetch(
|
||||
'https://httpbin.org/anything?ingredients=bacon&ingredients=mozzarella&ingredients=pineapples',
|
||||
).then(r => r.json())
|
||||
).then((r) => r.json())
|
||||
}
|
||||
|
||||
@@ -10,10 +10,12 @@ describe('reactive-state Counter', () => {
|
||||
<Counter />
|
||||
</div>,
|
||||
)
|
||||
|
||||
cy.contains('.count', '0')
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
|
||||
cy.contains('.count', '3')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ const Counters = () => {
|
||||
sum: 0,
|
||||
})
|
||||
|
||||
const increment = i => {
|
||||
const increment = (i) => {
|
||||
state.counts[i]++
|
||||
state.sum++
|
||||
}
|
||||
|
||||
@@ -10,10 +10,12 @@ describe('reactive-state Counters', () => {
|
||||
<Counters />
|
||||
</div>,
|
||||
)
|
||||
|
||||
cy.contains('.count', '0')
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
|
||||
// increments the counter itself
|
||||
cy.contains('.count', '3')
|
||||
// increments the sum
|
||||
@@ -21,17 +23,20 @@ describe('reactive-state Counters', () => {
|
||||
|
||||
// add two more counters
|
||||
cy.contains('Add Counter')
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
|
||||
cy.get('.counts .count').should('have.length', 3)
|
||||
// clicking the new counters increments the sum
|
||||
cy.get('.count')
|
||||
.eq(1)
|
||||
.click()
|
||||
.eq(1)
|
||||
.click()
|
||||
|
||||
cy.contains('.sum', '4')
|
||||
cy.get('.count')
|
||||
.eq(2)
|
||||
.click()
|
||||
.eq(2)
|
||||
.click()
|
||||
|
||||
cy.contains('.sum', '5')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,11 +7,13 @@ const AddTodo = ({ onAdd }) => {
|
||||
})
|
||||
|
||||
// handle events
|
||||
const handleEnter = e => e.key === 'Enter' && handleAdd()
|
||||
const handleEnter = (e) => e.key === 'Enter' && handleAdd()
|
||||
|
||||
const handleAdd = () => {
|
||||
if (!state.input) return // ignore empty input
|
||||
|
||||
const todo = { title: state.input, completed: false } // make todo
|
||||
|
||||
onAdd(todo) // add it
|
||||
state.input = '' // clear input
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ const Todos = () => {
|
||||
todos: [],
|
||||
})
|
||||
|
||||
const removeTodo = i => state.todos.splice(i, 1)
|
||||
const addTodo = todo => state.todos.push(todo)
|
||||
const removeTodo = (i) => state.todos.splice(i, 1)
|
||||
const addTodo = (todo) => state.todos.push(todo)
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
@@ -18,9 +18,11 @@ const Todos = () => {
|
||||
{!state.todos.length && <div className="no-todos"> No Todos added </div>}
|
||||
|
||||
<div className="todos">
|
||||
{state.todos.map((todo, i) => (
|
||||
<Todo todo={todo} key={i} onRemove={() => removeTodo(i)} />
|
||||
))}
|
||||
{state.todos.map((todo, i) => {
|
||||
return (
|
||||
<Todo todo={todo} key={i} onRemove={() => removeTodo(i)} />
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -10,23 +10,26 @@ describe('reactive-state Todos', () => {
|
||||
<Todos />
|
||||
</div>,
|
||||
)
|
||||
|
||||
cy.get('.add-todo input').type('code{enter}')
|
||||
cy.get('.add-todo input').type('test')
|
||||
cy.get('.add-todo')
|
||||
.contains('Add')
|
||||
.click()
|
||||
.contains('Add')
|
||||
.click()
|
||||
|
||||
// now check things
|
||||
cy.get('.todos .todo').should('have.length', 2)
|
||||
// remove the first one
|
||||
cy.get('.todos .todo')
|
||||
.first()
|
||||
.should('contain', 'code')
|
||||
.find('.todo_remove')
|
||||
.click()
|
||||
.first()
|
||||
.should('contain', 'code')
|
||||
.find('.todo_remove')
|
||||
.click()
|
||||
|
||||
// single todo left
|
||||
cy.get('.todos .todo')
|
||||
.should('have.length', 1)
|
||||
.first()
|
||||
.should('contain', 'test')
|
||||
.should('have.length', 1)
|
||||
.first()
|
||||
.should('contain', 'test')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ import Todos from './Todos'
|
||||
import Select from './Select'
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
|
||||
@@ -6,10 +6,11 @@ class Note extends React.Component {
|
||||
saved: '',
|
||||
}
|
||||
|
||||
onChange = evt => {
|
||||
onChange = (evt) => {
|
||||
this.setState({
|
||||
content: evt.target.value,
|
||||
})
|
||||
|
||||
console.log('updating content')
|
||||
}
|
||||
|
||||
@@ -20,7 +21,8 @@ class Note extends React.Component {
|
||||
}
|
||||
|
||||
load = () => {
|
||||
var me = this
|
||||
let me = this
|
||||
|
||||
setTimeout(() => {
|
||||
me.setState({
|
||||
data: [{ title: 'test' }, { title: 'test2' }],
|
||||
@@ -28,7 +30,7 @@ class Note extends React.Component {
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<label htmlFor="change">Change text</label>
|
||||
@@ -36,11 +38,13 @@ class Note extends React.Component {
|
||||
<div data-testid="saved">{this.state.saved}</div>
|
||||
{this.state.data && (
|
||||
<div data-testid="data">
|
||||
{this.state.data.map(item => (
|
||||
<div data-testid="item" className="item">
|
||||
{item.title}
|
||||
</div>
|
||||
))}
|
||||
{this.state.data.map((item) => {
|
||||
return (
|
||||
<div data-testid="item" className="item">
|
||||
{item.title}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
|
||||
@@ -17,9 +17,9 @@ describe('Note', () => {
|
||||
// there is a built-in delay in loading the data
|
||||
// but we don't worry about it - we just check if the text eventually appears
|
||||
cy.get('[data-testid=item]')
|
||||
.should('have.length', 2)
|
||||
.and('be.visible')
|
||||
.first()
|
||||
.should('have.text', 'test')
|
||||
.should('have.length', 2)
|
||||
.and('be.visible')
|
||||
.first()
|
||||
.should('have.text', 'test')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,37 +1,41 @@
|
||||
import React from 'react'
|
||||
import './Todos.css'
|
||||
|
||||
const Todos = ({ todos, select, selected }) => (
|
||||
<React.Fragment>
|
||||
{todos.map(todo => (
|
||||
<React.Fragment key={todo.title}>
|
||||
<h3
|
||||
data-testid="item"
|
||||
className={
|
||||
selected && selected.title === todo.title ? 'selected' : ''
|
||||
}
|
||||
>
|
||||
{todo.title}
|
||||
</h3>
|
||||
<div>{todo.description}</div>
|
||||
<button onClick={() => select(todo)}>Select</button>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</React.Fragment>
|
||||
)
|
||||
const Todos = ({ todos, select, selected }) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{todos.map((todo) => {
|
||||
return (
|
||||
<React.Fragment key={todo.title}>
|
||||
<h3
|
||||
data-testid="item"
|
||||
className={
|
||||
selected && selected.title === todo.title ? 'selected' : ''
|
||||
}
|
||||
>
|
||||
{todo.title}
|
||||
</h3>
|
||||
<div>{todo.description}</div>
|
||||
<button onClick={() => select(todo)}>Select</button>
|
||||
</React.Fragment>
|
||||
)
|
||||
})}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
class TodosContainer extends React.Component {
|
||||
state = {
|
||||
todo: void 0,
|
||||
}
|
||||
|
||||
select = todo => {
|
||||
select = (todo) => {
|
||||
this.setState({
|
||||
todo,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<Todos {...this.props} select={this.select} selected={this.state.todo} />
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@ it('Todo - should create snapshot', () => {
|
||||
]}
|
||||
/>,
|
||||
)
|
||||
|
||||
cy.get('[data-testid=item]').should('have.length', 2)
|
||||
// disabled snapshot commands for now
|
||||
// to speed up bundling
|
||||
@@ -23,25 +24,26 @@ it('Todo - should create snapshot', () => {
|
||||
|
||||
// entire test area
|
||||
cy.get('#cypress-root')
|
||||
.invoke('html')
|
||||
.then(pretty)
|
||||
.should(
|
||||
'equal',
|
||||
stripIndent`
|
||||
.invoke('html')
|
||||
.then(pretty)
|
||||
.should(
|
||||
'equal',
|
||||
stripIndent`
|
||||
<h3 data-testid="item" class="">item1</h3>
|
||||
<div>an item</div><button>Select</button>
|
||||
<h3 data-testid="item" class="">item2</h3>
|
||||
<div>another item</div><button>Select</button>
|
||||
`,
|
||||
)
|
||||
)
|
||||
|
||||
cy.contains('[data-testid=item]', 'item1').should('be.visible')
|
||||
// selecting works
|
||||
cy.contains('[data-testid=item]', 'item2')
|
||||
.next()
|
||||
.should('have.text', 'another item')
|
||||
.next()
|
||||
.should('have.text', 'Select')
|
||||
.click()
|
||||
.next()
|
||||
.should('have.text', 'another item')
|
||||
.next()
|
||||
.should('have.text', 'Select')
|
||||
.click()
|
||||
|
||||
cy.contains('[data-testid=item]', 'item2').should('have.class', 'selected')
|
||||
})
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
function add(a, b) {
|
||||
function add (a, b) {
|
||||
if (a > 0 && b > 0) {
|
||||
return a + b
|
||||
}
|
||||
|
||||
throw new Error('parameters must be larger than zero')
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import add from './add'
|
||||
describe('add', () => {
|
||||
it('testing addition', () => {
|
||||
const actual = add(1, 2)
|
||||
|
||||
expect(actual).to.equal(3)
|
||||
})
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import add from './add'
|
||||
describe('add', () => {
|
||||
it('testing addition2', () => {
|
||||
const actual = add(2, 2)
|
||||
|
||||
expect(actual).to.equal(4)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react'
|
||||
import { getProducts } from '../products'
|
||||
|
||||
class AProduct extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
myName: props.name,
|
||||
@@ -10,13 +10,13 @@ class AProduct extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
order() {
|
||||
order () {
|
||||
this.setState({
|
||||
orderCount: this.state.orderCount + 1,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<div className="product">
|
||||
<span className="name">{this.state.myName}</span>
|
||||
@@ -31,7 +31,7 @@ class AProduct extends React.Component {
|
||||
|
||||
const Products = ({ products }) => (
|
||||
<React.Fragment>
|
||||
{products.map(product => (
|
||||
{products.map((product) => (
|
||||
<AProduct key={product.id} name={product.name} />
|
||||
))}
|
||||
</React.Fragment>
|
||||
@@ -42,16 +42,16 @@ class ProductsContainer extends React.Component {
|
||||
products: [],
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount () {
|
||||
// for now use promises
|
||||
return getProducts().then(products => {
|
||||
return getProducts().then((products) => {
|
||||
this.setState({
|
||||
products,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<div className="product-container">
|
||||
<Products products={this.state.products} />
|
||||
|
||||
@@ -9,15 +9,16 @@ describe('Selecting by React props and state', () => {
|
||||
context('without delay', () => {
|
||||
beforeEach(() => {
|
||||
cy.stub(window, 'fetch')
|
||||
.withArgs('http://myapi.com/products')
|
||||
.resolves({
|
||||
json: cy.stub().resolves({
|
||||
products: [
|
||||
{ id: 1, name: 'First item' },
|
||||
{ id: 2, name: 'Second item' },
|
||||
],
|
||||
}),
|
||||
})
|
||||
.withArgs('http://myapi.com/products')
|
||||
.resolves({
|
||||
json: cy.stub().resolves({
|
||||
products: [
|
||||
{ id: 1, name: 'First item' },
|
||||
{ id: 2, name: 'Second item' },
|
||||
],
|
||||
}),
|
||||
})
|
||||
|
||||
mount(<ProductsList />)
|
||||
|
||||
// to find DOM elements by React component constructor name, props, or state
|
||||
@@ -35,32 +36,35 @@ describe('Selecting by React props and state', () => {
|
||||
// find the top level <ProductsContainer> that we have mounted
|
||||
// under imported name "ProductsList"
|
||||
cy.react('ProductsContainer')
|
||||
.first()
|
||||
.should('have.class', 'product-container')
|
||||
.first()
|
||||
.should('have.class', 'product-container')
|
||||
|
||||
// find all instances of <AProduct> component
|
||||
cy.react('AProduct').should('have.length', 2)
|
||||
// find a single instance with prop
|
||||
// <AProduct name={'Second item'} />
|
||||
cy.react('AProduct', { props: { name: 'Second item' } })
|
||||
.first()
|
||||
.find('.name')
|
||||
.and('have.text', 'Second item')
|
||||
.first()
|
||||
.find('.name')
|
||||
.and('have.text', 'Second item')
|
||||
})
|
||||
|
||||
it('find React components', () => {
|
||||
cy.log('**cy.getReact**')
|
||||
// returns React component wrapper with props
|
||||
cy.getReact('AProduct', { props: { name: 'Second item' } })
|
||||
.getProps()
|
||||
.should('deep.equal', { name: 'Second item' })
|
||||
.getProps()
|
||||
.should('deep.equal', { name: 'Second item' })
|
||||
|
||||
cy.getReact('AProduct', { props: { name: 'First item' } })
|
||||
// get single prop
|
||||
.getProps('name')
|
||||
.should('eq', 'First item')
|
||||
// get single prop
|
||||
.getProps('name')
|
||||
.should('eq', 'First item')
|
||||
|
||||
cy.log('**.getCurrentState**')
|
||||
cy.getReact('AProduct', { props: { name: 'Second item' } })
|
||||
.getCurrentState()
|
||||
.should('include', { myName: 'Second item' })
|
||||
.getCurrentState()
|
||||
.should('include', { myName: 'Second item' })
|
||||
|
||||
// find component using state
|
||||
cy.getReact('AProduct', { state: { myName: 'Second item' } }).should(
|
||||
@@ -71,47 +75,47 @@ describe('Selecting by React props and state', () => {
|
||||
it('chains getReact', () => {
|
||||
// note that by itself, the component is found
|
||||
cy.getReact('AProduct', { props: { name: 'First item' } })
|
||||
.getProps('name')
|
||||
.should('eq', 'First item')
|
||||
.getProps('name')
|
||||
.should('eq', 'First item')
|
||||
|
||||
// chaining getReact
|
||||
cy.getReact('ProductsContainer')
|
||||
.getReact('AProduct', { props: { name: 'First item' } })
|
||||
.getProps('name')
|
||||
.should('eq', 'First item')
|
||||
.getReact('AProduct', { props: { name: 'First item' } })
|
||||
.getProps('name')
|
||||
.should('eq', 'First item')
|
||||
})
|
||||
|
||||
it('finds components by props and state', () => {
|
||||
// by clicking on the Order button we change the
|
||||
// internal state of that component
|
||||
cy.contains('.product', 'First item')
|
||||
.find('button.order')
|
||||
.click()
|
||||
.wait(1000)
|
||||
.find('button.order')
|
||||
.click()
|
||||
.wait(1000)
|
||||
|
||||
// the component is there for sure, since the DOM has updated
|
||||
cy.contains('.product', '1')
|
||||
.find('.name')
|
||||
.should('have.text', 'First item')
|
||||
.find('.name')
|
||||
.should('have.text', 'First item')
|
||||
|
||||
// now find that component using the state value
|
||||
cy.react('AProduct', { state: { orderCount: 1 } })
|
||||
.find('.name')
|
||||
.should('have.text', 'First item')
|
||||
.find('.name')
|
||||
.should('have.text', 'First item')
|
||||
})
|
||||
|
||||
it('finds components by props and state (click twice)', () => {
|
||||
// by clicking on the Order button we change the
|
||||
// internal state of that component
|
||||
cy.contains('.product', 'First item')
|
||||
.find('button.order')
|
||||
.click()
|
||||
.click()
|
||||
.find('button.order')
|
||||
.click()
|
||||
.click()
|
||||
|
||||
// now find that component using the state value
|
||||
cy.react('AProduct', { state: { orderCount: 2 } })
|
||||
.find('.name')
|
||||
.should('have.text', 'First item')
|
||||
.find('.name')
|
||||
.should('have.text', 'First item')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -129,9 +133,10 @@ describe('Selecting by React props and state', () => {
|
||||
}
|
||||
|
||||
cy.stub(window, 'fetch')
|
||||
.withArgs('http://myapi.com/products')
|
||||
// simulate slow load by delaying the response
|
||||
.resolves(Cypress.Promise.resolve(response).delay(1000))
|
||||
.withArgs('http://myapi.com/products')
|
||||
// simulate slow load by delaying the response
|
||||
.resolves(Cypress.Promise.resolve(response).delay(1000))
|
||||
|
||||
mount(<ProductsList />)
|
||||
|
||||
// to find DOM elements by React component constructor name, props, or state
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
export const getProducts = () => {
|
||||
console.log('fetch products')
|
||||
|
||||
return fetch('http://myapi.com/products')
|
||||
.then(r => r.json())
|
||||
.then(json => {
|
||||
console.log('products', json.products)
|
||||
return json.products
|
||||
})
|
||||
.then((r) => r.json())
|
||||
.then((json) => {
|
||||
console.log('products', json.products)
|
||||
|
||||
return json.products
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@ import { getProducts } from './products'
|
||||
describe('products', () => {
|
||||
it('should return a list', () => {
|
||||
const fetchStub = cy
|
||||
.stub(window, 'fetch')
|
||||
.withArgs('http://myapi.com/products')
|
||||
.resolves({
|
||||
json: cy.stub().resolves({
|
||||
products: [{ id: 1, name: 'test' }],
|
||||
}),
|
||||
})
|
||||
.stub(window, 'fetch')
|
||||
.withArgs('http://myapi.com/products')
|
||||
.resolves({
|
||||
json: cy.stub().resolves({
|
||||
products: [{ id: 1, name: 'test' }],
|
||||
}),
|
||||
})
|
||||
|
||||
return getProducts().then(list => {
|
||||
return getProducts().then((list) => {
|
||||
expect(list).to.have.length(1)
|
||||
expect(fetchStub).to.have.been.calledOnce.and.have.been.calledWith(
|
||||
'http://myapi.com/products',
|
||||
|
||||
@@ -3,12 +3,12 @@ import { Dropdown } from 'react-bootstrap'
|
||||
import { mount } from 'cypress-react-unit-test'
|
||||
|
||||
class UserControls extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {}
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle>Top Toggle</Dropdown.Toggle>
|
||||
@@ -30,6 +30,7 @@ describe('react-bootstrap Dropdown', () => {
|
||||
mount(<UserControls />, {
|
||||
cssFile: 'node_modules/bootstrap/dist/css/bootstrap.min.css',
|
||||
})
|
||||
|
||||
cy.contains('Top Toggle').click()
|
||||
cy.contains('li', 'First').should('be.visible')
|
||||
cy.get('li').should('have.length', 3)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Button, Modal } from 'react-bootstrap'
|
||||
import { mount } from 'cypress-react-unit-test'
|
||||
|
||||
export class Example extends React.Component {
|
||||
constructor(props, context) {
|
||||
constructor (props, context) {
|
||||
super(props, context)
|
||||
|
||||
this.handleShow = this.handleShow.bind(this)
|
||||
@@ -14,15 +14,15 @@ export class Example extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleClose() {
|
||||
handleClose () {
|
||||
this.setState({ show: false })
|
||||
}
|
||||
|
||||
handleShow() {
|
||||
handleShow () {
|
||||
this.setState({ show: true })
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
This text is all that renders. And the modal is not rendered, regardless
|
||||
@@ -53,6 +53,7 @@ describe('react-bootstrap Modal', () => {
|
||||
mount(<Example />, {
|
||||
cssFile: 'node_modules/bootstrap/dist/css/bootstrap.min.css',
|
||||
})
|
||||
|
||||
// confirm modal is visible
|
||||
cy.contains('h4', 'Text in a modal').should('be.visible')
|
||||
cy.contains('button', 'Close').click()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import { Routes, Route, Link } from 'react-router-dom'
|
||||
|
||||
function Home() {
|
||||
function Home () {
|
||||
return (
|
||||
<div>
|
||||
<h2>Home</h2>
|
||||
@@ -10,7 +10,7 @@ function Home() {
|
||||
)
|
||||
}
|
||||
|
||||
function About() {
|
||||
function About () {
|
||||
return <h2>About</h2>
|
||||
}
|
||||
|
||||
|
||||
@@ -15,13 +15,14 @@ describe('React Router', () => {
|
||||
|
||||
cy.get('nav').should('be.visible')
|
||||
cy.contains('Home')
|
||||
.click()
|
||||
.location('pathname')
|
||||
.should('equal', '/') // Home route
|
||||
.click()
|
||||
.location('pathname')
|
||||
.should('equal', '/') // Home route
|
||||
|
||||
cy.contains('h2', 'Home')
|
||||
cy.contains('About')
|
||||
.click()
|
||||
.location('pathname')
|
||||
.should('equal', '/about') // About route
|
||||
.click()
|
||||
.location('pathname')
|
||||
.should('equal', '/about') // About route
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,44 +3,47 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default class MouseMovement extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
console.log('MouseMovement constructor')
|
||||
super(props)
|
||||
this.state = {
|
||||
timer: undefined,
|
||||
}
|
||||
|
||||
this.timeout = this.timeout.bind(this)
|
||||
this.onMouseMove = this.onMouseMove.bind(this)
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
componentWillMount () {
|
||||
console.log('MouseMovement componentWillMount')
|
||||
document.addEventListener('mousemove', this.onMouseMove)
|
||||
const timer = setTimeout(this.timeout, 4000)
|
||||
|
||||
this.setState({ timer })
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
componentWillUnmount () {
|
||||
console.log('MouseMovement componentWillUnmount')
|
||||
document.removeEventListener('mousemove', this.onMouseMove)
|
||||
clearTimeout(this.state.timer)
|
||||
this.setState({ timer: undefined })
|
||||
}
|
||||
|
||||
onMouseMove() {
|
||||
onMouseMove () {
|
||||
console.log('MouseMovement onMouseMove')
|
||||
clearTimeout(this.state.timer)
|
||||
const timer = setTimeout(this.timeout, 4000)
|
||||
|
||||
this.setState({ timer })
|
||||
this.props.onMoved(true)
|
||||
}
|
||||
|
||||
timeout() {
|
||||
timeout () {
|
||||
console.log('timeout')
|
||||
clearTimeout(this.state.timer)
|
||||
this.props.onMoved(false)
|
||||
}
|
||||
render() {
|
||||
render () {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,32 +8,35 @@ describe('Renderless component', () => {
|
||||
// let's also spy on "console.log" calls
|
||||
// to make sure the entire sequence of calls happens
|
||||
cy.window()
|
||||
.its('console')
|
||||
.then(console => {
|
||||
cy.spy(console, 'log').as('log')
|
||||
})
|
||||
.its('console')
|
||||
.then((console) => {
|
||||
cy.spy(console, 'log').as('log')
|
||||
})
|
||||
|
||||
const onMoved = cy.stub()
|
||||
|
||||
mount(<MouseMovement onMoved={onMoved} />)
|
||||
cy.get('#cypress-root').should('be.empty')
|
||||
cy.document()
|
||||
.trigger('mousemove')
|
||||
.then(() => {
|
||||
expect(onMoved).to.have.been.calledWith(true)
|
||||
})
|
||||
.trigger('mousemove')
|
||||
.then(() => {
|
||||
expect(onMoved).to.have.been.calledWith(true)
|
||||
})
|
||||
|
||||
unmount()
|
||||
|
||||
cy.get('@log')
|
||||
.its('callCount')
|
||||
.should('equal', 4)
|
||||
.its('callCount')
|
||||
.should('equal', 4)
|
||||
|
||||
cy.get('@log')
|
||||
.invoke('getCalls')
|
||||
.then(calls => calls.map(call => call.args[0]))
|
||||
.should('deep.equal', [
|
||||
'MouseMovement constructor',
|
||||
'MouseMovement componentWillMount',
|
||||
'MouseMovement onMouseMove',
|
||||
'MouseMovement componentWillUnmount',
|
||||
])
|
||||
.invoke('getCalls')
|
||||
.then((calls) => calls.map((call) => call.args[0]))
|
||||
.should('deep.equal', [
|
||||
'MouseMovement constructor',
|
||||
'MouseMovement componentWillMount',
|
||||
'MouseMovement onMouseMove',
|
||||
'MouseMovement componentWillUnmount',
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,15 +8,15 @@ class App extends Component {
|
||||
isLoading: true,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount () {
|
||||
this._timer = setTimeout(() => this.setState({ isLoading: false }), 2000)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
componentWillUnmount () {
|
||||
clearTimeout(this._timer)
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
|
||||
@@ -10,7 +10,7 @@ export default class LoadingIndicator extends Component {
|
||||
isPastDelay: false,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount () {
|
||||
console.log('component did mount')
|
||||
this._delayTimer = setTimeout(() => {
|
||||
console.log('2000ms passed')
|
||||
@@ -18,18 +18,20 @@ export default class LoadingIndicator extends Component {
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
componentWillUnmount () {
|
||||
console.log('componentWillUnmount')
|
||||
clearTimeout(this._delayTimer)
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
if (this.props.isLoading) {
|
||||
if (!this.state.isPastDelay) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <div>loading...</div>
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ describe('LoadingIndicator', () => {
|
||||
<div>ahoy!</div>
|
||||
</LoadingIndicator>,
|
||||
)
|
||||
|
||||
cy.contains('div', 'ahoy!')
|
||||
})
|
||||
})
|
||||
@@ -26,6 +27,7 @@ describe('LoadingIndicator', () => {
|
||||
<div>ahoy!</div>
|
||||
</LoadingIndicator>,
|
||||
)
|
||||
|
||||
cy.contains('loading...').should('be.visible')
|
||||
cy.contains('ahoy!').should('not.exist')
|
||||
})
|
||||
@@ -38,6 +40,7 @@ describe('LoadingIndicator', () => {
|
||||
<div>ahoy!</div>
|
||||
</LoadingIndicator>,
|
||||
)
|
||||
|
||||
// this test runs for 2 seconds, since it just waits for the
|
||||
// loading indicator to show up after "setTimeout"
|
||||
cy.contains('loading...', { timeout: 2100 }).should('be.visible')
|
||||
@@ -50,6 +53,7 @@ describe('LoadingIndicator', () => {
|
||||
<div>ahoy!</div>
|
||||
</LoadingIndicator>,
|
||||
)
|
||||
|
||||
// force 2 seconds to pass instantly
|
||||
// and component's setTimeout to fire
|
||||
cy.tick(2010)
|
||||
@@ -61,23 +65,25 @@ describe('LoadingIndicator', () => {
|
||||
describe('on unmount', () => {
|
||||
it('should clear timeout', () => {
|
||||
cy.clock()
|
||||
cy.window().then(win => cy.spy(win, 'clearTimeout').as('clearTimeout'))
|
||||
cy.window().then((win) => cy.spy(win, 'clearTimeout').as('clearTimeout'))
|
||||
|
||||
mount(
|
||||
<LoadingIndicator isLoading={true}>
|
||||
<div>ahoy!</div>
|
||||
</LoadingIndicator>,
|
||||
)
|
||||
|
||||
cy.tick(2010)
|
||||
cy.get('#cypress-root').then($el => {
|
||||
cy.get('#cypress-root').then(($el) => {
|
||||
unmountComponentAtNode($el[0])
|
||||
})
|
||||
|
||||
cy.get('@clearTimeout').should('have.been.calledOnce')
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cy.get('#cypress-root').then($el => {
|
||||
cy.get('#cypress-root').then(($el) => {
|
||||
unmountComponentAtNode($el[0])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import { mount } from 'cypress-react-unit-test'
|
||||
// https://github.com/cypress-io/cypress/pull/3968
|
||||
// you can skip the tests if there is no retries feature
|
||||
const describeOrSkip = Cypress.getTestRetries ? describe : describe.skip
|
||||
|
||||
describeOrSkip('Test', () => {
|
||||
const Hello = () => {
|
||||
// this is how you can get the current retry number
|
||||
@@ -15,6 +16,7 @@ describeOrSkip('Test', () => {
|
||||
const n = cy.state('test').currentRetry
|
||||
? cy.state('test').currentRetry()
|
||||
: 0
|
||||
|
||||
return <div>retry {n}</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import React, { useState } from 'react'
|
||||
import axios from 'axios'
|
||||
|
||||
export default function Fetcher({ url }) {
|
||||
export default function Fetcher ({ url }) {
|
||||
const [greeting, setGreeting] = useState('')
|
||||
const [buttonClicked, setButtonClicked] = useState(false)
|
||||
|
||||
const fetchGreeting = () => {
|
||||
axios.get(url).then(response => {
|
||||
axios.get(url).then((response) => {
|
||||
const data = response.data
|
||||
const { greeting } = data
|
||||
|
||||
setGreeting(greeting)
|
||||
setButtonClicked(true)
|
||||
})
|
||||
|
||||
@@ -7,12 +7,13 @@ it('loads and displays greeting', () => {
|
||||
cy.route('/greeting', { greeting: 'Hello there' }).as('greet')
|
||||
|
||||
const url = '/greeting'
|
||||
|
||||
mount(<Fetcher url={url} />)
|
||||
|
||||
cy.contains('Load Greeting').click()
|
||||
cy.get('[role=heading]').should('have.text', 'Hello there')
|
||||
cy.get('[role=button]').should('be.disabled')
|
||||
cy.get('@greet')
|
||||
.its('url')
|
||||
.should('match', /\/greeting$/)
|
||||
.its('url')
|
||||
.should('match', /\/greeting$/)
|
||||
})
|
||||
|
||||
@@ -10,14 +10,16 @@ it('loads and displays greeting (testing-lib)', () => {
|
||||
cy.route('/greeting', { greeting: 'Hello there' }).as('greet')
|
||||
|
||||
const url = '/greeting'
|
||||
|
||||
mount(<Fetcher url={url} />)
|
||||
|
||||
cy.findByText('Load Greeting')
|
||||
.wait(1000)
|
||||
.click()
|
||||
.wait(1000)
|
||||
.click()
|
||||
|
||||
cy.findByRole('heading').should('have.text', 'Hello there')
|
||||
cy.findByRole('button').should('be.disabled')
|
||||
cy.get('@greet')
|
||||
.its('url')
|
||||
.should('match', /\/greeting$/)
|
||||
.its('url')
|
||||
.should('match', /\/greeting$/)
|
||||
})
|
||||
|
||||
@@ -7,6 +7,7 @@ import { mount } from 'cypress-react-unit-test'
|
||||
// https://github.com/bahmutov/cypress-react-unit-test/issues/200
|
||||
it('should select null after timing out (fast)', () => {
|
||||
const onSelect = cy.stub()
|
||||
|
||||
// https://on.cypress.io/clock
|
||||
cy.clock()
|
||||
|
||||
@@ -18,22 +19,27 @@ it('should select null after timing out (fast)', () => {
|
||||
// not yet
|
||||
expect(onSelect).to.not.have.been.called
|
||||
})
|
||||
|
||||
cy.tick(1000).then(() => {
|
||||
// not yet
|
||||
expect(onSelect).to.not.have.been.called
|
||||
})
|
||||
|
||||
cy.tick(1000).then(() => {
|
||||
// not yet
|
||||
expect(onSelect).to.not.have.been.called
|
||||
})
|
||||
|
||||
cy.tick(1000).then(() => {
|
||||
// not yet
|
||||
expect(onSelect).to.not.have.been.called
|
||||
})
|
||||
|
||||
cy.tick(1000).then(() => {
|
||||
// not yet
|
||||
expect(onSelect).to.not.have.been.called
|
||||
})
|
||||
|
||||
cy.log('5 seconds passed')
|
||||
cy.tick(1000).then(() => {
|
||||
// NOW
|
||||
@@ -44,16 +50,18 @@ it('should select null after timing out (fast)', () => {
|
||||
it('should select null after timing out (slow)', () => {
|
||||
// without synthetic clock we must wait for the real delay
|
||||
const onSelect = cy.stub().as('selected')
|
||||
|
||||
mount(<Card onSelect={onSelect} />)
|
||||
cy.get('@selected', { timeout: 5100 }).should('have.been.calledWith', null)
|
||||
})
|
||||
|
||||
it('should accept selections', () => {
|
||||
const onSelect = cy.stub()
|
||||
|
||||
mount(<Card onSelect={onSelect} />)
|
||||
cy.get("[data-testid='2']")
|
||||
.click()
|
||||
.then(() => {
|
||||
expect(onSelect).to.have.been.calledWith(2)
|
||||
})
|
||||
cy.get('[data-testid=\'2\']')
|
||||
.click()
|
||||
.then(() => {
|
||||
expect(onSelect).to.have.been.calledWith(2)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,6 +6,7 @@ import { unmountComponentAtNode } from 'react-dom'
|
||||
|
||||
it('should select null after timing out', () => {
|
||||
const onSelect = cy.stub()
|
||||
|
||||
// https://on.cypress.io/clock
|
||||
cy.clock()
|
||||
mount(<Card onSelect={onSelect} />)
|
||||
@@ -13,6 +14,7 @@ it('should select null after timing out', () => {
|
||||
cy.tick(100).then(() => {
|
||||
expect(onSelect).to.not.have.been.called
|
||||
})
|
||||
|
||||
cy.tick(5000).then(() => {
|
||||
expect(onSelect).to.have.been.calledWith(null)
|
||||
})
|
||||
@@ -20,13 +22,14 @@ it('should select null after timing out', () => {
|
||||
|
||||
it('should cleanup on being removed', () => {
|
||||
const onSelect = cy.stub()
|
||||
|
||||
cy.clock()
|
||||
mount(<Card onSelect={onSelect} />)
|
||||
cy.tick(100).then(() => {
|
||||
expect(onSelect).to.not.have.been.called
|
||||
})
|
||||
|
||||
cy.get('#cypress-root').then($el => {
|
||||
cy.get('#cypress-root').then(($el) => {
|
||||
unmountComponentAtNode($el[0])
|
||||
})
|
||||
|
||||
@@ -37,6 +40,7 @@ it('should cleanup on being removed', () => {
|
||||
|
||||
it('should cleanup on being removed (using unmount)', () => {
|
||||
const onSelect = cy.stub()
|
||||
|
||||
cy.clock()
|
||||
mount(<Card onSelect={onSelect} />)
|
||||
cy.tick(100).then(() => {
|
||||
@@ -52,10 +56,11 @@ it('should cleanup on being removed (using unmount)', () => {
|
||||
|
||||
it('should accept selections', () => {
|
||||
const onSelect = cy.stub()
|
||||
|
||||
mount(<Card onSelect={onSelect} />)
|
||||
cy.get("[data-testid='2']")
|
||||
.click()
|
||||
.then(() => {
|
||||
expect(onSelect).to.have.been.calledWith(2)
|
||||
})
|
||||
cy.get('[data-testid=\'2\']')
|
||||
.click()
|
||||
.then(() => {
|
||||
expect(onSelect).to.have.been.calledWith(2)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import React, { Component } from 'react'
|
||||
|
||||
export default class Card extends Component {
|
||||
componentDidMount() {
|
||||
componentDidMount () {
|
||||
this._timeoutID = setTimeout(() => {
|
||||
this.props.onSelect(null)
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
componentWillUnmount () {
|
||||
clearTimeout(this._timeoutID)
|
||||
}
|
||||
|
||||
render() {
|
||||
return [1, 2, 3, 4].map(choice => (
|
||||
render () {
|
||||
return [1, 2, 3, 4].map((choice) => (
|
||||
<button
|
||||
key={choice}
|
||||
data-testid={choice}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
export default function Card(props) {
|
||||
export default function Card (props) {
|
||||
useEffect(() => {
|
||||
const timeoutID = setTimeout(() => {
|
||||
console.log('after timeout')
|
||||
@@ -14,7 +14,8 @@ export default function Card(props) {
|
||||
}, [props.onSelect])
|
||||
|
||||
console.log('inside Card')
|
||||
return [1, 2, 3, 4].map(choice => (
|
||||
|
||||
return [1, 2, 3, 4].map((choice) => (
|
||||
<button
|
||||
key={choice}
|
||||
data-testid={choice}
|
||||
|
||||
@@ -7,6 +7,7 @@ describe('Shopping list', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport(600, 600)
|
||||
})
|
||||
|
||||
it('shows FB list', () => {
|
||||
mount(<ShoppingList name="Facebook" />)
|
||||
cy.get('li').should('have.length', 3)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
|
||||
export default class ShoppingList extends React.Component {
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<div className="shopping-list">
|
||||
<h1>Shopping List for {this.props.name}</h1>
|
||||
|
||||
@@ -4,14 +4,14 @@ import { mount } from 'cypress-react-unit-test'
|
||||
|
||||
// let's put React component right in the spec file
|
||||
class Square extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
value: null,
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<button
|
||||
className="square"
|
||||
@@ -26,19 +26,21 @@ class Square extends React.Component {
|
||||
describe('Square', () => {
|
||||
it('changes value on click', () => {
|
||||
const selector = 'button.square'
|
||||
|
||||
mount(<Square value="X" />)
|
||||
// initially button is blank
|
||||
cy.get(selector).should('have.text', '')
|
||||
// but it changes text on click
|
||||
cy.get(selector)
|
||||
.click()
|
||||
.should('have.text', 'X')
|
||||
.click()
|
||||
.should('have.text', 'X')
|
||||
})
|
||||
|
||||
it('looks good', () => {
|
||||
mount(<Square />, {
|
||||
cssFile: 'cypress/component/advanced/tutorial/tic-tac-toe.css',
|
||||
})
|
||||
|
||||
// pause to show it
|
||||
cy.wait(1000)
|
||||
cy.get('.square').click()
|
||||
@@ -46,7 +48,7 @@ describe('Square', () => {
|
||||
|
||||
// check if style was applied
|
||||
cy.get('.square')
|
||||
.should('have.css', 'background-color', 'rgb(255, 255, 255)')
|
||||
.and('have.css', 'border', '1px solid rgb(153, 153, 153)')
|
||||
.should('have.css', 'background-color', 'rgb(255, 255, 255)')
|
||||
.and('have.css', 'border', '1px solid rgb(153, 153, 153)')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -16,10 +16,12 @@ describe('Tic Tac Toe', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport(200, 200)
|
||||
})
|
||||
|
||||
it('starts and lets X win', () => {
|
||||
mount(<Game />, {
|
||||
cssFile: 'cypress/component/advanced/tutorial/tic-tac-toe.css',
|
||||
})
|
||||
|
||||
cy.contains('.status', 'Next player: X')
|
||||
clickSquare(0, 0).click()
|
||||
cy.contains('.status', 'Next player: O')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// the code taken from https://codepen.io/gaearon/pen/LyyXgK
|
||||
import React from 'react'
|
||||
|
||||
export function calculateWinner(squares) {
|
||||
export function calculateWinner (squares) {
|
||||
const lines = [
|
||||
[0, 1, 2],
|
||||
[3, 4, 5],
|
||||
@@ -13,16 +13,19 @@ export function calculateWinner(squares) {
|
||||
[0, 4, 8],
|
||||
[2, 4, 6],
|
||||
]
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const [a, b, c] = lines[i]
|
||||
|
||||
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
|
||||
return squares[a]
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function Square(props) {
|
||||
function Square (props) {
|
||||
return (
|
||||
<button className="square" onClick={props.onClick}>
|
||||
{props.value}
|
||||
@@ -31,7 +34,7 @@ function Square(props) {
|
||||
}
|
||||
|
||||
export class Board extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
squares: Array(9).fill(null),
|
||||
@@ -39,19 +42,21 @@ export class Board extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleClick(i) {
|
||||
handleClick (i) {
|
||||
const squares = this.state.squares.slice()
|
||||
|
||||
if (calculateWinner(squares) || squares[i]) {
|
||||
return
|
||||
}
|
||||
|
||||
squares[i] = this.state.xIsNext ? 'X' : 'O'
|
||||
this.setState({
|
||||
squares: squares,
|
||||
squares,
|
||||
xIsNext: !this.state.xIsNext,
|
||||
})
|
||||
}
|
||||
|
||||
renderSquare(i) {
|
||||
renderSquare (i) {
|
||||
return (
|
||||
<Square
|
||||
value={this.state.squares[i]}
|
||||
@@ -60,13 +65,14 @@ export class Board extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
const winner = calculateWinner(this.state.squares)
|
||||
let status
|
||||
|
||||
if (winner) {
|
||||
status = 'Winner: ' + winner
|
||||
status = `Winner: ${winner}`
|
||||
} else {
|
||||
status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O')
|
||||
status = `Next player: ${this.state.xIsNext ? 'X' : 'O'}`
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -93,7 +99,7 @@ export class Board extends React.Component {
|
||||
}
|
||||
|
||||
export class Game extends React.Component {
|
||||
render() {
|
||||
render () {
|
||||
return (
|
||||
<div className="game">
|
||||
<div className="game-board">
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import * as React from 'react'
|
||||
|
||||
export default function App() {
|
||||
const [cart, setCart] = useState(
|
||||
export default function App () {
|
||||
const [cart, setCart] = React.useState(
|
||||
JSON.parse(localStorage.getItem('cart')) || ['kiwi 🥝'],
|
||||
)
|
||||
|
||||
const addJuice = () => {
|
||||
const updatedCart = cart.concat('juice 🧃')
|
||||
|
||||
setCart(updatedCart)
|
||||
localStorage.setItem('cart', JSON.stringify(updatedCart))
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ describe('App', () => {
|
||||
|
||||
it('uses cart from localStorage', () => {
|
||||
const items = ['apples 🍎', 'oranges 🍊', 'grapes 🍇']
|
||||
|
||||
localStorage.setItem('cart', JSON.stringify(items))
|
||||
mount(<App />)
|
||||
cy.get('.item').should('have.length', 3)
|
||||
@@ -27,7 +28,7 @@ describe('App', () => {
|
||||
// is updated after a delay, the assertion waits for it
|
||||
// https://on.cypress.io/retry-ability
|
||||
cy.wrap(localStorage)
|
||||
.invoke('getItem', 'cart')
|
||||
.should('equal', JSON.stringify(['kiwi 🥝', 'juice 🧃']))
|
||||
.invoke('getItem', 'cart')
|
||||
.should('equal', JSON.stringify(['kiwi 🥝', 'juice 🧃']))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,6 +7,7 @@ import { mount } from 'cypress-react-unit-test'
|
||||
describe('Stateless alert', () => {
|
||||
beforeEach(() => {
|
||||
const spy = cy.spy().as('alert')
|
||||
|
||||
cy.on('window:alert', spy)
|
||||
mount(<HelloWorld name="React" />, { ReactDom })
|
||||
})
|
||||
@@ -18,7 +19,7 @@ describe('Stateless alert', () => {
|
||||
it('alerts with name', () => {
|
||||
cy.contains('Say Hi').click()
|
||||
cy.get('@alert')
|
||||
.should('have.been.calledOnce')
|
||||
.and('have.been.be.calledWithExactly', 'Hi React')
|
||||
.should('have.been.calledOnce')
|
||||
.and('have.been.be.calledWithExactly', 'Hi React')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,24 +5,27 @@ import { mount } from 'cypress-react-unit-test'
|
||||
describe('Alias', () => {
|
||||
it('returns component by its name', () => {
|
||||
const Greeting = () => <div>Hello!</div>
|
||||
|
||||
mount(<Greeting />)
|
||||
// get the component instance by name "Greeting"
|
||||
cy.get('@Greeting')
|
||||
.its('props')
|
||||
.should('be.empty')
|
||||
.its('props')
|
||||
.should('be.empty')
|
||||
|
||||
// the component was constructed from the function Greeting
|
||||
cy.get('@Greeting')
|
||||
.its('type')
|
||||
.should('equal', Greeting)
|
||||
.its('type')
|
||||
.should('equal', Greeting)
|
||||
})
|
||||
|
||||
it('returns component by given display name', () => {
|
||||
const GreetingCard = props => <div>Hello {props.name}!</div>
|
||||
const GreetingCard = (props) => <div>Hello {props.name}!</div>
|
||||
|
||||
mount(<GreetingCard name="World" />, { alias: 'Hello' })
|
||||
cy.get('@Hello')
|
||||
.its('props')
|
||||
.should('deep.equal', {
|
||||
name: 'World',
|
||||
})
|
||||
.its('props')
|
||||
.should('deep.equal', {
|
||||
name: 'World',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,19 +9,19 @@ describe('Counter', () => {
|
||||
it('counts clicks', () => {
|
||||
mount(<Counter />)
|
||||
cy.contains('count: 0')
|
||||
.click()
|
||||
.contains('count: 1')
|
||||
.click()
|
||||
.contains('count: 2')
|
||||
.click()
|
||||
.contains('count: 1')
|
||||
.click()
|
||||
.contains('count: 2')
|
||||
})
|
||||
|
||||
it('counts clicks 2', () => {
|
||||
mount(<Counter />)
|
||||
cy.contains('count: 0')
|
||||
.click()
|
||||
.contains('count: 1')
|
||||
.click()
|
||||
.contains('count: 2')
|
||||
.click()
|
||||
.contains('count: 1')
|
||||
.click()
|
||||
.contains('count: 2')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -32,9 +32,9 @@ describe('Counter mounted before each test', () => {
|
||||
|
||||
it('goes to 3', () => {
|
||||
cy.contains('count: 0')
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.contains('count: 3')
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.contains('count: 3')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
|
||||
export class Counter extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
count: 0,
|
||||
@@ -14,7 +14,7 @@ export class Counter extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
return <p onClick={this.click}>count: {this.state.count}</p>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ describe('Counter using hooks', () => {
|
||||
mount(<Counter />)
|
||||
cy.contains('You clicked 0 times')
|
||||
cy.contains('Click me')
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
.click()
|
||||
|
||||
cy.contains('You clicked 3 times')
|
||||
})
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user