diff --git a/packages/driver/src/cy/commands/querying.js b/packages/driver/src/cy/commands/querying.js index af78bc946847..18124f7013c7 100644 --- a/packages/driver/src/cy/commands/querying.js +++ b/packages/driver/src/cy/commands/querying.js @@ -1,28 +1,10 @@ const _ = require('lodash') -const $ = require('jquery') const Promise = require('bluebird') const $dom = require('../../dom') const $errUtils = require('../../cypress/error_utils') -const $expr = $.expr[':'] - -const $contains = $expr.contains - -const restoreContains = () => { - return $expr.contains = $contains -} - -const whitespaces = /\s+/g - module.exports = (Commands, Cypress, cy) => { - // restore initially when a run starts - restoreContains() - - // restore before each test and whenever we stop - Cypress.on('test:before:run', restoreContains) - Cypress.on('stop', restoreContains) - Commands.addAll({ focused (options = {}) { _.defaults(options, { @@ -483,46 +465,9 @@ module.exports = (Commands, Cypress, cy) => { options._log.set({ $el }) } - // When multiple space characters are considered as a single whitespace in all tags except
.
-      const normalizeWhitespaces = (elem) => {
-        let testText = elem.textContent || elem.innerText || $.text(elem)
-
-        if (elem.tagName === 'PRE') {
-          return testText
-        }
-
-        return testText.replace(whitespaces, ' ')
-      }
-
-      if (_.isRegExp(text)) {
-        if (options.matchCase === false && !text.flags.includes('i')) {
-          text = new RegExp(text.source, text.flags + 'i') // eslint-disable-line prefer-template
-        }
-
-        // taken from jquery's normal contains method
-        $expr.contains = (elem) => {
-          let testText = normalizeWhitespaces(elem)
-
-          return text.test(testText)
-        }
-      }
-
-      if (_.isString(text)) {
-        $expr.contains = (elem) => {
-          let testText = normalizeWhitespaces(elem)
-
-          if (!options.matchCase) {
-            testText = testText.toLowerCase()
-            text = text.toLowerCase()
-          }
-
-          return testText.includes(text)
-        }
-      }
-
-      // find elements by the :contains psuedo selector
+      // find elements by the :cy-contains psuedo selector
       // and any submit inputs with the attributeContainsWord selector
-      const selector = $dom.getContainsSelector(text, filter)
+      const selector = $dom.getContainsSelector(text, filter, options)
 
       const resolveElements = () => {
         const getOpts = _.extend(_.clone(options), {
@@ -563,11 +508,6 @@ module.exports = (Commands, Cypress, cy) => {
 
       return Promise
       .try(resolveElements)
-      // always restore contains in case
-      // we used a regexp!
-      .finally(() => {
-        restoreContains()
-      })
     },
   })
 
diff --git a/packages/driver/src/dom/elements.ts b/packages/driver/src/dom/elements.ts
index fc1641b6a4d1..904c1936159d 100644
--- a/packages/driver/src/dom/elements.ts
+++ b/packages/driver/src/dom/elements.ts
@@ -945,7 +945,23 @@ const getElements = ($el) => {
   return els
 }
 
-const getContainsSelector = (text, filter = '') => {
+const whitespaces = /\s+/g
+
+// When multiple space characters are considered as a single whitespace in all tags except 
.
+const normalizeWhitespaces = (elem) => {
+  let testText = elem.textContent || elem.innerText || $(elem).text()
+
+  if (elem.tagName === 'PRE') {
+    return testText
+  }
+
+  return testText.replace(whitespaces, ' ')
+}
+const getContainsSelector = (text, filter = '', options: {
+  matchCase?: boolean
+} = {}) => {
+  const $expr = $.expr[':']
+
   const escapedText = $utils.escapeQuotes(text)
 
   // they may have written the filter as
@@ -953,8 +969,41 @@ const getContainsSelector = (text, filter = '') => {
   // https://github.com/cypress-io/cypress/issues/2407
   const filters = filter.trim().split(',')
 
+  let cyContainsSelector
+
+  if (_.isRegExp(text)) {
+    if (options.matchCase === false && !text.flags.includes('i')) {
+      text = new RegExp(text.source, text.flags + 'i') // eslint-disable-line prefer-template
+    }
+
+    // taken from jquery's normal contains method
+    cyContainsSelector = function (elem) {
+      let testText = normalizeWhitespaces(elem)
+
+      return text.test(testText)
+    }
+  } else if (_.isString(text)) {
+    cyContainsSelector = function (elem) {
+      let testText = normalizeWhitespaces(elem)
+
+      if (!options.matchCase) {
+        testText = testText.toLowerCase()
+        text = text.toLowerCase()
+      }
+
+      return testText.includes(text)
+    }
+  } else {
+    cyContainsSelector = $expr.contains
+  }
+
+  // we set the `cy-contains` jquery selector which will only be used
+  // in the context of cy.contains(...) command and selector playground.
+  $expr['cy-contains'] = cyContainsSelector
+
   const selectors = _.map(filters, (filter) => {
-    return `${filter}:not(script,style):contains('${escapedText}'), ${filter}[type='submit'][value~='${escapedText}']`
+    // use custom cy-contains selector that is registered above
+    return `${filter}:not(script,style):cy-contains('${escapedText}'), ${filter}[type='submit'][value~='${escapedText}']`
   })
 
   return selectors.join()
diff --git a/packages/driver/test/cypress/integration/commands/assertions_spec.js b/packages/driver/test/cypress/integration/commands/assertions_spec.js
index 481e4bcc404a..05b8859f3404 100644
--- a/packages/driver/test/cypress/integration/commands/assertions_spec.js
+++ b/packages/driver/test/cypress/integration/commands/assertions_spec.js
@@ -355,6 +355,26 @@ describe('src/cy/commands/assertions', () => {
 
         cy.contains('Nested Find').should('have.length', 2)
       })
+
+      // https://github.com/cypress-io/cypress/issues/6384
+      it('can chain contains assertions off of cy.contains', () => {
+        cy.timeout(100)
+        cy.contains('foo')
+        .should('not.contain', 'asdfasdf')
+
+        cy.contains('foo')
+        .should('contain', 'foo')
+
+        cy.contains(/foo/)
+        .should('not.contain', 'asdfsadf')
+
+        cy.contains(/foo/)
+        .should('contain', 'foo')
+
+        // this isn't valid: .should('contain') does not support regex
+        // cy.contains(/foo/)
+        // .should('contain', /foo/)
+      })
     })
 
     describe('have.class', () => {
diff --git a/packages/driver/test/cypress/plugins/index.js b/packages/driver/test/cypress/plugins/index.js
index ee321ee0af4a..b0a17637cffb 100644
--- a/packages/driver/test/cypress/plugins/index.js
+++ b/packages/driver/test/cypress/plugins/index.js
@@ -7,6 +7,7 @@ const fs = require('fs-extra')
 const Promise = require('bluebird')
 const webpack = require('@cypress/webpack-preprocessor')
 
+process.env.NO_LIVERELOAD = '1'
 const webpackOptions = require('@packages/runner/webpack.config.ts').default
 
 /**
diff --git a/packages/web-config/webpack.config.base.ts b/packages/web-config/webpack.config.base.ts
index be234e278fa0..bc7f7a0e1866 100644
--- a/packages/web-config/webpack.config.base.ts
+++ b/packages/web-config/webpack.config.base.ts
@@ -15,7 +15,7 @@ execa.sync('rebuild-node-sass', { cwd: path.join(__dirname, './node_modules/.bin
 
 const env = process.env.NODE_ENV === 'production' ? 'production' : 'development'
 const args = process.argv.slice(2)
-const liveReloadEnabled = !args.includes('--no-livereload')
+const liveReloadEnabled = !(args.includes('--no-livereload') || process.env.NO_LIVERELOAD)
 const watchModeEnabled = args.includes('--watch') || args.includes('-w')
 
 // opt out of livereload with arg --no-livereload