Skip to content

Commit

Permalink
feat: support assertions with two values (#112)
Browse files Browse the repository at this point in the history
* feat: support assertions with two values

* bump upload artifact action

* update v11 code

* store screenshots and videos on failure

* updated the tests

* debugging

* fix the last else branch
  • Loading branch information
bahmutov authored Oct 15, 2024
1 parent 40e0e48 commit 0f96a3d
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 19 deletions.
18 changes: 17 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ jobs:
# check if the types agree
build: npm run types

# https://github.com/actions/upload-artifact
- uses: actions/upload-artifact@v4
name: Store any error screenshots 🖼
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshots

- uses: actions/upload-artifact@v4
name: Store any videos 🖼
if: failure()
with:
name: cypress-videos
path: cypress/videos

# there was a breaking change under the hood in Cypress v11.1.0
# so make sure this plugin still works for older versions
test-cypress-v11-0:
Expand Down Expand Up @@ -45,7 +60,8 @@ jobs:
with:
working-directory: cypress-v9

- uses: actions/upload-artifact@v2
# https://github.com/actions/upload-artifact
- uses: actions/upload-artifact@v4
name: Store any v9 screenshots 🖼
if: failure()
with:
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,17 @@ cy.wrap(42).if('not.null') // takes IF path

See spec [null.cy.js](./cypress/e2e/null.cy.js)

## Multiple values

Some assertions need two values, for example:

```js
// only checks the presence of the "data-x" HTML attribute
.if('have.attr', 'data-x')
// checks if the "data-x" attribute present AND has value "123"
.if('have.attr', 'data-x', '123')
```

## raise

This plugin includes a utility custom command `cy.raise` that lets you conveniently throw an error.
Expand Down
1 change: 1 addition & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = defineConfig({
viewportWidth: 200,
viewportHeight: 200,
defaultCommandTimeout: 1000,
video: true,
setupNodeEvents(on, config) {
// implement node event listeners here
on('task', {
Expand Down
58 changes: 58 additions & 0 deletions cypress/e2e/has-attribute.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/// <reference types="cypress" />
// @ts-check

import '../../src'

describe('has attribute assertion', () => {
beforeEach(() => {
cy.visit('cypress/terms.html')
})

it('has attribute present', () => {
cy.get('#submit')
.if('have.attr', 'id')
.log('button has an id')
.else()
.raise(new Error('button should have an id'))
})

it(
'has attribute present after delay',
{ defaultCommandTimeout: 2000 },
() => {
cy.get('#submit').should('have.attr', 'data-x')
cy.get('#submit')
.if('have.attr', 'data-x')
.invoke('attr', 'data-x')
.should('equal', '123')
.else()
.raise(new Error('data-x not found'))
},
)

it(
'has attribute with matching value present after delay',
{ defaultCommandTimeout: 2000 },
() => {
cy.get('#submit').should('have.attr', 'data-x')
cy.get('#submit')
.if('have.attr', 'data-x', '123')
.log('data-X found')
.else()
.raise(new Error('data-x not found'))
},
)

it(
'has attribute with a different value',
{ defaultCommandTimeout: 2000 },
() => {
cy.get('#submit').should('have.attr', 'data-x')
cy.get('#submit')
// the attribute is present, but has a different value
.if('have.attr', 'data-x', '99')
.raise(new Error('data-x has wrong value'))
.else('data-x value is correct')
},
)
})
3 changes: 3 additions & 0 deletions cypress/e2e/terms-and-conditions.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import '../../src'
it('submits the terms forms', () => {
cy.visit('cypress/terms.html')
cy.get('#agreed')
cy.get('#agreed')
.should('be.visible')
.if('not.checked')
.click()
.log('clicked the checkbox')
.else()
.log('The user already agreed')
cy.get('button#submit').click()
Expand Down
5 changes: 5 additions & 0 deletions cypress/terms.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
} else {
console.log('checked = false')
}

// set an attribute after a delay
setTimeout(() => {
document.getElementById('submit').setAttribute('data-X', '123')
}, 1000)
</script>
</body>
</html>
31 changes: 23 additions & 8 deletions src/index-v11.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function getCypressCurrentSubject() {
Cypress.Commands.add(
'if',
{ prevSubject: true },
function (subject, assertion, assertionValue) {
function (subject, assertion, assertionValue1, assertionValue2) {
const cmd = cy.state('current')
debug('if', cmd.attributes, 'subject', subject, 'assertion?', assertion)
debug('next command', cmd.next)
Expand Down Expand Up @@ -86,18 +86,33 @@ Cypress.Commands.add(
const parts = assertion.split('.')
let assertionReduced = expect(subject).to
parts.forEach((assertionPart, k) => {
if (
k === parts.length - 1 &&
typeof assertionValue !== 'undefined'
) {
assertionReduced = assertionReduced[assertionPart](assertionValue)
if (k === parts.length - 1) {
if (
typeof assertionValue1 !== 'undefined' &&
typeof assertionValue2 !== 'undefined'
) {
assertionReduced = assertionReduced[assertionPart](
assertionValue1,
assertionValue2,
)
} else if (typeof assertionValue1 !== 'undefined') {
assertionReduced =
assertionReduced[assertionPart](assertionValue1)
} else {
assertionReduced = assertionReduced[assertionPart]
}
} else {
assertionReduced = assertionReduced[assertionPart]
}
})
} else {
if (typeof assertionValue !== 'undefined') {
expect(subject).to.be[assertion](assertionValue)
if (
typeof assertionValue1 !== 'undefined' &&
typeof assertionValue2 !== 'undefined'
) {
expect(subject).to.be[assertion](assertionValue1, assertionValue2)
} else if (typeof assertionValue1 !== 'undefined') {
expect(subject).to.be[assertion](assertionValue1)
} else {
expect(subject).to.be[assertion]
}
Expand Down
3 changes: 2 additions & 1 deletion src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ declare namespace Cypress {
if(
this: Chainable<Subject>,
assertion?: string,
value?: any,
value1?: any,
value2?: any,
): Chainable<Subject>

/**
Expand Down
32 changes: 23 additions & 9 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ if (major < 12) {
Cypress.Commands.add(
'if',
{ prevSubject: true },
function (subject, assertion, assertionValue) {
function (subject, assertion, assertionValue1, assertionValue2) {
const cmd = cy.state('current')
debug('if', cmd.attributes, 'subject', subject, 'assertion?', assertion)
debug('next command', cmd.next)
Expand Down Expand Up @@ -90,19 +90,33 @@ if (major < 12) {
const parts = assertion.split('.')
let assertionReduced = expect(subject).to
parts.forEach((assertionPart, k) => {
if (
k === parts.length - 1 &&
typeof assertionValue !== 'undefined'
) {
assertionReduced =
assertionReduced[assertionPart](assertionValue)
if (k === parts.length - 1) {
if (
typeof assertionValue1 !== 'undefined' &&
typeof assertionValue2 !== 'undefined'
) {
assertionReduced = assertionReduced[assertionPart](
assertionValue1,
assertionValue2,
)
} else if (typeof assertionValue1 !== 'undefined') {
assertionReduced =
assertionReduced[assertionPart](assertionValue1)
} else {
assertionReduced = assertionReduced[assertionPart]
}
} else {
assertionReduced = assertionReduced[assertionPart]
}
})
} else {
if (typeof assertionValue !== 'undefined') {
expect(subject).to.be[assertion](assertionValue)
if (
typeof assertionValue1 !== 'undefined' &&
typeof assertionValue2 !== 'undefined'
) {
expect(subject).to.be[assertion](assertionValue1, assertionValue2)
} else if (typeof assertionValue1 !== 'undefined') {
expect(subject).to.be[assertion](assertionValue1)
} else {
expect(subject).to.be[assertion]
}
Expand Down

0 comments on commit 0f96a3d

Please sign in to comment.