Skip to content

Commit

Permalink
fix: supported switching to iframes that are places in a Shadow Root (#…
Browse files Browse the repository at this point in the history
…7139)

* test: added test Should switch context between a shadow iframe and the main window

* fix: fixed function findIframeByWindow

* test: fixed tests

* refactor: deleted unnecessary condition

* test: fixed test

* test: added test Should switch context between a nested shadow iframe and the main window

* refactor: refactored findIframeByWindow

* test: fixed tests
  • Loading branch information
Aleksey28 committed Jul 20, 2022
1 parent 10eef98 commit bc8bac1
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 8 deletions.
13 changes: 11 additions & 2 deletions src/client/core/utils/dom.js
Expand Up @@ -10,6 +10,7 @@ const getElementStyleProperty = hammerhead.utils.style.get;

export const getActiveElement = hammerhead.utils.dom.getActiveElement;
export const findDocument = hammerhead.utils.dom.findDocument;
export const find = hammerhead.utils.dom.find;
export const isElementInDocument = hammerhead.utils.dom.isElementInDocument;
export const isElementInIframe = hammerhead.utils.dom.isElementInIframe;
export const getIframeByElement = hammerhead.utils.dom.getIframeByElement;
Expand Down Expand Up @@ -390,8 +391,16 @@ export function isTopWindow (win) {
}
}

export function findIframeByWindow (iframeWindow, iframeDestinationWindow) {
const iframes = (iframeDestinationWindow || window).document.getElementsByTagName('iframe');
export function findIframeByWindow (iframeWindow) {
const iframes = [];

find(document, '*', elem => {
if (elem.tagName === 'IFRAME')
iframes.push(elem);

if (elem.shadowRoot)
find(elem.shadowRoot, 'iframe', iframe => iframes.push(iframe));
});

for (let i = 0; i < iframes.length; i++) {
if (nativeMethods.contentWindowGetter.call(iframes[i]) === iframeWindow)
Expand Down
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Iframe</title>
</head>
<body>
<button id="btn">button</button>

<div id="shadow-element"></div>

<script>
const shadowElement = document.getElementById('shadow-element');

shadowElement.attachShadow({mode: 'open'});

const shadowRoot = shadowElement.shadowRoot;
const shadowIframe = document.createElement('iframe');

shadowIframe.src = 'nested-iframe.html';
shadowIframe.width = '500px';
shadowIframe.height = '300px';

shadowRoot.appendChild(shadowIframe);

document.getElementById('btn').addEventListener('click', function () {
window.top.iframeBtnClickCount = (window.top.iframeBtnClickCount || 0) + 1;
});
</script>
</body>
</html>
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button id="btn">button</button>

<div id="shadow-element"></div>

<script>
const shadowElement = document.getElementById('shadow-element');

shadowElement.attachShadow({mode: 'open'});

const shadowRoot = shadowElement.shadowRoot;
const shadowIframe = document.createElement('iframe');

shadowIframe.src = 'shadow-iframe.html';
shadowIframe.width = '500px';
shadowIframe.height = '300px';

shadowRoot.appendChild(shadowIframe);

document.getElementById('btn').addEventListener('click', function () {
window.btnClickCount = (window.btnClickCount || 0) + 1;
});
</script>
</body>
</html>
26 changes: 20 additions & 6 deletions test/functional/fixtures/api/es-next/iframe-switching/test.js
Expand Up @@ -23,6 +23,20 @@ describe('[API] t.switchToIframe(), t.switchToMainWindow()', function () {
return runTests('./testcafe-fixtures/iframe-switching-test.js', 'Click on element in a nested iframe', DEFAULT_RUN_OPTIONS);
});

it('Should switch context between a shadow iframe and the main window', function () {
return runTests('./testcafe-fixtures/iframe-switching-test.js', 'Click on an element in a shadow iframe and return to the main window', {
...DEFAULT_RUN_OPTIONS,
skip: ['ie', 'edge'],
});
});

it('Should switch context between a nested shadow iframe and the main window', function () {
return runTests('./testcafe-fixtures/iframe-switching-test.js', 'Click on element in a nested shadow iframe', {
...DEFAULT_RUN_OPTIONS,
skip: ['ie', 'edge'],
});
});

it('Should wait while a target iframe is loaded', function () {
return runTests('./testcafe-fixtures/iframe-switching-test.js', 'Click in a slowly loading iframe', DEFAULT_RUN_OPTIONS);
});
Expand Down Expand Up @@ -64,7 +78,7 @@ describe('[API] t.switchToIframe(), t.switchToMainWindow()', function () {
expect(errs[0]).to.contains(
'The specified selector does not match any element in the DOM tree.' +
' > | Selector(\'#non-existent\')');
expect(errs[0]).to.contains("> 56 | await t.switchToIframe('#non-existent');");
expect(/> *\d* *\| *await t\.switchToIframe\('#non-existent'\);/.test(errs[0])).ok;
});
});

Expand All @@ -81,7 +95,7 @@ describe('[API] t.switchToIframe(), t.switchToMainWindow()', function () {
return runTests('./testcafe-fixtures/iframe-switching-test.js', 'Try to switch to an incorrect element', { shouldFail: true })
.catch(function (errs) {
expect(errs[0]).to.contains('The action element is expected to be an <iframe>.');
expect(errs[0]).to.contains("> 74 | await t.switchToIframe('body');");
expect(/> *\d* *\| *await t\.switchToIframe\('body'\);/.test(errs[0])).ok;
});
});

Expand All @@ -92,23 +106,23 @@ describe('[API] t.switchToIframe(), t.switchToMainWindow()', function () {
})
.catch(function (errs) {
expect(errs[0]).to.contains('Content of the iframe to which you are switching did not load.');
expect(errs[0]).to.contains("> 189 | .switchToIframe('#too-slowly-loading-iframe')");
expect(/> *\d* *\| *\.switchToIframe\('#too-slowly-loading-iframe'\)/.test(errs[0])).ok;
});
});

