1

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

Harvey
  • 399
  • 4
  • 15
  • 31
  • Not sure is it going to help but try to get frem like: browser.switchTo().frame(1) – Gaj Julije Nov 22 '20 at 19:47
  • I actually have tried that, and errors. Cannot find the frame. – Harvey Nov 23 '20 at 17:08
  • To download an embedded PDF with Chrome, launch the browser with preference `'plugins.always_open_pdf_externally": true`, switch to the frame `iframe[src$=pdf]` and click on `#open-button`. – Florent B. Nov 28 '20 at 13:36

4 Answers4

3

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

Anton
  • 2,669
  • 1
  • 7
  • 15
  • The download button will be visible upon mouse hover. Please see description for the picture. It is actually a download icon, not "Download" button. Thanks! – Harvey Nov 26 '20 at 19:23
  • 1
    @Harvey ok, I added description and images to my answer. Please take a look. – Anton Nov 27 '20 at 11:06
  • I now got your point. Thank you very much for the explanation. I appreciate it. I have tried pasting the js code you gave to the console, and it really did work. Will try to imitate this using typescript. Thank you – Harvey Nov 27 '20 at 17:24
1

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

K. B.
  • 3,342
  • 3
  • 19
  • 32
  • This is the first time I have encountered this term. So I really thought this would solve my issue. I've tried injecting in my code what you have suggested, the value of the JS Path. But then I have encountered this error. Failed: javascript error: Cannot read property 'shadowRoot' of null. Also tried to switch to iframe first but no luck. – Harvey Nov 29 '20 at 11:39
  • You can try deepCss protractor method https://www.protractortest.org/#/api?view=ProtractorBy.prototype.deepCss. – K. B. Nov 29 '20 at 15:37
  • 1
    I tried another appoarch that looks promising - open in new window the source from the iframe https://www.w3docs.com/uploads/media/default/0001/01/540cb75550adf33f281f29132dddd14fded85bfc.pdf Then sendKeys Ctrl+S in order to download it. I will add my two suggestions to my answer. – K. B. Nov 29 '20 at 15:53
  • Tried your 2nd suggestion which is much easier to implement. But I'm not sure what element to sendkeys, so I tried to send using the body. await element(by.css('body')).sendKeys(Key.chord(Key.CONTROL, 's')); No luck. So will try the first suggestion, with the shadowroot workaround. Thank you very much. Appreciate the help. Will update you. – Harvey Nov 30 '20 at 00:02
  • I also tried the non-element variant: browser.actions().keyDown(Key.CONTROL).sendKeys('s').keyUp(Key.CONTROL).perform(); – Harvey Nov 30 '20 at 00:24
  • Not sure if this would trigger the download so I've added chrome capabilities in config. plugins:{ always_open_pdf_externally: true } – Harvey Nov 30 '20 at 00:25
0

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;    
    };
PDHide
  • 18,113
  • 2
  • 31
  • 46
  • Understood. I am used to doing this. I've just tried to chain because I thought that that's causing the issue which is not in my case. btw, thanks! – Harvey Nov 19 '20 at 14:34
0

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

Sergey Pleshakov
  • 7,964
  • 2
  • 17
  • 40
  • 1
    probably you just needed to wait for content to load – Sergey Pleshakov Nov 23 '20 at 15:28
  • Okay will try to have some added waits. Thanks! – Harvey Nov 23 '20 at 16:17
  • Apparently, upon checking the sample I gave is not the closest. Here's the closest example page that I'm working with. http://www.africau.edu/images/default/sample.pdf I have tried almost the same exact code that you have see my updates in my question. – Harvey Nov 23 '20 at 20:58
  • I have added a mouseMove action to mimic the hover action for the download icon to be visible. But still no luck. – Harvey Nov 23 '20 at 21:06
  • 1. I already spent time and solved the task given in the question. for another problem please open another question. 2. the url you added just opens the pdf, what does it have to do with iframes and the problem you were describing? – Sergey Pleshakov Nov 23 '20 at 21:11
  • 1. Okay will do. 2. This should be the URL. >> https://www.w3docs.com/tools/code-editor/1085 – Harvey Nov 23 '20 at 21:23