Sli.do event code #cy-for-ng
https://lizkeogh.com/2019/07/02/off-the-charts/
+3 degrees Celsius will be the end.
Sli.do event code #cy-for-ng
C / C++ / C# / Java / CoffeeScript / JavaScript / Node / Angular / Vue / Cycle.js / functional
(these slides)
"Run digest cycle in web worker" ng-conf 2015
angular.module('A', []).value('foo', 42);
ngDescribe({
name: 'inject example',
modules: 'A',
inject: ['foo', '$timeout'],
tests: function (deps) {
it('has foo', function () {
expect(deps.foo).toEqual(42);
});
it('has timeout service', function () {
expect(typeof deps.$timeout).toEqual('function');
});
}
});
Make testing (Angular) apps simpler
50 people. Atlanta, Philly, Boston, NYC, the world
Fast, easy and reliable testing for anything that runs in a browser
$ npm install -D cypress
it('adds 2 todos', () => {
cy.visit('http://localhost:3000')
cy.get('.new-todo')
.type('learn testing{enter}')
.type('be cool{enter}')
cy.get('.todo-list li')
.should('have.length', 2)
})
$ npx cypress open
Wait, where is the _Angular_ testing?
🕵🏻♂️ 🎁
$ ng add @cypress/schematic
Copied from briebug/cypress-schematic and maintained by Cypress team
$ ng run {project-name}:cypress-open
$ ng run {project-name}:cypress-run
# set up E2E with Cypress
$ ng e2e
describe('Authorization tests', () => {
it('allows the user to signup for a new account', () => {
browser.get('/signup')
element(by.css('#email-field')).sendKeys('user@email.com')
element(by.css('#confirm-email-field')).sendKeys('user@email.com')
element(by.css('#password-field')).sendKeys('testPassword1234')
element(by.cssContainingText('button', 'Create new account')).click()
expect(browser.getCurrentUrl()).toEqual('/signup/success')
})
})
Protractor
describe('Authorization Tests', () => {
it('allows the user to signup for a new account', () => {
cy.visit('/signup')
cy.get('#email-field').type('user@email.com')
cy.get('#confirm-email-field').type('user@email.com')
cy.get('#password-field').type('testPassword1234')
cy.get('button').contains('Create new account').click()
cy.url().should('include', '/signup/success')
})
})
Cypress
element(by.tagName('h1'))
element(by.css('.my-class'))
element(by.id('my-id'))
element(by.name('field-name'))
element(by.cssContainingText('.my-class', 'text'))
element(by.linkText('text'))
Protractor
cy.get('h1')
cy.get('.my-class')
cy.get('#my-id')
cy.get('input[name="field-name"]')
cy.get('.my-class').contains('text')
cy.contains('text')
Cypress
element(by.css('button')).click()
element(by.css('input')).sendKeys('my text')
element(by.css('input')).clear()
element.all(by.css('[type="checkbox"]')).first().click()
element(by.css('[type="radio"][value="radio1"]')).click()
element.all(by.css('[type="checkbox"][checked="true"]')).first().click()
element(by.cssContainingText('option', 'my value')).click()
Protractor
cy.get('button').click()
cy.get('input').type('my text')
cy.get('input').clear()
cy.get('[type="checkbox"]').first().check()
cy.get('[type="radio"]').check('radio1')
cy.get('[type="checkbox"]').not('[disabled]').first().uncheck()
cy.get('select[name="optionsList"]').select('my value')
Cypress
const list = element.all(by.css('li.selected'))
expect(list.count()).toBe(3)
expect(
element(by.tagName('form'))
.element(by.tagName('input'))
.getAttribute('class')
).not.toContain('disabled')
Protractor
cy.get('li.selected').should('have.length', 3)
cy.get('form').find('input')
.should('not.have.class', 'disabled')
Cypress
Is there an automated way to convert Protractor tests to Cypress specs?
No, sorry.
const page = {
login: () => {
element(by.css('.username')).sendKeys('my username')
element(by.css('.password')).sendKeys('my password')
element(by.css('button')).click()
},
}
it('should display the username of a logged in user', () => {
page.login()
expect(by.css('.username').getText()).toEqual('my username')
})
Protractor
const page = {
login () {
cy.get('.username').type('my username')
cy.get('.password').type('my password')
cy.get('button').click()
},
}
it('should display the username of a logged in user', () => {
page.login()
cy.get('.username').contains('my username')
})
Cypress
Cypress.Commands.add('login', (username, password) => {
cy.get('.username').type(username)
cy.get('.password').type(password)
cy.get('button').click()
})
it('should display the username of a logged in user', () => {
cy.login('Matt', Cypress.env('password'))
cy.get('.username').contains('Matt')
})
Cypress
export class HeroListComponent implements OnInit {
heroes: Observable<Hero[]>;
selectedHero: Hero;
constructor(private router: Router, private heroService: HeroService) {
// @ts-ignore
if (window.Cypress) {
// @ts-ignore
window.heroService = this.heroService;
}
}
it('should delete a hero', () => {
cy.window()
.its('heroService')
.invoke('deleteHero', { id: 11 })
.invoke('subscribe', (result) => {
// TODO return a promise
// so Cypress can wait for
// this event
expect(result).to.equal(null);
});
});
it('deletes all heroes through UI', () => {
cy.visit('/heroes')
// confirm the heroes have loaded and select "delete" buttons
cy.get('ul.heroes li button.delete')
.should('have.length.gt', 0)
// and delete all heroes
.click({ multiple: true })
})
it('deletes all heroes through UI', () => {
cy.visit('/heroes')
// confirm the heroes have loaded and select "delete" buttons
cy.get('ul.heroes li button.delete')
.should('have.length.gt', 0)
// and delete all heroes
.click({ multiple: true })
})
const getHeroesComponent = () =>
cy.window()
.should('have.property', 'HeroesComponent')
const getHeroes = () =>
getHeroesComponent().should('have.property', 'heroes')
const clearHeroes = () =>
getHeroes()
.then(heroes => {
cy.log(`clearing ${heroes.length} heroes`)
// @ts-ignore
heroes.length = 0
})
it('deletes all heroes through app action', () => {
cy.visit('/heroes')
clearHeroes()
getHeroes().should('have.length.gt', 0)
})
it('should delete a hero', () => {
cy.intercept('DELETE', '/api/heroes/11').as('sad')
cy.window()
.its('heroService')
.invoke('deleteHero', { id: 11 })
cy.wait('@sad')
});
Cypress tests can control the network responses
Intercept HTTP calls here
Any request can be observed or stubbed
(+ ServiceWorker, WebWorker)
cy.intercept( routeMatcher )
Spy on requests matching the route
cy.intercept( routeMatcher, response )
Stub requests matching the route
cy.intercept(...).as('alias')
Give request an alias for waiting
cy.intercept(url, routeHandler?)
cy.intercept(method, url, routeHandler?)
cy.intercept(routeMatcher, routeHandler?)
if present, than it is a stub*
*mostly
cy.intercept(url, routeHandler?)
cy.intercept(method, url, routeHandler?)
cy.intercept(routeMatcher, routeHandler?)
cy.intercept('http://example.com/widgets') // spy
cy.intercept('http://example.com/widgets',
{ fixture: 'widgets.json' }) // stubs
cy.intercept('POST', 'http://example.com/widgets',
{ statusCode: 200, body: 'it worked!' })
cy.intercept({ method: 'POST',
url: 'http://example.com/widgets' },
{ statusCode: 200, body: 'it worked!' })
cy.intercept('POST', '/graphql', (req) => {
if (req.body.hasOwnProperty('mutation')) {
req.alias = 'gqlMutation'
}
})
// assert that a matching request has been made
cy.wait('@gqlMutation')
*routeHandler but is a spy
cy.intercept('POST', '/graphql', (req) => {
if (req.body.hasOwnProperty('mutation')) {
req.reply({
data: {
id: 101
}
})
}
})
if you call req.reply from the route handler, it becomes a stub*
cy.intercept('POST', '/graphql', (req) => {
if (req.body.hasOwnProperty('mutation')) {
req.reply((res) => {
// 'res' represents the real destination's response
// three items in the response is enough
res.data.items.length = 3
})
}
})
// requests to '/users.json' will be fulfilled
// with the contents of the "users.json" fixture
cy.intercept('/users.json',
{ fixture: 'users.json' })
cy.fixture('users').then(users => {
// do something with the list
})
load JSON, images, binary files using https://on.cypress.io/fixture
cy.fixture('init-array').then(initArray => {
cy.fixture('solved-array').then(solvedArray => {
cy.stub(UniqueSudoku, 'getUniqueSudoku')
.returns([initArray, solvedArray])
})
})
import initArray from '../cypress/fixtures/init-array.json'
import solvedArray from '../cypress/fixtures/solved-array.json'
it('mocks board creation', () => {
cy.stub(UniqueSudoku, 'getUniqueSudoku')
.returns([initArray, solvedArray])
...
})
import { initEnv, mount } from 'cypress-angular-unit-test';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
it('shows the input', () => {
// Init Angular stuff
initEnv(AppComponent);
// You can also :
// initEnv({declarations: [AppComponent]});
// initEnv({imports: [MyModule]});
// component + any inputs object
mount(AppComponent, { title: 'World' });
// use any Cypress command afterwards
cy.contains('Welcome to World!');
});
});
import { initEnv, mount } from 'cypress-angular-unit-test';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
it('shows the input', () => {
// Init Angular stuff
initEnv(AppComponent);
// You can also :
// initEnv({declarations: [AppComponent]});
// initEnv({imports: [MyModule]});
// component + any inputs object
mount(AppComponent, { title: 'World' });
// use any Cypress command afterwards
cy.contains('Welcome to World!');
});
});