Harvey
Harvey

Reputation: 389

No element found using locator error after switching to iframe using protractor

I have been trying to download embedded PDF from webpage using protractor selenium. I'm currently stuck on having to actually download the file since I always got the following error:

  • Failed: No element found using locator: By(css selector, *[id="download"])

It cannot find the button even after switching to frame.

I have also tried the approach indicated in the answer here where it extracts the src attribute value and go directly to the URL but same issue. The download button (icon) cannot be found.

We have the same exact requirements where we just need to click the download icon embedded in the PDF which happens to be inside an iframe. Example page like this.

Here is my code snippet.

        const iframe = $('#printFrame'),            
              downloadBtn = $('#download'),
              content = $('#content');

        await this.disableWaitForAngular();
        await browser.wait(EC.visibilityOf(iframe),waitTimeout);
        console.log("Switching to iframe...");
        await browser.switchTo().frame(iframe.getWebElement());

        await browser.wait(EC.visibilityOf(content), waitTimeout);
        await browser.actions().mouseMove(content).perform();

        console.log("Waiting for download button.");
        await browser.wait(EC.visibilityOf(downloadBtn), waitTimeout);

        await downloadBtn.click();

        await browser.switchTo().defaultContent();
        await this.enableWaitForAngular();

UPDATE:

Tried to inject the following code as suggested on one of the proposed answers before and after switching frames but it gives me an error.

const downloadIcon: WebElement = await browser.executeScript('return document.querySelector("#viewer").shadowRoot.querySelector("#toolbar").shadowRoot.querySelector("#downloads").shadowRoot.querySelector("#download").shadowRoot.querySelector("#icon > iron-icon");');

    await downloadIcon.click();

Error:

 - Failed: javascript error: Cannot read property 'shadowRoot' of null
(Session info: chrome=87.0.4280.66)
(Driver info: chromedriver=87.0.4280.20 (c99e81631faa0b2a448e658c0dbd8311fb04ddbd-refs/branch-heads/4280@{#355}),platform=Windows NT 10.0.14393 x86_64)

Download icon for reference: enter image description here

Upvotes: 1

Views: 956

Answers (4)

K. B.
K. B.

Reputation: 3690

I think that the issue you have is due to shadow-root.

  1. Try to use by.deepCss(selector) Protractor method. If this is not working, look at the workaround here.

  2. You can get the src attribute of the iframe and open it in a new window. Then sendKeys Ctrl+S.

  3. You can try to run as JavaScript code in protractor:

    document.querySelector("#viewer").shadowRoot.querySelector("#toolbar").shadowRoot.querySelector("#downloads").shadowRoot.querySelector("#download").shadowRoot.querySelector("#icon > iron-icon").click();

I get this while inspecting the element and selected "Copy JS Path" from Chrome DevTools.

Reference:
How to interact with the elements within #shadow-root (open) while Clearing Browsing Data of Chrome Browser using cssSelector
deepCss is failing to identify an element inside the shadow root in Protractor
Protractor: Unable select input element inside a shadow DOM (Polymer) using by.deepCss('input')
https://www.protractortest.org/#/api?view=ProtractorBy.prototype.deepCss
https://superuser.com/questions/505899/keyboard-shortcuts-for-chrome-pdf-viewer

Upvotes: 1

Anton
Anton

Reputation: 2703

On the example page you provided, there is no "Download" button, because this button is a part of the Browser, not the page. You can't find it with any type of selector.

But we have pdf loaded there as an iframe, so we can create our own download button:

a = document.createElement('a'); a.href = $('iframe').src; a.download = "hello.pdf"; a.click();

So copy this code, go to your page, paste it into console and run. This will download the file.

This is just and example, but maybe you can adjust this idea to your needs.

UPDATE

Ok, I will try to explain what is under the hood. Here are the same page in different browsers: Chrome, FF, Edge, IE.

enter image description here

enter image description here

enter image description here

enter image description here

As you can see, the "inner contents" of PDF page are different, because it is not a page-ralative, it is controlled by the browser and it's plugins.

Pressing the "download" icon/button is the same as pressing something inside the plugin. It is not possible.

Actually, the page itself has only PDF object inside, not the controls.

For example in IE you can not locate "download" button. It simply does not exist, but we can press on it ("in browser", not "in page"), look:

enter image description here

Upvotes: 3

Sergey Pleshakov
Sergey Pleshakov

Reputation: 8958

This code did the work

await browser.waitForAngularEnabled(false);
await browser.get('https://www.sebi.gov.in/enforcement/orders/jun-2019/adjudication-order-in-respect-of-three-entities-in-the-matter-of-prism-medico-and-pharmacy-ltd-_43323.html');
let $iFrame = $('iframe[src*="sebi"]'),
    $downloadButton = $('#download');
await browser.wait(ExpectedConditions.presenceOf($iFrame), 5000);
await browser.switchTo().frame($iFrame.getWebElement());
await browser.wait(ExpectedConditions.presenceOf($downloadButton), 2000);
await $downloadButton.click();
await browser.sleep(2500);
await browser.switchTo().defaultContent();
await browser.waitForAngularEnabled(true);

if it doesn't work, then there might be something wrong with your config, so you'll need to update your question and include its content

Upvotes: 0

PDHide
PDHide

Reputation: 19949

in protractor its anti-pattern to mix chaining and awaiting together:

isPDFDownloadSuccessful: () => Promise<boolean> = async() => {        
        await this.disableWaitForAngular(); //function equivalent to browser.waitForAngularEnabled(false);
        const iframe = await element(by.tagName("iframe"));
        console.log("Switching to iFrame...");        
        await browser.switchTo().frame(element(by.tagName('iframe')).getWebElement());
        await element(by.id("download")).click()
     
        //Insert here actions to check successful download 
        return true;    
    };

Upvotes: 0

Related Questions