Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: cy.contains() ignores <style> and <script> without removing them. #19424

Merged
merged 8 commits into from
Dec 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/driver/cypress/fixtures/content-in-body.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,25 @@ <h1>Script and Style in the body</h1>
h1 { font-size: 32px }
</style>
</div>

<!--
// https://github.com/cypress-io/cypress/issues/19377
Below is added to ensure `style`, `script` tags are not removed by `cy.contains()` command.
-->
<p>Hello</p>
<button id="my_button_1" style="display: none;">I'm a button</button>
<button id="my_button_2">Click me</button>
<div id="result"></div>
<style>
button#my_button_1 {
background-color: red;
display: inline !important;
}
</style>
<script id="my_script">
document.getElementById('my_button_2').addEventListener('click', function() {
document.getElementById('result').innerHTML = 'This is the result';
});
</script>
</body>
</html>
30 changes: 30 additions & 0 deletions packages/driver/cypress/integration/commands/querying_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1955,6 +1955,7 @@ space
})
})

// https://github.com/cypress-io/cypress/issues/14861
describe('ignores style and script tag in body', () => {
it('style', (done) => {
cy.on('fail', (err) => {
Expand All @@ -1977,6 +1978,35 @@ space
cy.visit('fixtures/content-in-body.html')
cy.contains('I am in the script tag in body', { timeout: 500 })
})

// https://github.com/cypress-io/cypress/issues/19377
describe('cy.contains() does not remove <style> and <script> tags', () => {
it('cy.contains() does not remove style tags from the DOM', () => {
cy.visit('fixtures/content-in-body.html')

cy.get('button#my_button_1').should('be.visible')
cy.contains('Hello').should('be.visible')
cy.get('button#my_button_1').should('be.visible')
})

it('cy.contains() does not remove script tags from the DOM', () => {
cy.visit('fixtures/content-in-body.html')

cy.window().then((win) => {
const scriptElement = win.document.getElementById('my_script')

expect(scriptElement?.id).to.equal('my_script')
})

cy.get('button#my_button_2').click()
cy.contains('This is the result').should('be.visible')
cy.window().then((win) => {
const scriptElement = win.document.getElementById('my_script')

expect(scriptElement?.id).to.equal('my_script')
})
})
})
})

describe('subject contains text nodes', () => {
Expand Down
30 changes: 10 additions & 20 deletions packages/driver/src/dom/elements/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import $document from '../document'
import $jquery from '../jquery'
import { getTagName } from './elementHelpers'
import { isWithinShadowRoot, getShadowElementFromPoint } from './shadow'
import { normalizeWhitespaces, escapeQuotes, isSelector } from './utils'
import { normalizeWhitespaces, escapeQuotes } from './utils'

/**
* Find Parents relative to an initial element
Expand Down Expand Up @@ -125,10 +125,16 @@ export const getFirstDeepestElement = ($el: JQuery, index = 0) => {
const $current = $el.slice(index, index + 1)
const $next = $el.slice(index + 1, index + 2)

if (!$next) {
if (!$next || $current.length === 0) {
return $current
}

// https://github.com/cypress-io/cypress/issues/14861
// filter out the <script> and <style> tags
if ($current && ['SCRIPT', 'STYLE'].includes($current.prop('tagName'))) {
return getFirstDeepestElement($el, index + 1)
}

// does current contain next?
if ($.contains($current.get(0), $next.get(0))) {
return getFirstDeepestElement($el, index + 1)
Expand Down Expand Up @@ -217,20 +223,6 @@ export const getElements = ($el) => {
return els
}

// Remove <style> and <script> elements inside <body>. Even though the contains
// selector avoids selecting them with :not(script,style), it will find the
// text anyway and attribute it to the <body>
// https://github.com/cypress-io/cypress/issues/14861
const removeScriptAndStyleElements = (elem) => {
const $elem = $(elem)

if (!isSelector($elem, 'body')) return elem

$elem.find('script,style').remove()

return $elem[0]
}

export const getContainsSelector = (text, filter = '', options: {
matchCase?: boolean
} = {}) => {
Expand All @@ -252,14 +244,12 @@ export const getContainsSelector = (text, filter = '', options: {

// taken from jquery's normal contains method
cyContainsSelector = function (elem) {
elem = removeScriptAndStyleElements(elem)
let testText = normalizeWhitespaces(elem)
const testText = normalizeWhitespaces(elem)

return text.test(testText)
}
} else if (_.isString(text)) {
cyContainsSelector = function (elem) {
elem = removeScriptAndStyleElements(elem)
let testText = normalizeWhitespaces(elem)

if (!options.matchCase) {
Expand All @@ -284,7 +274,7 @@ export const getContainsSelector = (text, filter = '', options: {
const textToFind = escapedText.includes(`\'`) ? `"${escapedText}"` : `'${escapedText}'`

// use custom cy-contains selector that is registered above
return `${filter}:not(script,style):cy-contains(${textToFind}), ${filter}[type='submit'][value~=${textToFind}]`
return `${filter}:cy-contains(${textToFind}), ${filter}[type='submit'][value~=${textToFind}]`
})

return selectors.join()
Expand Down