it('Should raise an error when trying to execute an action in an unavailable iframe', function () {
return runTests('./testcafe-fixtures/iframe-switching-test.js', 'Click in a removed iframe', DEFAULT_FAILED_RUN_OPTIONS)
.catch(function (errs) {
expect(errs[0]).to.contains('The iframe in which the test is currently operating does not exist anymore.');
expect(errs[0]).to.contains("> 93 | .click('#btn');");
expect(/> *\d* *\| *\.click\('#btn'\);/.test(errs[0])).ok;
});
});

it('Should raise an error when trying to execute an action in an invisible iframe', function () {
return runTests('./testcafe-fixtures/iframe-switching-test.js', 'Click in an invisible iframe', DEFAULT_FAILED_RUN_OPTIONS)
.catch(function (errs) {
expect(errs[0]).to.contains('The iframe in which the test is currently operating is not visible anymore.');
expect(errs[0]).to.contains("> 201 | .click('#btn');");
expect(/> *\d* *\| *\.click\('#btn'\);/.test(errs[0])).ok;
});
});

Expand All @@ -122,7 +136,7 @@ describe('[API] t.switchToIframe(), t.switchToMainWindow()', function () {
})
.catch(function (errs) {
expect(errs[0]).to.contains('Content of the iframe in which the test is currently operating did not load.');
expect(errs[0]).to.contains("> 209 | .click('#second-page-btn');");
expect(/> *\d* *\| *\.click\('#second-page-btn'\);/.test(errs[0])).ok;
});
});
});
Expand Down
Expand Up @@ -52,6 +52,49 @@ test('Click on element in a nested iframe', async t => {
expect(iframeBtnClickCount).eql(1);
});

test.page`http://localhost:3000/fixtures/api/es-next/iframe-switching/pages/shadow.html`
('Click on an element in a shadow iframe and return to the main window', async t => {
await t
.switchToIframe(() => document.querySelector('#shadow-element').shadowRoot.querySelector('iframe'))
.click('#btn')
.switchToMainWindow()
.click('#btn');

const btnClickCount = await getBtnClickCount();
const iframeBtnClickCount = await getIframeBtnClickCount();

expect(btnClickCount).eql(1);
expect(iframeBtnClickCount).eql(1);
});

test.page`http://localhost:3000/fixtures/api/es-next/iframe-switching/pages/shadow.html`
('Click on element in a nested shadow iframe', async t => {
await t
.switchToIframe(() => document.querySelector('#shadow-element').shadowRoot.querySelector('iframe'))
.switchToIframe(() => document.querySelector('#shadow-element').shadowRoot.querySelector('iframe'))
.click('#btn')
.switchToMainWindow()
.click('#btn');

let btnClickCount = await getBtnClickCount();
const nestedIframeBtnClickCount = await getNestedIframeBtnClickCount();

expect(btnClickCount).eql(1);
expect(nestedIframeBtnClickCount).eql(1);

await t
.switchToIframe(() => document.querySelector('#shadow-element').shadowRoot.querySelector('iframe'))
.click('#btn')
.switchToMainWindow()
.click('#btn');

btnClickCount = await getBtnClickCount();
const iframeBtnClickCount = await getIframeBtnClickCount();

expect(btnClickCount).eql(2);
expect(iframeBtnClickCount).eql(1);
});

test('Switch to a non-existent iframe', async t => {
await t.switchToIframe('#non-existent');
});
Expand Down

0 comments on commit bc8bac1

Please sign in to comment.