From f0a90e1c250cc7570a554c68c7e8343921ba8321 Mon Sep 17 00:00:00 2001 From: KHeo Date: Mon, 21 Mar 2022 11:14:00 +0900 Subject: [PATCH 1/5] fix: handle backslash in `should('contain')`. --- .../integration/commands/assertions_spec.js | 39 +++++++++++++++++++ packages/driver/src/cy/chai.ts | 11 +++++- packages/driver/src/cypress/utils.ts | 6 +++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/packages/driver/cypress/integration/commands/assertions_spec.js b/packages/driver/cypress/integration/commands/assertions_spec.js index 86429d1dad34..c8fa82d1e032 100644 --- a/packages/driver/cypress/integration/commands/assertions_spec.js +++ b/packages/driver/cypress/integration/commands/assertions_spec.js @@ -1277,6 +1277,36 @@ describe('src/cy/commands/assertions', () => { }) }) + // TODO: this suite should be merged with the suite above + describe('message formatting', () => { + const expectMarkdown = (test, message, done) => { + cy.then(() => { + test() + }) + + cy.on('log:added', (attrs, log) => { + if (attrs.name === 'assert') { + cy.removeAllListeners('log:added') + + expect(log.get('message')).to.eq(message) + + done() + } + }) + } + + // https://github.com/cypress-io/cypress/issues/19116 + it('text with backslashes', (done) => { + const text = '" expect(text).to.equal(text), + `expected **" { beforeEach(function () { this.$body = cy.$$('body') @@ -1327,6 +1357,15 @@ describe('src/cy/commands/assertions', () => { cy.get('#escape-quotes').should('contain', 'shouldn\'t') }) + + // https://github.com/cypress-io/cypress/issues/19116 + it('escapes backslashes', () => { + const $span = '"<OE_D]dQ\\' + + cy.$$($span).appendTo(cy.$$('body')) + + cy.get('#escape-backslashes').should('contain', '" { diff --git a/packages/driver/src/cy/chai.ts b/packages/driver/src/cy/chai.ts index 57bf42c49a6c..a3ceeb3cb6d9 100644 --- a/packages/driver/src/cy/chai.ts +++ b/packages/driver/src/cy/chai.ts @@ -29,6 +29,8 @@ const trailingWhitespaces = /\s*'\*\*/g const whitespace = /\s/g const valueHasLeadingOrTrailingWhitespaces = /\*\*'\s+|\s+'\*\*/g const imageMarkdown = /!\[.*?\]\(.*?\)/g +const doubleslashRe = /\\\\/g +const escapedDoubleslashRe = /__doulbe_slash__/g type CreateFunc = ((specWindow, state, assertFn) => ({ chai: Chai.ChaiStatic @@ -103,6 +105,9 @@ chai.use((chai, u) => { return }) + const escapeDoubleSlash = (str: string) => str.replace(doubleslashRe, '__doulbe_slash__') + const restoreDoubleSlash = (str: string) => str.replace(escapedDoubleslashRe, '\\\\') + // remove any single quotes between our **, // except escaped quotes, empty strings and number strings. const removeOrKeepSingleQuotesBetweenStars = (message) => { @@ -277,7 +282,9 @@ chai.use((chai, u) => { return _super.apply(this, arguments) } - const escText = $utils.escapeQuotes(text) + const escText = $utils.escapeQuotes( + $utils.escapeBackslashes(text), + ) const selector = `:contains('${escText}'), [type='submit'][value~='${escText}']` @@ -457,7 +464,9 @@ chai.use((chai, u) => { let message = chaiUtils.getMessage(this, customArgs as Chai.AssertionArgs) const actual = chaiUtils.getActual(this, customArgs as Chai.AssertionArgs) + message = escapeDoubleSlash(message) message = removeOrKeepSingleQuotesBetweenStars(message) + message = restoreDoubleSlash(message) message = escapeMarkdown(message) try { diff --git a/packages/driver/src/cypress/utils.ts b/packages/driver/src/cypress/utils.ts index 378307718790..35fa30003e82 100644 --- a/packages/driver/src/cypress/utils.ts +++ b/packages/driver/src/cypress/utils.ts @@ -11,6 +11,7 @@ import { $Location } from './location' const tagOpen = /\[([a-z\s='"-]+)\]/g const tagClosed = /\[\/([a-z]+)\]/g const quotesRe = /('|")/g +const backslashRe = /\\/g const defaultOptions = { delay: 10, @@ -273,6 +274,11 @@ export default { return (`${text}`).replace(quotesRe, '\\$1') }, + escapeBackslashes (text) { + // convert to str and escape any backslashes + return (`${text}`).replace(backslashRe, '\\\\') + }, + normalizeNumber (num) { const parsed = Number(num) From e4f78a082ffd36efb87a9395186735627a3a0c92 Mon Sep 17 00:00:00 2001 From: KHeo Date: Mon, 21 Mar 2022 11:28:18 +0900 Subject: [PATCH 2/5] move escape functions to its own file. remove redundancy. --- packages/driver/src/cy/chai.ts | 5 +++-- packages/driver/src/cypress/utils.ts | 13 ------------- packages/driver/src/dom/elements/find.ts | 3 ++- packages/driver/src/dom/elements/utils.ts | 7 ------- packages/driver/src/util/escape.ts | 13 +++++++++++++ 5 files changed, 18 insertions(+), 23 deletions(-) create mode 100644 packages/driver/src/util/escape.ts diff --git a/packages/driver/src/cy/chai.ts b/packages/driver/src/cy/chai.ts index a3ceeb3cb6d9..2cc8399e5fef 100644 --- a/packages/driver/src/cy/chai.ts +++ b/packages/driver/src/cy/chai.ts @@ -8,6 +8,7 @@ import sinonChai from '@cypress/sinon-chai' import $dom from '../dom' import $utils from '../cypress/utils' +import { escapeBackslashes, escapeQuotes } from '../util/escape' import $errUtils from '../cypress/error_utils' import $stackUtils from '../cypress/stack_utils' import $chaiJquery from '../cypress/chai_jquery' @@ -282,8 +283,8 @@ chai.use((chai, u) => { return _super.apply(this, arguments) } - const escText = $utils.escapeQuotes( - $utils.escapeBackslashes(text), + const escText = escapeQuotes( + escapeBackslashes(text), ) const selector = `:contains('${escText}'), [type='submit'][value~='${escText}']` diff --git a/packages/driver/src/cypress/utils.ts b/packages/driver/src/cypress/utils.ts index 35fa30003e82..ef4f7d0f576e 100644 --- a/packages/driver/src/cypress/utils.ts +++ b/packages/driver/src/cypress/utils.ts @@ -10,8 +10,6 @@ import { $Location } from './location' const tagOpen = /\[([a-z\s='"-]+)\]/g const tagClosed = /\[\/([a-z]+)\]/g -const quotesRe = /('|")/g -const backslashRe = /\\/g const defaultOptions = { delay: 10, @@ -268,17 +266,6 @@ export default { } }, - escapeQuotes (text) { - // convert to str and escape any single - // or double quotes - return (`${text}`).replace(quotesRe, '\\$1') - }, - - escapeBackslashes (text) { - // convert to str and escape any backslashes - return (`${text}`).replace(backslashRe, '\\\\') - }, - normalizeNumber (num) { const parsed = Number(num) diff --git a/packages/driver/src/dom/elements/find.ts b/packages/driver/src/dom/elements/find.ts index 6669a9190111..04eb6570ff2c 100644 --- a/packages/driver/src/dom/elements/find.ts +++ b/packages/driver/src/dom/elements/find.ts @@ -4,7 +4,8 @@ import $document from '../document' import $jquery from '../jquery' import { getTagName } from './elementHelpers' import { isWithinShadowRoot, getShadowElementFromPoint } from './shadow' -import { normalizeWhitespaces, escapeQuotes } from './utils' +import { normalizeWhitespaces } from './utils' +import { escapeQuotes } from '../../util/escape' /** * Find Parents relative to an initial element diff --git a/packages/driver/src/dom/elements/utils.ts b/packages/driver/src/dom/elements/utils.ts index c319dcceab1a..66238c2b0d33 100644 --- a/packages/driver/src/dom/elements/utils.ts +++ b/packages/driver/src/dom/elements/utils.ts @@ -5,7 +5,6 @@ import $window from '../window' import $document from '../document' const whitespaces = /\s+/g -const quotesRe = /('|")/g // When multiple space characters are considered as a single whitespace in all tags except
.
 export const normalizeWhitespaces = (elem) => {
@@ -29,12 +28,6 @@ export const isSelector = ($el: JQuery, selector) => {
   return $el.is(selector)
 }
 
-export function escapeQuotes (text) {
-  // convert to str and escape any single
-  // or double quotes
-  return (`${text}`).replace(quotesRe, '\\$1')
-}
-
 export function switchCase (value, casesObj, defaultKey = 'default') {
   if (_.has(casesObj, value)) {
     return _.result(casesObj, value)
diff --git a/packages/driver/src/util/escape.ts b/packages/driver/src/util/escape.ts
new file mode 100644
index 000000000000..ae6633f5a0fc
--- /dev/null
+++ b/packages/driver/src/util/escape.ts
@@ -0,0 +1,13 @@
+const quotesRe = /('|")/g
+const backslashRe = /\\/g
+
+export function escapeQuotes (text) {
+  // convert to str and escape any single
+  // or double quotes
+  return (`${text}`).replace(quotesRe, '\\$1')
+}
+
+export function escapeBackslashes (text) {
+  // convert to str and escape any backslashes
+  return (`${text}`).replace(backslashRe, '\\\\')
+}

From 44161a97c589ed66d8656ad9ff537324d4b86e6c Mon Sep 17 00:00:00 2001
From: KHeo 
Date: Mon, 21 Mar 2022 11:30:17 +0900
Subject: [PATCH 3/5] fix cy.contains.

---
 packages/driver/cypress/fixtures/dom.html                   | 2 ++
 .../cypress/integration/commands/querying/querying_spec.js  | 5 +++++
 packages/driver/src/dom/elements/find.ts                    | 6 ++++--
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/packages/driver/cypress/fixtures/dom.html b/packages/driver/cypress/fixtures/dom.html
index 762e7fa1a18b..3d29f9b6684a 100644
--- a/packages/driver/cypress/fixtures/dom.html
+++ b/packages/driver/cypress/fixtures/dom.html
@@ -160,6 +160,8 @@
 
       
 
+      
"<OE_D]dQ\
+
New York
diff --git a/packages/driver/cypress/integration/commands/querying/querying_spec.js b/packages/driver/cypress/integration/commands/querying/querying_spec.js index 151198bccd5e..b3061b75be98 100644 --- a/packages/driver/cypress/integration/commands/querying/querying_spec.js +++ b/packages/driver/cypress/integration/commands/querying/querying_spec.js @@ -1224,6 +1224,11 @@ describe('src/cy/commands/querying', () => { cy.contains(/\'/) }) + // https://github.com/cypress-io/cypress/issues/19116 + it('handles backslashes', () => { + cy.get('#backslashes').contains('" { it('returns null when no content exists', () => { cy.contains('alksjdflkasjdflkajsdf').should('not.exist').then(($el) => { diff --git a/packages/driver/src/dom/elements/find.ts b/packages/driver/src/dom/elements/find.ts index 04eb6570ff2c..8ac5466aba1b 100644 --- a/packages/driver/src/dom/elements/find.ts +++ b/packages/driver/src/dom/elements/find.ts @@ -5,7 +5,7 @@ import $jquery from '../jquery' import { getTagName } from './elementHelpers' import { isWithinShadowRoot, getShadowElementFromPoint } from './shadow' import { normalizeWhitespaces } from './utils' -import { escapeQuotes } from '../../util/escape' +import { escapeQuotes, escapeBackslashes } from '../../util/escape' /** * Find Parents relative to an initial element @@ -229,7 +229,9 @@ export const getContainsSelector = (text, filter = '', options: { } = {}) => { const $expr = $.expr[':'] - const escapedText = escapeQuotes(text) + const escapedText = escapeQuotes( + escapeBackslashes(text), + ) // they may have written the filter as // comma separated dom els, so we want to search all From 86ade70847c0551110f0b218eb88c9c481e6aaf3 Mon Sep 17 00:00:00 2001 From: KHeo Date: Mon, 21 Mar 2022 12:26:59 +0900 Subject: [PATCH 4/5] fix failure. --- packages/driver/cypress/fixtures/dom.html | 2 -- .../cypress/integration/commands/querying/querying_spec.js | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/driver/cypress/fixtures/dom.html b/packages/driver/cypress/fixtures/dom.html index 3d29f9b6684a..762e7fa1a18b 100644 --- a/packages/driver/cypress/fixtures/dom.html +++ b/packages/driver/cypress/fixtures/dom.html @@ -160,8 +160,6 @@
-
"<OE_D]dQ\
-
New York
diff --git a/packages/driver/cypress/integration/commands/querying/querying_spec.js b/packages/driver/cypress/integration/commands/querying/querying_spec.js index b3061b75be98..39b7050eb60c 100644 --- a/packages/driver/cypress/integration/commands/querying/querying_spec.js +++ b/packages/driver/cypress/integration/commands/querying/querying_spec.js @@ -1226,6 +1226,7 @@ describe('src/cy/commands/querying', () => { // https://github.com/cypress-io/cypress/issues/19116 it('handles backslashes', () => { + $('
"<OE_D]dQ\\
').appendTo(cy.$$('body')) cy.get('#backslashes').contains('" Date: Thu, 21 Apr 2022 14:49:16 +0900 Subject: [PATCH 5/5] Fix typo. --- packages/driver/src/cy/chai.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/driver/src/cy/chai.ts b/packages/driver/src/cy/chai.ts index 2cc8399e5fef..49fdd466e851 100644 --- a/packages/driver/src/cy/chai.ts +++ b/packages/driver/src/cy/chai.ts @@ -31,7 +31,7 @@ const whitespace = /\s/g const valueHasLeadingOrTrailingWhitespaces = /\*\*'\s+|\s+'\*\*/g const imageMarkdown = /!\[.*?\]\(.*?\)/g const doubleslashRe = /\\\\/g -const escapedDoubleslashRe = /__doulbe_slash__/g +const escapedDoubleslashRe = /__double_slash__/g type CreateFunc = ((specWindow, state, assertFn) => ({ chai: Chai.ChaiStatic @@ -106,7 +106,7 @@ chai.use((chai, u) => { return }) - const escapeDoubleSlash = (str: string) => str.replace(doubleslashRe, '__doulbe_slash__') + const escapeDoubleSlash = (str: string) => str.replace(doubleslashRe, '__double_slash__') const restoreDoubleSlash = (str: string) => str.replace(escapedDoubleslashRe, '\\\\') // remove any single quotes between our **,