cast numbers to strings when accessing assertions which always yield string subjects (#7315)

Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
This commit is contained in:
Brian Mann
2020-05-18 22:35:17 -04:00
committed by GitHub
parent 0dbd6304c8
commit b43fd1079a
4 changed files with 103 additions and 17 deletions

View File

@@ -13,7 +13,7 @@ const selectors = {
focused: 'focused',
}
const attrs = {
const accessors = {
attr: 'attribute',
css: 'CSS property',
prop: 'property',
@@ -25,6 +25,12 @@ const attrs = {
// the methods on it
const wrap = (ctx) => $(ctx._obj)
const maybeCastNumberToString = (num) => {
// if this is a finite number (no Infinity or NaN)
// cast to a string
return _.isFinite(num) ? `${num}` : num
}
const $chaiJquery = (chai, chaiUtils, callbacks = {}) => {
const { inspect, flag } = chaiUtils
@@ -111,6 +117,8 @@ const $chaiJquery = (chai, chaiUtils, callbacks = {}) => {
})
chai.Assertion.addMethod('id', function (id) {
id = maybeCastNumberToString(id)
return assert(
this,
'id',
@@ -145,6 +153,8 @@ const $chaiJquery = (chai, chaiUtils, callbacks = {}) => {
})
chai.Assertion.addMethod('text', function (text) {
text = maybeCastNumberToString(text)
assertDom(
this,
'text',
@@ -168,6 +178,8 @@ const $chaiJquery = (chai, chaiUtils, callbacks = {}) => {
})
chai.Assertion.addMethod('value', function (value) {
value = maybeCastNumberToString(value)
assertDom(
this,
'value',
@@ -250,23 +262,23 @@ const $chaiJquery = (chai, chaiUtils, callbacks = {}) => {
})
})
_.each(attrs, (description, attr) => {
return chai.Assertion.addMethod(attr, function (name, val) {
_.each(accessors, (description, accessor) => {
return chai.Assertion.addMethod(accessor, function (name, val) {
assertDom(
this,
attr,
accessor,
`expected #{this} to have ${description} #{exp}`,
`expected #{this} not to have ${description} #{exp}`,
name,
)
const actual = wrap(this)[attr](name)
const actual = wrap(this)[accessor](name)
// when we only have 1 argument dont worry about val
if (arguments.length === 1) {
assert(
this,
attr,
accessor,
actual !== undefined,
`expected #{this} to have ${description} #{exp}`,
`expected #{this} not to have ${description} #{exp}`,
@@ -276,7 +288,7 @@ const $chaiJquery = (chai, chaiUtils, callbacks = {}) => {
// change the subject
this._obj = actual
} else {
// if we don't have an attribute here at all we need to
// if we don't have an accessor here at all we need to
// have a different failure message
let message; let negatedMessage
@@ -290,9 +302,17 @@ const $chaiJquery = (chai, chaiUtils, callbacks = {}) => {
negatedMessage = `expected \#{this} not to have ${description} ${inspect(name)} with the value \#{exp}, but the value was \#{act}`
}
// only cast .attr() as a string
// since prop stores the native javascript type
// and we don't want to optimistically cast those
// values as a string
if (accessor === 'attr') {
val = maybeCastNumberToString(val)
}
assert(
this,
attr,
accessor,
(actual != null) && (actual === val),
message,
negatedMessage,

View File

@@ -7,7 +7,7 @@
<body>
<span id="foo">foo<span>
<div>div</div>
<input />
<input id="input" />
<button>button</button>
<ul id="list">
<li>li 0</li>
@@ -16,6 +16,12 @@
<div>
Nested Find
</div>
<span id="number" class="999">123</span>
<span id="456">span with id=number</span>
<span id="data-number" data-number='222'>span with data-number=number</span>
<input id="value-number" value=123 />
<span id="attr-number" num="777">span with attr=number</span>
<span id="prop-number">span with prop=number</span>
<!-- cy.visit() fails when element with id="jquery" exists
// https://github.com/cypress-io/cypress/issues/6193 -->
<p id="jquery">jQuery</p>

View File

@@ -1316,6 +1316,14 @@ describe('src/cy/commands/assertions', () => {
expect(this.logs.length).to.eq(8)
})
// https://github.com/cypress-io/cypress/issues/7314
it('supports a number argument', () => {
cy.get('#data-number').then(($el) => {
expect($el).to.have.data('number', 222)
expect($el).not.to.have.data('number', '222')
})
})
it('throws when obj is not DOM', function (done) {
cy.on('fail', (err) => {
expect(this.logs.length).to.eq(1)
@@ -1357,6 +1365,14 @@ describe('src/cy/commands/assertions', () => {
)
})
// https://github.com/cypress-io/cypress/issues/7314
it('supports a number argument', () => {
cy.get('.999').then(($el) => {
expect($el).to.have.class(999)
expect($el).to.have.class('999')
})
})
it('throws when obj is not DOM', function (done) {
cy.on('fail', (err) => {
expect(this.logs.length).to.eq(1)
@@ -1428,6 +1444,14 @@ describe('src/cy/commands/assertions', () => {
)
})
// https://github.com/cypress-io/cypress/issues/7314
it('supports a number argument', () => {
cy.get('#456').then(($el) => {
expect($el).to.have.id(456)
expect($el).to.have.id('456')
})
})
it('throws when obj is not DOM', function (done) {
cy.on('fail', (err) => {
expect(this.logs.length).to.eq(1)
@@ -1581,6 +1605,13 @@ describe('src/cy/commands/assertions', () => {
}
})
// https://github.com/cypress-io/cypress/issues/7314
it('supports a number argument', () => {
cy.get('#number').then(($el) => {
expect($el).to.have.text(123)
})
})
it('partial match', function () {
expect(this.$div).to.have.text('foo')
expect(this.$div).to.contain.text('o')
@@ -1663,6 +1694,14 @@ describe('src/cy/commands/assertions', () => {
}
})
// https://github.com/cypress-io/cypress/issues/7314
it('supports a number argument', () => {
cy.get('#value-number').then(($el) => {
expect($el).to.have.value(123)
expect($el).to.have.value('123')
})
})
it('throws when obj is not DOM', function (done) {
cy.on('fail', (err) => {
expect(this.logs.length).to.eq(1)
@@ -2431,6 +2470,14 @@ describe('src/cy/commands/assertions', () => {
)
})
// https://github.com/cypress-io/cypress/issues/7314
it('supports a number argument', () => {
cy.get('#attr-number').then(($el) => {
expect($el).to.have.attr('num', 777)
expect($el).to.have.attr('num', '777')
})
})
it('throws when obj is not DOM', function (done) {
cy.on('fail', (err) => {
expect(this.logs.length).to.eq(1)
@@ -2534,6 +2581,19 @@ describe('src/cy/commands/assertions', () => {
)
})
// https://github.com/cypress-io/cypress/issues/7314
it('supports a number argument', () => {
cy.get('#prop-number').then(($el) => {
$el.prop('foo', 444)
$el.prop('bar', '333')
expect($el).to.have.prop('foo', 444)
expect($el).not.to.have.prop('foo', '444')
expect($el).not.to.have.prop('bar', 333)
expect($el).to.have.prop('bar', '333')
})
})
it('throws when obj is not DOM', function (done) {
cy.on('fail', (err) => {
expect(this.logs.length).to.eq(1)

View File

@@ -7,7 +7,7 @@ describe('keyboard', () => {
it('fires keyboard and click events with modifier', () => {
cy
.window().then((win) => {
win.$('input').one('keyup', (e) => {
win.$('#input').one('keyup', (e) => {
expect(e.ctrlKey).to.be.true
expect(e.which).to.equal(83)
@@ -17,7 +17,7 @@ describe('keyboard', () => {
expect(e.ctrlKey).to.be.true
})
cy.get('input').type('{ctrl}s', { release: false })
cy.get('#input').type('{ctrl}s', { release: false })
cy.get('button').click()
})
@@ -26,11 +26,11 @@ describe('keyboard', () => {
it('releases modifiers between tests', () => {
cy
.window().then((win) => {
win.$('input').one('keyup', (e) => {
win.$('#input').one('keyup', (e) => {
expect(e.ctrlKey).to.be.false
})
cy.get('input').type('s')
cy.get('#input').type('s')
})
})
@@ -48,28 +48,28 @@ describe('keyboard', () => {
characters.forEach(([char, asciiCode, keyCode]) => {
it(`for ${char}`, () => {
cy.window().then((win) => {
win.$('input').one('keydown', (e) => {
win.$('#input').one('keydown', (e) => {
expect(e.charCode).to.equal(0)
expect(e.which).to.equal(keyCode)
expect(e.keyCode).to.equal(keyCode)
})
win.$('input').one('keypress', (e) => {
win.$('#input').one('keypress', (e) => {
expect(e.charCode).to.equal(asciiCode)
expect(e.which).to.equal(asciiCode)
expect(e.keyCode).to.equal(asciiCode)
})
win.$('input').one('keyup', (e) => {
win.$('#input').one('keyup', (e) => {
expect(e.charCode).to.equal(0)
expect(e.which).to.equal(keyCode)
expect(e.keyCode).to.equal(keyCode)
})
cy.get('input').type(char)
cy.get('#input').type(char)
})
})
})