115 | Sign in or sign up to add comments on this article.
116 |
117 |
118 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/angular-realworld-example-app/cypress/integration/examples/utilities.spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | context('Utilities', () => {
4 | beforeEach(() => {
5 | cy.visit('https://example.cypress.io/utilities')
6 | })
7 |
8 | it('Cypress._ - call a lodash method', () => {
9 | // https://on.cypress.io/_
10 | cy.request('https://jsonplaceholder.cypress.io/users')
11 | .then((response) => {
12 | let ids = Cypress._.chain(response.body).map('id').take(3).value()
13 |
14 | expect(ids).to.deep.eq([1, 2, 3])
15 | })
16 | })
17 |
18 | it('Cypress.$ - call a jQuery method', () => {
19 | // https://on.cypress.io/$
20 | let $li = Cypress.$('.utility-jquery li:first')
21 |
22 | cy.wrap($li)
23 | .should('not.have.class', 'active')
24 | .click()
25 | .should('have.class', 'active')
26 | })
27 |
28 | it('Cypress.Blob - blob utilities and base64 string conversion', () => {
29 | // https://on.cypress.io/blob
30 | cy.get('.utility-blob').then(($div) => {
31 | // https://github.com/nolanlawson/blob-util#imgSrcToDataURL
32 | // get the dataUrl string for the javascript-logo
33 | return Cypress.Blob.imgSrcToDataURL('https://example.cypress.io/assets/img/javascript-logo.png', undefined, 'anonymous')
34 | .then((dataUrl) => {
35 | // create an element and set its src to the dataUrl
36 | let img = Cypress.$('', { src: dataUrl })
37 |
38 | // need to explicitly return cy here since we are initially returning
39 | // the Cypress.Blob.imgSrcToDataURL promise to our test
40 | // append the image
41 | $div.append(img)
42 |
43 | cy.get('.utility-blob img').click()
44 | .should('have.attr', 'src', dataUrl)
45 | })
46 | })
47 | })
48 |
49 | it('Cypress.minimatch - test out glob patterns against strings', () => {
50 | // https://on.cypress.io/minimatch
51 | let matching = Cypress.minimatch('/users/1/comments', '/users/*/comments', {
52 | matchBase: true,
53 | })
54 |
55 | expect(matching, 'matching wildcard').to.be.true
56 |
57 | matching = Cypress.minimatch('/users/1/comments/2', '/users/*/comments', {
58 | matchBase: true,
59 | })
60 |
61 | expect(matching, 'comments').to.be.false
62 |
63 | // ** matches against all downstream path segments
64 | matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/**', {
65 | matchBase: true,
66 | })
67 |
68 | expect(matching, 'comments').to.be.true
69 |
70 | // whereas * matches only the next path segment
71 |
72 | matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/*', {
73 | matchBase: false,
74 | })
75 |
76 | expect(matching, 'comments').to.be.false
77 | })
78 |
79 | it('Cypress.moment() - format or parse dates using a moment method', () => {
80 | // https://on.cypress.io/moment
81 | const time = Cypress.moment('2014-04-25T19:38:53.196Z').utc().format('h:mm A')
82 |
83 | expect(time).to.be.a('string')
84 |
85 | cy.get('.utility-moment').contains('3:38 PM')
86 | .should('have.class', 'badge')
87 |
88 | // the time in the element should be between 3pm and 5pm
89 | const start = Cypress.moment('3:00 PM', 'LT')
90 | const end = Cypress.moment('5:00 PM', 'LT')
91 |
92 | cy.get('.utility-moment .badge')
93 | .should(($el) => {
94 | // parse American time like "3:38 PM"
95 | const m = Cypress.moment($el.text().trim(), 'LT')
96 |
97 | // display hours + minutes + AM|PM
98 | const f = 'h:mm A'
99 |
100 | expect(m.isBetween(start, end),
101 | `${m.format(f)} should be between ${start.format(f)} and ${end.format(f)}`).to.be.true
102 | })
103 | })
104 |
105 | it('Cypress.Promise - instantiate a bluebird promise', () => {
106 | // https://on.cypress.io/promise
107 | let waited = false
108 |
109 | /**
110 | * @return Bluebird
111 | */
112 | function waitOneSecond () {
113 | // return a promise that resolves after 1 second
114 | // @ts-ignore TS2351 (new Cypress.Promise)
115 | return new Cypress.Promise((resolve, reject) => {
116 | setTimeout(() => {
117 | // set waited to true
118 | waited = true
119 |
120 | // resolve with 'foo' string
121 | resolve('foo')
122 | }, 1000)
123 | })
124 | }
125 |
126 | cy.then(() => {
127 | // return a promise to cy.then() that
128 | // is awaited until it resolves
129 | // @ts-ignore TS7006
130 | return waitOneSecond().then((str) => {
131 | expect(str).to.eq('foo')
132 | expect(waited).to.be.true
133 | })
134 | })
135 | })
136 | })
137 |
--------------------------------------------------------------------------------
/angular-realworld-example-app/cypress/integration/examples/assertions.spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | context('Assertions', () => {
4 | beforeEach(() => {
5 | cy.visit('https://example.cypress.io/commands/assertions')
6 | })
7 |
8 | describe('Implicit Assertions', () => {
9 | it('.should() - make an assertion about the current subject', () => {
10 | // https://on.cypress.io/should
11 | cy.get('.assertion-table')
12 | .find('tbody tr:last')
13 | .should('have.class', 'success')
14 | .find('td')
15 | .first()
16 | // checking the text of the
element in various ways
17 | .should('have.text', 'Column content')
18 | .should('contain', 'Column content')
19 | .should('have.html', 'Column content')
20 | // chai-jquery uses "is()" to check if element matches selector
21 | .should('match', 'td')
22 | // to match text content against a regular expression
23 | // first need to invoke jQuery method text()
24 | // and then match using regular expression
25 | .invoke('text')
26 | .should('match', /column content/i)
27 |
28 | // a better way to check element's text content against a regular expression
29 | // is to use "cy.contains"
30 | // https://on.cypress.io/contains
31 | cy.get('.assertion-table')
32 | .find('tbody tr:last')
33 | // finds first
element with text content matching regular expression
34 | .contains('td', /column content/i)
35 | .should('be.visible')
36 |
37 | // for more information about asserting element's text
38 | // see https://on.cypress.io/using-cypress-faq#How-do-I-get-an-element’s-text-contents
39 | })
40 |
41 | it('.and() - chain multiple assertions together', () => {
42 | // https://on.cypress.io/and
43 | cy.get('.assertions-link')
44 | .should('have.class', 'active')
45 | .and('have.attr', 'href')
46 | .and('include', 'cypress.io')
47 | })
48 | })
49 |
50 | describe('Explicit Assertions', () => {
51 | // https://on.cypress.io/assertions
52 | it('expect - make an assertion about a specified subject', () => {
53 | // We can use Chai's BDD style assertions
54 | expect(true).to.be.true
55 | const o = { foo: 'bar' }
56 |
57 | expect(o).to.equal(o)
58 | expect(o).to.deep.equal({ foo: 'bar' })
59 | // matching text using regular expression
60 | expect('FooBar').to.match(/bar$/i)
61 | })
62 |
63 | it('pass your own callback function to should()', () => {
64 | // Pass a function to should that can have any number
65 | // of explicit assertions within it.
66 | // The ".should(cb)" function will be retried
67 | // automatically until it passes all your explicit assertions or times out.
68 | cy.get('.assertions-p')
69 | .find('p')
70 | .should(($p) => {
71 | // https://on.cypress.io/$
72 | // return an array of texts from all of the p's
73 | // @ts-ignore TS6133 unused variable
74 | const texts = $p.map((i, el) => Cypress.$(el).text())
75 |
76 | // jquery map returns jquery object
77 | // and .get() convert this to simple array
78 | const paragraphs = texts.get()
79 |
80 | // array should have length of 3
81 | expect(paragraphs, 'has 3 paragraphs').to.have.length(3)
82 |
83 | // use second argument to expect(...) to provide clear
84 | // message with each assertion
85 | expect(paragraphs, 'has expected text in each paragraph').to.deep.eq([
86 | 'Some text from first p',
87 | 'More text from second p',
88 | 'And even more text from third p',
89 | ])
90 | })
91 | })
92 |
93 | it('finds element by class name regex', () => {
94 | cy.get('.docs-header')
95 | .find('div')
96 | // .should(cb) callback function will be retried
97 | .should(($div) => {
98 | expect($div).to.have.length(1)
99 |
100 | const className = $div[0].className
101 |
102 | expect(className).to.match(/heading-/)
103 | })
104 | // .then(cb) callback is not retried,
105 | // it either passes or fails
106 | .then(($div) => {
107 | expect($div, 'text content').to.have.text('Introduction')
108 | })
109 | })
110 |
111 | it('can throw any error', () => {
112 | cy.get('.docs-header')
113 | .find('div')
114 | .should(($div) => {
115 | if ($div.length !== 1) {
116 | // you can throw your own errors
117 | throw new Error('Did not find 1 element')
118 | }
119 |
120 | const className = $div[0].className
121 |
122 | if (!className.match(/heading-/)) {
123 | throw new Error(`Could not find class "heading-" in ${className}`)
124 | }
125 | })
126 | })
127 |
128 | it('matches unknown text between two elements', () => {
129 | /**
130 | * Text from the first element.
131 | * @type {string}
132 | */
133 | let text
134 |
135 | /**
136 | * Normalizes passed text,
137 | * useful before comparing text with spaces and different capitalization.
138 | * @param {string} s Text to normalize
139 | */
140 | const normalizeText = (s) => s.replace(/\s/g, '').toLowerCase()
141 |
142 | cy.get('.two-elements')
143 | .find('.first')
144 | .then(($first) => {
145 | // save text from the first element
146 | text = normalizeText($first.text())
147 | })
148 |
149 | cy.get('.two-elements')
150 | .find('.second')
151 | .should(($div) => {
152 | // we can massage text before comparing
153 | const secondText = normalizeText($div.text())
154 |
155 | expect(secondText, 'second text').to.equal(text)
156 | })
157 | })
158 |
159 | it('assert - assert shape of an object', () => {
160 | const person = {
161 | name: 'Joe',
162 | age: 20,
163 | }
164 |
165 | assert.isObject(person, 'value is object')
166 | })
167 |
168 | it('retries the should callback until assertions pass', () => {
169 | cy.get('#random-number')
170 | .should(($div) => {
171 | const n = parseFloat($div.text())
172 |
173 | expect(n).to.be.gte(1).and.be.lte(10)
174 | })
175 | })
176 | })
177 | })
178 |
--------------------------------------------------------------------------------
/angular-realworld-example-app/cypress/integration/examples/cypress_api.spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | context('Cypress.Commands', () => {
4 | beforeEach(() => {
5 | cy.visit('https://example.cypress.io/cypress-api')
6 | })
7 |
8 | // https://on.cypress.io/custom-commands
9 |
10 | it('.add() - create a custom command', () => {
11 | Cypress.Commands.add('console', {
12 | prevSubject: true,
13 | }, (subject, method) => {
14 | // the previous subject is automatically received
15 | // and the commands arguments are shifted
16 |
17 | // allow us to change the console method used
18 | method = method || 'log'
19 |
20 | // log the subject to the console
21 | // @ts-ignore TS7017
22 | console[method]('The subject is', subject)
23 |
24 | // whatever we return becomes the new subject
25 | // we don't want to change the subject so
26 | // we return whatever was passed in
27 | return subject
28 | })
29 |
30 | // @ts-ignore TS2339
31 | cy.get('button').console('info').then(($button) => {
32 | // subject is still $button
33 | })
34 | })
35 | })
36 |
37 | context('Cypress.Cookies', () => {
38 | beforeEach(() => {
39 | cy.visit('https://example.cypress.io/cypress-api')
40 | })
41 |
42 | // https://on.cypress.io/cookies
43 | it('.debug() - enable or disable debugging', () => {
44 | Cypress.Cookies.debug(true)
45 |
46 | // Cypress will now log in the console when
47 | // cookies are set or cleared
48 | cy.setCookie('fakeCookie', '123ABC')
49 | cy.clearCookie('fakeCookie')
50 | cy.setCookie('fakeCookie', '123ABC')
51 | cy.clearCookie('fakeCookie')
52 | cy.setCookie('fakeCookie', '123ABC')
53 | })
54 |
55 | it('.preserveOnce() - preserve cookies by key', () => {
56 | // normally cookies are reset after each test
57 | cy.getCookie('fakeCookie').should('not.be.ok')
58 |
59 | // preserving a cookie will not clear it when
60 | // the next test starts
61 | cy.setCookie('lastCookie', '789XYZ')
62 | Cypress.Cookies.preserveOnce('lastCookie')
63 | })
64 |
65 | it('.defaults() - set defaults for all cookies', () => {
66 | // now any cookie with the name 'session_id' will
67 | // not be cleared before each new test runs
68 | Cypress.Cookies.defaults({
69 | preserve: 'session_id',
70 | })
71 | })
72 | })
73 |
74 | context('Cypress.arch', () => {
75 | beforeEach(() => {
76 | cy.visit('https://example.cypress.io/cypress-api')
77 | })
78 |
79 | it('Get CPU architecture name of underlying OS', () => {
80 | // https://on.cypress.io/arch
81 | expect(Cypress.arch).to.exist
82 | })
83 | })
84 |
85 | context('Cypress.config()', () => {
86 | beforeEach(() => {
87 | cy.visit('https://example.cypress.io/cypress-api')
88 | })
89 |
90 | it('Get and set configuration options', () => {
91 | // https://on.cypress.io/config
92 | let myConfig = Cypress.config()
93 |
94 | expect(myConfig).to.have.property('animationDistanceThreshold', 5)
95 | expect(myConfig).to.have.property('baseUrl', null)
96 | expect(myConfig).to.have.property('defaultCommandTimeout', 4000)
97 | expect(myConfig).to.have.property('requestTimeout', 5000)
98 | expect(myConfig).to.have.property('responseTimeout', 30000)
99 | expect(myConfig).to.have.property('viewportHeight', 660)
100 | expect(myConfig).to.have.property('viewportWidth', 1000)
101 | expect(myConfig).to.have.property('pageLoadTimeout', 60000)
102 | expect(myConfig).to.have.property('waitForAnimations', true)
103 |
104 | expect(Cypress.config('pageLoadTimeout')).to.eq(60000)
105 |
106 | // this will change the config for the rest of your tests!
107 | Cypress.config('pageLoadTimeout', 20000)
108 |
109 | expect(Cypress.config('pageLoadTimeout')).to.eq(20000)
110 |
111 | Cypress.config('pageLoadTimeout', 60000)
112 | })
113 | })
114 |
115 | context('Cypress.dom', () => {
116 | beforeEach(() => {
117 | cy.visit('https://example.cypress.io/cypress-api')
118 | })
119 |
120 | // https://on.cypress.io/dom
121 | it('.isHidden() - determine if a DOM element is hidden', () => {
122 | let hiddenP = Cypress.$('.dom-p p.hidden').get(0)
123 | let visibleP = Cypress.$('.dom-p p.visible').get(0)
124 |
125 | // our first paragraph has css class 'hidden'
126 | expect(Cypress.dom.isHidden(hiddenP)).to.be.true
127 | expect(Cypress.dom.isHidden(visibleP)).to.be.false
128 | })
129 | })
130 |
131 | context('Cypress.env()', () => {
132 | beforeEach(() => {
133 | cy.visit('https://example.cypress.io/cypress-api')
134 | })
135 |
136 | // We can set environment variables for highly dynamic values
137 |
138 | // https://on.cypress.io/environment-variables
139 | it('Get environment variables', () => {
140 | // https://on.cypress.io/env
141 | // set multiple environment variables
142 | Cypress.env({
143 | host: 'veronica.dev.local',
144 | api_server: 'http://localhost:8888/v1/',
145 | })
146 |
147 | // get environment variable
148 | expect(Cypress.env('host')).to.eq('veronica.dev.local')
149 |
150 | // set environment variable
151 | Cypress.env('api_server', 'http://localhost:8888/v2/')
152 | expect(Cypress.env('api_server')).to.eq('http://localhost:8888/v2/')
153 |
154 | // get all environment variable
155 | expect(Cypress.env()).to.have.property('host', 'veronica.dev.local')
156 | expect(Cypress.env()).to.have.property('api_server', 'http://localhost:8888/v2/')
157 | })
158 | })
159 |
160 | context('Cypress.log', () => {
161 | beforeEach(() => {
162 | cy.visit('https://example.cypress.io/cypress-api')
163 | })
164 |
165 | it('Control what is printed to the Command Log', () => {
166 | // https://on.cypress.io/cypress-log
167 | })
168 | })
169 |
170 | context('Cypress.platform', () => {
171 | beforeEach(() => {
172 | cy.visit('https://example.cypress.io/cypress-api')
173 | })
174 |
175 | it('Get underlying OS name', () => {
176 | // https://on.cypress.io/platform
177 | expect(Cypress.platform).to.be.exist
178 | })
179 | })
180 |
181 | context('Cypress.version', () => {
182 | beforeEach(() => {
183 | cy.visit('https://example.cypress.io/cypress-api')
184 | })
185 |
186 | it('Get current version of Cypress being run', () => {
187 | // https://on.cypress.io/version
188 | expect(Cypress.version).to.be.exist
189 | })
190 | })
191 |
192 | context('Cypress.spec', () => {
193 | beforeEach(() => {
194 | cy.visit('https://example.cypress.io/cypress-api')
195 | })
196 |
197 | it('Get current spec information', () => {
198 | // https://on.cypress.io/spec
199 | // wrap the object so we can inspect it easily by clicking in the command log
200 | cy.wrap(Cypress.spec).should('include.keys', ['name', 'relative', 'absolute'])
201 | })
202 | })
203 |
--------------------------------------------------------------------------------
/angular-realworld-example-app/cypress/integration/examples/network_requests.spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | context('Network Requests', () => {
4 | beforeEach(() => {
5 | cy.visit('https://example.cypress.io/commands/network-requests')
6 | })
7 |
8 | // Manage HTTP requests in your app
9 |
10 | it('cy.request() - make an XHR request', () => {
11 | // https://on.cypress.io/request
12 | cy.request('https://jsonplaceholder.cypress.io/comments')
13 | .should((response) => {
14 | expect(response.status).to.eq(200)
15 | // the server sometimes gets an extra comment posted from another machine
16 | // which gets returned as 1 extra object
17 | expect(response.body).to.have.property('length').and.be.oneOf([500, 501])
18 | expect(response).to.have.property('headers')
19 | expect(response).to.have.property('duration')
20 | })
21 | })
22 |
23 | it('cy.request() - verify response using BDD syntax', () => {
24 | cy.request('https://jsonplaceholder.cypress.io/comments')
25 | .then((response) => {
26 | // https://on.cypress.io/assertions
27 | expect(response).property('status').to.equal(200)
28 | expect(response).property('body').to.have.property('length').and.be.oneOf([500, 501])
29 | expect(response).to.include.keys('headers', 'duration')
30 | })
31 | })
32 |
33 | it('cy.request() with query parameters', () => {
34 | // will execute request
35 | // https://jsonplaceholder.cypress.io/comments?postId=1&id=3
36 | cy.request({
37 | url: 'https://jsonplaceholder.cypress.io/comments',
38 | qs: {
39 | postId: 1,
40 | id: 3,
41 | },
42 | })
43 | .its('body')
44 | .should('be.an', 'array')
45 | .and('have.length', 1)
46 | .its('0') // yields first element of the array
47 | .should('contain', {
48 | postId: 1,
49 | id: 3,
50 | })
51 | })
52 |
53 | it('cy.request() - pass result to the second request', () => {
54 | // first, let's find out the userId of the first user we have
55 | cy.request('https://jsonplaceholder.cypress.io/users?_limit=1')
56 | .its('body') // yields the response object
57 | .its('0') // yields the first element of the returned list
58 | // the above two commands its('body').its('0')
59 | // can be written as its('body.0')
60 | // if you do not care about TypeScript checks
61 | .then((user) => {
62 | expect(user).property('id').to.be.a('number')
63 | // make a new post on behalf of the user
64 | cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', {
65 | userId: user.id,
66 | title: 'Cypress Test Runner',
67 | body: 'Fast, easy and reliable testing for anything that runs in a browser.',
68 | })
69 | })
70 | // note that the value here is the returned value of the 2nd request
71 | // which is the new post object
72 | .then((response) => {
73 | expect(response).property('status').to.equal(201) // new entity created
74 | expect(response).property('body').to.contain({
75 | title: 'Cypress Test Runner',
76 | })
77 |
78 | // we don't know the exact post id - only that it will be > 100
79 | // since JSONPlaceholder has built-in 100 posts
80 | expect(response.body).property('id').to.be.a('number')
81 | .and.to.be.gt(100)
82 |
83 | // we don't know the user id here - since it was in above closure
84 | // so in this test just confirm that the property is there
85 | expect(response.body).property('userId').to.be.a('number')
86 | })
87 | })
88 |
89 | it('cy.request() - save response in the shared test context', () => {
90 | // https://on.cypress.io/variables-and-aliases
91 | cy.request('https://jsonplaceholder.cypress.io/users?_limit=1')
92 | .its('body').its('0') // yields the first element of the returned list
93 | .as('user') // saves the object in the test context
94 | .then(function () {
95 | // NOTE 👀
96 | // By the time this callback runs the "as('user')" command
97 | // has saved the user object in the test context.
98 | // To access the test context we need to use
99 | // the "function () { ... }" callback form,
100 | // otherwise "this" points at a wrong or undefined object!
101 | cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', {
102 | userId: this.user.id,
103 | title: 'Cypress Test Runner',
104 | body: 'Fast, easy and reliable testing for anything that runs in a browser.',
105 | })
106 | .its('body').as('post') // save the new post from the response
107 | })
108 | .then(function () {
109 | // When this callback runs, both "cy.request" API commands have finished
110 | // and the test context has "user" and "post" objects set.
111 | // Let's verify them.
112 | expect(this.post, 'post has the right user id').property('userId').to.equal(this.user.id)
113 | })
114 | })
115 |
116 | it('cy.intercept() - route responses to matching requests', () => {
117 | // https://on.cypress.io/http
118 |
119 | let message = 'whoa, this comment does not exist'
120 |
121 | // Listen to GET to comments/1
122 | cy.intercept('GET', '**/comments/*').as('getComment')
123 |
124 | // we have code that gets a comment when
125 | // the button is clicked in scripts.js
126 | cy.get('.network-btn').click()
127 |
128 | // https://on.cypress.io/wait
129 | cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304])
130 |
131 | // Listen to POST to comments
132 | cy.intercept('POST', '**/comments').as('postComment')
133 |
134 | // we have code that posts a comment when
135 | // the button is clicked in scripts.js
136 | cy.get('.network-post').click()
137 | cy.wait('@postComment').should(({ request, response }) => {
138 | expect(request.body).to.include('email')
139 | expect(request.headers).to.have.property('content-type')
140 | expect(response && response.body).to.have.property('name', 'Using POST in cy.intercept()')
141 | })
142 |
143 | // Stub a response to PUT comments/ ****
144 | cy.intercept({
145 | method: 'PUT',
146 | url: '**/comments/*',
147 | }, {
148 | statusCode: 404,
149 | body: { error: message },
150 | headers: { 'access-control-allow-origin': '*' },
151 | delayMs: 500,
152 | }).as('putComment')
153 |
154 | // we have code that puts a comment when
155 | // the button is clicked in scripts.js
156 | cy.get('.network-put').click()
157 |
158 | cy.wait('@putComment')
159 |
160 | // our 404 statusCode logic in scripts.js executed
161 | cy.get('.network-put-comment').should('contain', message)
162 | })
163 | })
164 |
--------------------------------------------------------------------------------
/angular-realworld-example-app/cypress/integration/examples/spies_stubs_clocks.spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | // remove no check once Cypress.sinon is typed
3 | // https://github.com/cypress-io/cypress/issues/6720
4 |
5 | context('Spies, Stubs, and Clock', () => {
6 | it('cy.spy() - wrap a method in a spy', () => {
7 | // https://on.cypress.io/spy
8 | cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
9 |
10 | const obj = {
11 | foo () {},
12 | }
13 |
14 | const spy = cy.spy(obj, 'foo').as('anyArgs')
15 |
16 | obj.foo()
17 |
18 | expect(spy).to.be.called
19 | })
20 |
21 | it('cy.spy() retries until assertions pass', () => {
22 | cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
23 |
24 | const obj = {
25 | /**
26 | * Prints the argument passed
27 | * @param x {any}
28 | */
29 | foo (x) {
30 | console.log('obj.foo called with', x)
31 | },
32 | }
33 |
34 | cy.spy(obj, 'foo').as('foo')
35 |
36 | setTimeout(() => {
37 | obj.foo('first')
38 | }, 500)
39 |
40 | setTimeout(() => {
41 | obj.foo('second')
42 | }, 2500)
43 |
44 | cy.get('@foo').should('have.been.calledTwice')
45 | })
46 |
47 | it('cy.stub() - create a stub and/or replace a function with stub', () => {
48 | // https://on.cypress.io/stub
49 | cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
50 |
51 | const obj = {
52 | /**
53 | * prints both arguments to the console
54 | * @param a {string}
55 | * @param b {string}
56 | */
57 | foo (a, b) {
58 | console.log('a', a, 'b', b)
59 | },
60 | }
61 |
62 | const stub = cy.stub(obj, 'foo').as('foo')
63 |
64 | obj.foo('foo', 'bar')
65 |
66 | expect(stub).to.be.called
67 | })
68 |
69 | it('cy.clock() - control time in the browser', () => {
70 | // https://on.cypress.io/clock
71 |
72 | // create the date in UTC so its always the same
73 | // no matter what local timezone the browser is running in
74 | const now = new Date(Date.UTC(2017, 2, 14)).getTime()
75 |
76 | cy.clock(now)
77 | cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
78 | cy.get('#clock-div').click()
79 | .should('have.text', '1489449600')
80 | })
81 |
82 | it('cy.tick() - move time in the browser', () => {
83 | // https://on.cypress.io/tick
84 |
85 | // create the date in UTC so its always the same
86 | // no matter what local timezone the browser is running in
87 | const now = new Date(Date.UTC(2017, 2, 14)).getTime()
88 |
89 | cy.clock(now)
90 | cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
91 | cy.get('#tick-div').click()
92 | .should('have.text', '1489449600')
93 |
94 | cy.tick(10000) // 10 seconds passed
95 | cy.get('#tick-div').click()
96 | .should('have.text', '1489449610')
97 | })
98 |
99 | it('cy.stub() matches depending on arguments', () => {
100 | // see all possible matchers at
101 | // https://sinonjs.org/releases/latest/matchers/
102 | const greeter = {
103 | /**
104 | * Greets a person
105 | * @param {string} name
106 | */
107 | greet (name) {
108 | return `Hello, ${name}!`
109 | },
110 | }
111 |
112 | cy.stub(greeter, 'greet')
113 | .callThrough() // if you want non-matched calls to call the real method
114 | .withArgs(Cypress.sinon.match.string).returns('Hi')
115 | .withArgs(Cypress.sinon.match.number).throws(new Error('Invalid name'))
116 |
117 | expect(greeter.greet('World')).to.equal('Hi')
118 | // @ts-ignore
119 | expect(() => greeter.greet(42)).to.throw('Invalid name')
120 | expect(greeter.greet).to.have.been.calledTwice
121 |
122 | // non-matched calls goes the actual method
123 | // @ts-ignore
124 | expect(greeter.greet()).to.equal('Hello, undefined!')
125 | })
126 |
127 | it('matches call arguments using Sinon matchers', () => {
128 | // see all possible matchers at
129 | // https://sinonjs.org/releases/latest/matchers/
130 | const calculator = {
131 | /**
132 | * returns the sum of two arguments
133 | * @param a {number}
134 | * @param b {number}
135 | */
136 | add (a, b) {
137 | return a + b
138 | },
139 | }
140 |
141 | const spy = cy.spy(calculator, 'add').as('add')
142 |
143 | expect(calculator.add(2, 3)).to.equal(5)
144 |
145 | // if we want to assert the exact values used during the call
146 | expect(spy).to.be.calledWith(2, 3)
147 |
148 | // let's confirm "add" method was called with two numbers
149 | expect(spy).to.be.calledWith(Cypress.sinon.match.number, Cypress.sinon.match.number)
150 |
151 | // alternatively, provide the value to match
152 | expect(spy).to.be.calledWith(Cypress.sinon.match(2), Cypress.sinon.match(3))
153 |
154 | // match any value
155 | expect(spy).to.be.calledWith(Cypress.sinon.match.any, 3)
156 |
157 | // match any value from a list
158 | expect(spy).to.be.calledWith(Cypress.sinon.match.in([1, 2, 3]), 3)
159 |
160 | /**
161 | * Returns true if the given number is event
162 | * @param {number} x
163 | */
164 | const isEven = (x) => x % 2 === 0
165 |
166 | // expect the value to pass a custom predicate function
167 | // the second argument to "sinon.match(predicate, message)" is
168 | // shown if the predicate does not pass and assertion fails
169 | expect(spy).to.be.calledWith(Cypress.sinon.match(isEven, 'isEven'), 3)
170 |
171 | /**
172 | * Returns a function that checks if a given number is larger than the limit
173 | * @param {number} limit
174 | * @returns {(x: number) => boolean}
175 | */
176 | const isGreaterThan = (limit) => (x) => x > limit
177 |
178 | /**
179 | * Returns a function that checks if a given number is less than the limit
180 | * @param {number} limit
181 | * @returns {(x: number) => boolean}
182 | */
183 | const isLessThan = (limit) => (x) => x < limit
184 |
185 | // you can combine several matchers using "and", "or"
186 | expect(spy).to.be.calledWith(
187 | Cypress.sinon.match.number,
188 | Cypress.sinon.match(isGreaterThan(2), '> 2').and(Cypress.sinon.match(isLessThan(4), '< 4')),
189 | )
190 |
191 | expect(spy).to.be.calledWith(
192 | Cypress.sinon.match.number,
193 | Cypress.sinon.match(isGreaterThan(200), '> 200').or(Cypress.sinon.match(3)),
194 | )
195 |
196 | // matchers can be used from BDD assertions
197 | cy.get('@add').should('have.been.calledWith',
198 | Cypress.sinon.match.number, Cypress.sinon.match(3))
199 |
200 | // you can alias matchers for shorter test code
201 | const { match: M } = Cypress.sinon
202 |
203 | cy.get('@add').should('have.been.calledWith', M.number, M(3))
204 | })
205 | })
206 |
--------------------------------------------------------------------------------
/angular-realworld-example-app/cypress/integration/examples/actions.spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | context('Actions', () => {
4 | beforeEach(() => {
5 | cy.visit('https://example.cypress.io/commands/actions')
6 | })
7 |
8 | // https://on.cypress.io/interacting-with-elements
9 |
10 | it('.type() - type into a DOM element', () => {
11 | // https://on.cypress.io/type
12 | cy.get('.action-email')
13 | .type('fake@email.com').should('have.value', 'fake@email.com')
14 |
15 | // .type() with special character sequences
16 | .type('{leftarrow}{rightarrow}{uparrow}{downarrow}')
17 | .type('{del}{selectall}{backspace}')
18 |
19 | // .type() with key modifiers
20 | .type('{alt}{option}') //these are equivalent
21 | .type('{ctrl}{control}') //these are equivalent
22 | .type('{meta}{command}{cmd}') //these are equivalent
23 | .type('{shift}')
24 |
25 | // Delay each keypress by 0.1 sec
26 | .type('slow.typing@email.com', { delay: 100 })
27 | .should('have.value', 'slow.typing@email.com')
28 |
29 | cy.get('.action-disabled')
30 | // Ignore error checking prior to type
31 | // like whether the input is visible or disabled
32 | .type('disabled error checking', { force: true })
33 | .should('have.value', 'disabled error checking')
34 | })
35 |
36 | it('.focus() - focus on a DOM element', () => {
37 | // https://on.cypress.io/focus
38 | cy.get('.action-focus').focus()
39 | .should('have.class', 'focus')
40 | .prev().should('have.attr', 'style', 'color: orange;')
41 | })
42 |
43 | it('.blur() - blur off a DOM element', () => {
44 | // https://on.cypress.io/blur
45 | cy.get('.action-blur').type('About to blur').blur()
46 | .should('have.class', 'error')
47 | .prev().should('have.attr', 'style', 'color: red;')
48 | })
49 |
50 | it('.clear() - clears an input or textarea element', () => {
51 | // https://on.cypress.io/clear
52 | cy.get('.action-clear').type('Clear this text')
53 | .should('have.value', 'Clear this text')
54 | .clear()
55 | .should('have.value', '')
56 | })
57 |
58 | it('.submit() - submit a form', () => {
59 | // https://on.cypress.io/submit
60 | cy.get('.action-form')
61 | .find('[type="text"]').type('HALFOFF')
62 |
63 | cy.get('.action-form').submit()
64 | .next().should('contain', 'Your form has been submitted!')
65 | })
66 |
67 | it('.click() - click on a DOM element', () => {
68 | // https://on.cypress.io/click
69 | cy.get('.action-btn').click()
70 |
71 | // You can click on 9 specific positions of an element:
72 | // -----------------------------------
73 | // | topLeft top topRight |
74 | // | |
75 | // | |
76 | // | |
77 | // | left center right |
78 | // | |
79 | // | |
80 | // | |
81 | // | bottomLeft bottom bottomRight |
82 | // -----------------------------------
83 |
84 | // clicking in the center of the element is the default
85 | cy.get('#action-canvas').click()
86 |
87 | cy.get('#action-canvas').click('topLeft')
88 | cy.get('#action-canvas').click('top')
89 | cy.get('#action-canvas').click('topRight')
90 | cy.get('#action-canvas').click('left')
91 | cy.get('#action-canvas').click('right')
92 | cy.get('#action-canvas').click('bottomLeft')
93 | cy.get('#action-canvas').click('bottom')
94 | cy.get('#action-canvas').click('bottomRight')
95 |
96 | // .click() accepts an x and y coordinate
97 | // that controls where the click occurs :)
98 |
99 | cy.get('#action-canvas')
100 | .click(80, 75) // click 80px on x coord and 75px on y coord
101 | .click(170, 75)
102 | .click(80, 165)
103 | .click(100, 185)
104 | .click(125, 190)
105 | .click(150, 185)
106 | .click(170, 165)
107 |
108 | // click multiple elements by passing multiple: true
109 | cy.get('.action-labels>.label').click({ multiple: true })
110 |
111 | // Ignore error checking prior to clicking
112 | cy.get('.action-opacity>.btn').click({ force: true })
113 | })
114 |
115 | it('.dblclick() - double click on a DOM element', () => {
116 | // https://on.cypress.io/dblclick
117 |
118 | // Our app has a listener on 'dblclick' event in our 'scripts.js'
119 | // that hides the div and shows an input on double click
120 | cy.get('.action-div').dblclick().should('not.be.visible')
121 | cy.get('.action-input-hidden').should('be.visible')
122 | })
123 |
124 | it('.rightclick() - right click on a DOM element', () => {
125 | // https://on.cypress.io/rightclick
126 |
127 | // Our app has a listener on 'contextmenu' event in our 'scripts.js'
128 | // that hides the div and shows an input on right click
129 | cy.get('.rightclick-action-div').rightclick().should('not.be.visible')
130 | cy.get('.rightclick-action-input-hidden').should('be.visible')
131 | })
132 |
133 | it('.check() - check a checkbox or radio element', () => {
134 | // https://on.cypress.io/check
135 |
136 | // By default, .check() will check all
137 | // matching checkbox or radio elements in succession, one after another
138 | cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]')
139 | .check().should('be.checked')
140 |
141 | cy.get('.action-radios [type="radio"]').not('[disabled]')
142 | .check().should('be.checked')
143 |
144 | // .check() accepts a value argument
145 | cy.get('.action-radios [type="radio"]')
146 | .check('radio1').should('be.checked')
147 |
148 | // .check() accepts an array of values
149 | cy.get('.action-multiple-checkboxes [type="checkbox"]')
150 | .check(['checkbox1', 'checkbox2']).should('be.checked')
151 |
152 | // Ignore error checking prior to checking
153 | cy.get('.action-checkboxes [disabled]')
154 | .check({ force: true }).should('be.checked')
155 |
156 | cy.get('.action-radios [type="radio"]')
157 | .check('radio3', { force: true }).should('be.checked')
158 | })
159 |
160 | it('.uncheck() - uncheck a checkbox element', () => {
161 | // https://on.cypress.io/uncheck
162 |
163 | // By default, .uncheck() will uncheck all matching
164 | // checkbox elements in succession, one after another
165 | cy.get('.action-check [type="checkbox"]')
166 | .not('[disabled]')
167 | .uncheck().should('not.be.checked')
168 |
169 | // .uncheck() accepts a value argument
170 | cy.get('.action-check [type="checkbox"]')
171 | .check('checkbox1')
172 | .uncheck('checkbox1').should('not.be.checked')
173 |
174 | // .uncheck() accepts an array of values
175 | cy.get('.action-check [type="checkbox"]')
176 | .check(['checkbox1', 'checkbox3'])
177 | .uncheck(['checkbox1', 'checkbox3']).should('not.be.checked')
178 |
179 | // Ignore error checking prior to unchecking
180 | cy.get('.action-check [disabled]')
181 | .uncheck({ force: true }).should('not.be.checked')
182 | })
183 |
184 | it('.select() - select an option in a