From c2fb0fbd0afc2ee9f6344a0ec24efa90da79285b Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Thu, 6 Aug 2020 10:55:03 -0400 Subject: [PATCH 1/7] add tests for shadow dom parents with selector Co-authored-by: Zach Panzarino --- .../driver/cypress/fixtures/shadow-dom.html | 7 +- .../integration/commands/traversals_spec.js | 159 +++++++++++++++++- 2 files changed, 156 insertions(+), 10 deletions(-) diff --git a/packages/driver/cypress/fixtures/shadow-dom.html b/packages/driver/cypress/fixtures/shadow-dom.html index ecb3ed20eeef..219f4e94b5a0 100644 --- a/packages/driver/cypress/fixtures/shadow-dom.html +++ b/packages/driver/cypress/fixtures/shadow-dom.html @@ -12,8 +12,8 @@ - - + +
In Shadow Slot
@@ -27,11 +27,10 @@ super() const root = this.attachShadow({ mode: 'open' }) - const para = document.createElement('p') const content = this.getAttribute('content') const className = this.hasAttribute('innerClass') ? this.getAttribute('innerClass') : 'shadow-content' - root.innerHTML = `

${content}

` + root.innerHTML = `

${content}

` } }) } diff --git a/packages/driver/cypress/integration/commands/traversals_spec.js b/packages/driver/cypress/integration/commands/traversals_spec.js index 7d6e8e8a4b27..43ee45258d81 100644 --- a/packages/driver/cypress/integration/commands/traversals_spec.js +++ b/packages/driver/cypress/integration/commands/traversals_spec.js @@ -414,6 +414,17 @@ describe('src/cy/commands/traversals', () => { }) describe('closest', () => { + it('retrieves itself when it is the closest matching element within shadow dom', () => { + cy + .get('#shadow-element-3') + .find('p', { includeShadowDom: true }) + .closest('p') + .then(($parent) => { + expect($parent.length).to.eq(1) + expect($parent[0].className).to.eq('shadow-3') + }) + }) + it('retrieves the closest element beyond shadow boundaries', () => { cy .get('#shadow-element-3') @@ -434,6 +445,39 @@ describe('src/cy/commands/traversals', () => { expect($parent[0].id).to.eq('parent-of-shadow-container-0') }) }) + + it('retrieves closest element when element is the shadow root', () => { + cy + .get('#shadow-element-3') + .find('p', { includeShadowDom: true }) + .closest('#shadow-element-3') + .then(($parent) => { + expect($parent.length).to.eq(1) + expect($parent[0].id).to.eq('shadow-element-3') + }) + }) + + it('retrieves closest element when element is within the same shadow root', () => { + cy + .get('#shadow-element-3') + .find('p', { includeShadowDom: true }) + .closest('div') + .then(($parent) => { + expect($parent.length).to.eq(1) + expect($parent[0].className).to.eq('shadow-div') + }) + }) + + it('retrieves closest element when element is a nested shadow root parent', () => { + cy + .get('#shadow-element-5') + .find('p', { includeShadowDom: true }) + .closest('#shadow-element-4') + .then(($parent) => { + expect($parent.length).to.eq(1) + expect($parent[0].id).to.eq('shadow-element-4') + }) + }) }) describe('parents', () => { @@ -443,12 +487,13 @@ describe('src/cy/commands/traversals', () => { .find('p', { includeShadowDom: true }) .parents() .then(($parents) => { - expect($parents.length).to.eq(5) - expect($parents[0].id).to.eq('shadow-element-3') - expect($parents[1].id).to.eq('parent-of-shadow-container-1') - expect($parents[2].id).to.eq('parent-of-shadow-container-0') - expect($parents[3].nodeName).to.eq('BODY') - expect($parents[4].nodeName).to.eq('HTML') + expect($parents.length).to.eq(6) + expect($parents[0].className).to.eq('shadow-div') + expect($parents[1].id).to.eq('shadow-element-3') + expect($parents[2].id).to.eq('parent-of-shadow-container-1') + expect($parents[3].id).to.eq('parent-of-shadow-container-0') + expect($parents[4].nodeName).to.eq('BODY') + expect($parents[5].nodeName).to.eq('HTML') }) }) @@ -464,6 +509,108 @@ describe('src/cy/commands/traversals', () => { expect($parents[3].nodeName).to.eq('HTML') }) }) + + it('retrieves parents by selector within shadow root', () => { + cy + .get('#shadow-element-3') + .find('p', { includeShadowDom: true }) + .parents('.shadow-div') + .then(($parents) => { + expect($parents.length).to.eq(1) + expect($parents[0].className).to.eq('shadow-div') + }) + }) + + it('retrieves parents by selector within and outside of shadow root', () => { + cy + .get('#shadow-element-3') + .find('p', { includeShadowDom: true }) + .parents('div') + .then(($parents) => { + expect($parents.length).to.eq(4) + expect($parents[0].className).to.eq('shadow-div') + expect($parents[1].id).to.eq('shadow-element-3') + expect($parents[2].id).to.eq('parent-of-shadow-container-1') + expect($parents[3].id).to.eq('parent-of-shadow-container-0') + }) + }) + + it('retrieves parents by selector outside of shadow root', () => { + cy + .get('#shadow-element-3') + .find('p', { includeShadowDom: true }) + .parents('#parent-of-shadow-container-0') + .then(($parents) => { + expect($parents.length).to.eq(1) + expect($parents[0].id).to.eq('parent-of-shadow-container-0') + }) + }) + }) + + describe('parent', () => { + it('retrieves parent within shadow root', () => { + cy + .get('#shadow-element-3') + .find('p', { includeShadowDom: true }) + .parent() + .then(($parents) => { + expect($parents.length).to.eq(1) + expect($parents[0].className).to.eq('shadow-div') + }) + }) + + it('retrieves parent by selector within shadow root', () => { + cy + .get('#shadow-element-3') + .find('p', { includeShadowDom: true }) + .parent('.shadow-div') + .then(($parents) => { + expect($parents.length).to.eq(1) + expect($parents[0].className).to.eq('shadow-div') + }) + }) + + it('retrieves parent when parent is shadow root', () => { + cy + .get('#shadow-element-3') + .find('div', { includeShadowDom: true }) + .parent() + .then(($parents) => { + expect($parents.length).to.eq(1) + expect($parents[0].id).to.eq('shadow-element-3') + }) + }) + + it('retrieves parent by selector when parent is shadow root', () => { + cy + .get('#shadow-element-3') + .find('div', { includeShadowDom: true }) + .parent('#shadow-element-3') + .then(($parents) => { + expect($parents.length).to.eq(1) + expect($parents[0].id).to.eq('shadow-element-3') + }) + }) + + it('retrieves parent when element is shadow root', () => { + cy + .get('#shadow-element-3') + .parent() + .then(($parents) => { + expect($parents.length).to.eq(1) + expect($parents[0].id).to.eq('parent-of-shadow-container-1') + }) + }) + + it('retrieves parent by selector when element is shadow root', () => { + cy + .get('#shadow-element-3') + .parent('#parent-of-shadow-container-1') + .then(($parents) => { + expect($parents.length).to.eq(1) + expect($parents[0].id).to.eq('parent-of-shadow-container-1') + }) + }) }) }) }) From 4e2e933542d1a9145d57ac1f537a89a70d5f6d8d Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Thu, 6 Aug 2020 14:40:49 -0400 Subject: [PATCH 2/7] fix cy.parents() shadow dom implementation --- .../cypress/integration/commands/traversals_spec.js | 9 ++++----- packages/driver/src/cy/commands/traversals.js | 8 ++++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/driver/cypress/integration/commands/traversals_spec.js b/packages/driver/cypress/integration/commands/traversals_spec.js index 43ee45258d81..c1b337d7f0f4 100644 --- a/packages/driver/cypress/integration/commands/traversals_spec.js +++ b/packages/driver/cypress/integration/commands/traversals_spec.js @@ -480,7 +480,7 @@ describe('src/cy/commands/traversals', () => { }) }) - describe('parents', () => { + describe('#parents', () => { it('retrieves all parents, including those beyond shadow boundaries', () => { cy .get('#shadow-element-3') @@ -527,11 +527,10 @@ describe('src/cy/commands/traversals', () => { .find('p', { includeShadowDom: true }) .parents('div') .then(($parents) => { - expect($parents.length).to.eq(4) + expect($parents.length).to.eq(3) expect($parents[0].className).to.eq('shadow-div') - expect($parents[1].id).to.eq('shadow-element-3') - expect($parents[2].id).to.eq('parent-of-shadow-container-1') - expect($parents[3].id).to.eq('parent-of-shadow-container-0') + expect($parents[1].id).to.eq('parent-of-shadow-container-1') + expect($parents[2].id).to.eq('parent-of-shadow-container-0') }) }) diff --git a/packages/driver/src/cy/commands/traversals.js b/packages/driver/src/cy/commands/traversals.js index cfcca6c8aa09..f68cf783e847 100644 --- a/packages/driver/src/cy/commands/traversals.js +++ b/packages/driver/src/cy/commands/traversals.js @@ -20,12 +20,16 @@ const optInShadowTraversals = { } const autoShadowTraversals = { - parents: (cy, $el, arg1) => { + parents: (cy, $el, selector) => { const parents = $el.map((i, el) => { return $elements.getAllParents(el) }) - return cy.$$(parents) + if (!selector) { + return cy.$$(parents) + } + + return cy.$$(parents).filter(selector) }, closest: (cy, $el, arg1) => { const nodes = _.reduce($el, (nodes, el) => { From c52ca01959912f9cbb22efc049b9f9e096c953ca Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Thu, 6 Aug 2020 14:48:40 -0400 Subject: [PATCH 3/7] add support for .parent() in shadow dom --- .../integration/commands/traversals_spec.js | 6 ++--- packages/driver/src/cy/commands/traversals.js | 27 +++++++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/driver/cypress/integration/commands/traversals_spec.js b/packages/driver/cypress/integration/commands/traversals_spec.js index c1b337d7f0f4..08f9a995d41b 100644 --- a/packages/driver/cypress/integration/commands/traversals_spec.js +++ b/packages/driver/cypress/integration/commands/traversals_spec.js @@ -368,7 +368,7 @@ describe('src/cy/commands/traversals', () => { cy.visit('/fixtures/shadow-dom.html') }) - describe('find', () => { + describe('#find', () => { it('retrieves a matching element beyond shadow boundaries', () => { const el = cy.$$('#shadow-element-3')[0].shadowRoot.querySelector('p') @@ -413,7 +413,7 @@ describe('src/cy/commands/traversals', () => { }) }) - describe('closest', () => { + describe('#closest', () => { it('retrieves itself when it is the closest matching element within shadow dom', () => { cy .get('#shadow-element-3') @@ -546,7 +546,7 @@ describe('src/cy/commands/traversals', () => { }) }) - describe('parent', () => { + describe('#parent', () => { it('retrieves parent within shadow root', () => { cy .get('#shadow-element-3') diff --git a/packages/driver/src/cy/commands/traversals.js b/packages/driver/src/cy/commands/traversals.js index f68cf783e847..d80eaf0ae095 100644 --- a/packages/driver/src/cy/commands/traversals.js +++ b/packages/driver/src/cy/commands/traversals.js @@ -20,17 +20,6 @@ const optInShadowTraversals = { } const autoShadowTraversals = { - parents: (cy, $el, selector) => { - const parents = $el.map((i, el) => { - return $elements.getAllParents(el) - }) - - if (!selector) { - return cy.$$(parents) - } - - return cy.$$(parents).filter(selector) - }, closest: (cy, $el, arg1) => { const nodes = _.reduce($el, (nodes, el) => { const getClosest = (node) => { @@ -50,6 +39,22 @@ const autoShadowTraversals = { return cy.$$(nodes) }, + parent: (cy, $el) => { + const parent = $elements.getParent($el[0]) + + return cy.$$(parent) + }, + parents: (cy, $el, selector) => { + const parents = $el.map((i, el) => { + return $elements.getAllParents(el) + }) + + if (!selector) { + return cy.$$(parents) + } + + return cy.$$(parents).filter(selector) + }, } module.exports = (Commands, Cypress, cy) => { From b937d7f74c91d3daf86c12daf492f97cca100be5 Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Thu, 6 Aug 2020 14:49:29 -0400 Subject: [PATCH 4/7] reorder alphabetically --- .../integration/commands/traversals_spec.js | 186 +++++++++--------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/packages/driver/cypress/integration/commands/traversals_spec.js b/packages/driver/cypress/integration/commands/traversals_spec.js index 08f9a995d41b..510f96a8a0e6 100644 --- a/packages/driver/cypress/integration/commands/traversals_spec.js +++ b/packages/driver/cypress/integration/commands/traversals_spec.js @@ -368,51 +368,6 @@ describe('src/cy/commands/traversals', () => { cy.visit('/fixtures/shadow-dom.html') }) - describe('#find', () => { - it('retrieves a matching element beyond shadow boundaries', () => { - const el = cy.$$('#shadow-element-3')[0].shadowRoot.querySelector('p') - - cy - .get('#parent-of-shadow-container-0') - .find('p', { includeShadowDom: true }) - .then(($element) => { - expect($element.length).to.eq(1) - expect($element[0]).to.eq(el) - }) - }) - - it('retrieves a matching element when no shadow roots', () => { - const el = cy.$$('#shadow-element-3')[0] - - cy - .get('#parent-of-shadow-container-0') - .find('#shadow-element-3', { includeShadowDom: true }) - .then(($element) => { - expect($element.length).to.eq(1) - expect($element[0]).to.eq(el) - }) - }) - - it('allows traversal when already within a shadow root', () => { - const el = cy.$$('#shadow-element-3')[0].shadowRoot.querySelector('p') - - cy - .get('#shadow-element-3') - .shadow() - .find('p') - .then(($element) => { - expect($element.length).to.eq(1) - expect($element[0]).to.eq(el) - }) - }) - - // https://github.com/cypress-io/cypress/issues/7676 - it('does not error when querySelectorAll is wrapped and snapshots are off', () => { - cy.visit('/fixtures/shadow-dom.html?wrap-qsa=true') - cy.get('#shadow-element-1').find('.shadow-1', { includeShadowDom: true }) - }) - }) - describe('#closest', () => { it('retrieves itself when it is the closest matching element within shadow dom', () => { cy @@ -480,69 +435,48 @@ describe('src/cy/commands/traversals', () => { }) }) - describe('#parents', () => { - it('retrieves all parents, including those beyond shadow boundaries', () => { + describe('#find', () => { + it('retrieves a matching element beyond shadow boundaries', () => { + const el = cy.$$('#shadow-element-3')[0].shadowRoot.querySelector('p') + cy - .get('#shadow-element-3') + .get('#parent-of-shadow-container-0') .find('p', { includeShadowDom: true }) - .parents() - .then(($parents) => { - expect($parents.length).to.eq(6) - expect($parents[0].className).to.eq('shadow-div') - expect($parents[1].id).to.eq('shadow-element-3') - expect($parents[2].id).to.eq('parent-of-shadow-container-1') - expect($parents[3].id).to.eq('parent-of-shadow-container-0') - expect($parents[4].nodeName).to.eq('BODY') - expect($parents[5].nodeName).to.eq('HTML') + .then(($element) => { + expect($element.length).to.eq(1) + expect($element[0]).to.eq(el) }) }) - it('retrieves parents normally when no shadow roots exist', () => { - cy - .get('#shadow-element-3') - .parents() - .then(($parents) => { - expect($parents.length).to.eq(4) - expect($parents[0].id).to.eq('parent-of-shadow-container-1') - expect($parents[1].id).to.eq('parent-of-shadow-container-0') - expect($parents[2].nodeName).to.eq('BODY') - expect($parents[3].nodeName).to.eq('HTML') - }) - }) + it('retrieves a matching element when no shadow roots', () => { + const el = cy.$$('#shadow-element-3')[0] - it('retrieves parents by selector within shadow root', () => { cy - .get('#shadow-element-3') - .find('p', { includeShadowDom: true }) - .parents('.shadow-div') - .then(($parents) => { - expect($parents.length).to.eq(1) - expect($parents[0].className).to.eq('shadow-div') + .get('#parent-of-shadow-container-0') + .find('#shadow-element-3', { includeShadowDom: true }) + .then(($element) => { + expect($element.length).to.eq(1) + expect($element[0]).to.eq(el) }) }) - it('retrieves parents by selector within and outside of shadow root', () => { + it('allows traversal when already within a shadow root', () => { + const el = cy.$$('#shadow-element-3')[0].shadowRoot.querySelector('p') + cy .get('#shadow-element-3') - .find('p', { includeShadowDom: true }) - .parents('div') - .then(($parents) => { - expect($parents.length).to.eq(3) - expect($parents[0].className).to.eq('shadow-div') - expect($parents[1].id).to.eq('parent-of-shadow-container-1') - expect($parents[2].id).to.eq('parent-of-shadow-container-0') + .shadow() + .find('p') + .then(($element) => { + expect($element.length).to.eq(1) + expect($element[0]).to.eq(el) }) }) - it('retrieves parents by selector outside of shadow root', () => { - cy - .get('#shadow-element-3') - .find('p', { includeShadowDom: true }) - .parents('#parent-of-shadow-container-0') - .then(($parents) => { - expect($parents.length).to.eq(1) - expect($parents[0].id).to.eq('parent-of-shadow-container-0') - }) + // https://github.com/cypress-io/cypress/issues/7676 + it('does not error when querySelectorAll is wrapped and snapshots are off', () => { + cy.visit('/fixtures/shadow-dom.html?wrap-qsa=true') + cy.get('#shadow-element-1').find('.shadow-1', { includeShadowDom: true }) }) }) @@ -611,5 +545,71 @@ describe('src/cy/commands/traversals', () => { }) }) }) + + describe('#parents', () => { + it('retrieves all parents, including those beyond shadow boundaries', () => { + cy + .get('#shadow-element-3') + .find('p', { includeShadowDom: true }) + .parents() + .then(($parents) => { + expect($parents.length).to.eq(6) + expect($parents[0].className).to.eq('shadow-div') + expect($parents[1].id).to.eq('shadow-element-3') + expect($parents[2].id).to.eq('parent-of-shadow-container-1') + expect($parents[3].id).to.eq('parent-of-shadow-container-0') + expect($parents[4].nodeName).to.eq('BODY') + expect($parents[5].nodeName).to.eq('HTML') + }) + }) + + it('retrieves parents normally when no shadow roots exist', () => { + cy + .get('#shadow-element-3') + .parents() + .then(($parents) => { + expect($parents.length).to.eq(4) + expect($parents[0].id).to.eq('parent-of-shadow-container-1') + expect($parents[1].id).to.eq('parent-of-shadow-container-0') + expect($parents[2].nodeName).to.eq('BODY') + expect($parents[3].nodeName).to.eq('HTML') + }) + }) + + it('retrieves parents by selector within shadow root', () => { + cy + .get('#shadow-element-3') + .find('p', { includeShadowDom: true }) + .parents('.shadow-div') + .then(($parents) => { + expect($parents.length).to.eq(1) + expect($parents[0].className).to.eq('shadow-div') + }) + }) + + it('retrieves parents by selector within and outside of shadow root', () => { + cy + .get('#shadow-element-3') + .find('p', { includeShadowDom: true }) + .parents('div') + .then(($parents) => { + expect($parents.length).to.eq(3) + expect($parents[0].className).to.eq('shadow-div') + expect($parents[1].id).to.eq('parent-of-shadow-container-1') + expect($parents[2].id).to.eq('parent-of-shadow-container-0') + }) + }) + + it('retrieves parents by selector outside of shadow root', () => { + cy + .get('#shadow-element-3') + .find('p', { includeShadowDom: true }) + .parents('#parent-of-shadow-container-0') + .then(($parents) => { + expect($parents.length).to.eq(1) + expect($parents[0].id).to.eq('parent-of-shadow-container-0') + }) + }) + }) }) }) From c6e951e6dc924375ab5a3c54cd3d645dcd688d42 Mon Sep 17 00:00:00 2001 From: Chris Breiding Date: Tue, 11 Aug 2020 10:32:06 -0400 Subject: [PATCH 5/7] add support for parentsUntil and multiple elements in subject --- .../driver/cypress/fixtures/shadow-dom.html | 21 +- .../integration/commands/traversals_spec.js | 624 ++++++++++++++++-- packages/driver/src/cy/commands/traversals.js | 38 +- packages/driver/src/dom/elements.ts | 6 +- 4 files changed, 615 insertions(+), 74 deletions(-) diff --git a/packages/driver/cypress/fixtures/shadow-dom.html b/packages/driver/cypress/fixtures/shadow-dom.html index 219f4e94b5a0..c75e753632f8 100644 --- a/packages/driver/cypress/fixtures/shadow-dom.html +++ b/packages/driver/cypress/fixtures/shadow-dom.html @@ -8,7 +8,7 @@
-
+
@@ -19,6 +19,22 @@
In Shadow Slot
+
+
+ +
+
+