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

Unable to switch to new tab using chromedriver #346

Open
reators opened this issue Sep 15, 2020 · 13 comments
Open

Unable to switch to new tab using chromedriver #346

reators opened this issue Sep 15, 2020 · 13 comments

Comments

@reators
Copy link

reators commented Sep 15, 2020

The application that I'm testing using HSAC library opens a new tab when clicking on a certain button.
The test script is supposed to take a screenshot of that new tab and save its content (it's a PDF embedded in html, so its content is not easily testable).
The test script uses fixture switch to next tab, but this does not work: The script still stays on the the same tab.
I added some high level debugging with a scenario

|scenario|dump tabs        |
|show    |tab count        |
|show    |current tab index|

|scenario              |Show form|
|dump tabs                                       |
|click                 |Vertragsbedingungen      |
|wait                  |45    |seconds           |
|dump tabs                                       |
|seconds before timeout|20                       |
|switch to next tab                              |
|note                  |show  |save page source  |
|note                  |close tab                |
|dump tabs                                       |
|switch to previous tab                          |
|dump tabs                                       |
|seconds before timeout|5                        |

which confirms my observation: Before clicking the button the output is (1, 1), Tab count increases after clicking the button which leads to a (2, 1). But switch to next tabstill shows (2, 1):

switch-tabs-hsac-only

In order to analyse this I copied the java-code of switchToNextTab() and added some logging:

Custom switchToNextTab
    public boolean switchToMyNextTab() {
        boolean result = false;
        List<String> tabs = getTabHandles();
        int currentTab = getCurrentTabIndex(tabs);

        logger.error("tabs: " + Arrays.toString(tabs.toArray()));
        logger.error("tabs(0): size " + tabs.size() + " current " + currentTab);

        if (tabs.size() > 1 || currentTab < 0) {
            int nextTab = currentTab + 1;
            if (nextTab == tabs.size() || nextTab < 0) {
                nextTab = 0;
            }
            logger.error("tabs: goto " + nextTab);
            goToTab(tabs, nextTab);
            logger.error("tabs(2): size " + tabs.size() + " current " + getCurrentTabIndex(tabs));
            result = true;
        }
        return result;
    }

To my surprise the value of current tab indexchanges (2nd pic in attachments), but I run into timeouts for switchToMyNextTab() and every fixture method that is called afterwards.

switch-tabs-with-custom-fixture

FitNesse output on calling custom switchToNextTab
switch to my next tab 
org.openqa.selenium.TimeoutException: timeout: Timed out receiving message from renderer: 20.000
  (Session info: chrome=85.0.4183.102)
Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:17:03'
System info: host: 'Holgers-MacBook-Pro.local', ip: 'fe80:0:0:0:10b0:2b5d:511d:13e3%en0', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.15.6', java.version: '13.0.2'
Driver info: org.openqa.selenium.chrome.ChromeDriver
Capabilities {acceptInsecureCerts: false, browserName: chrome, browserVersion: 85.0.4183.102, chrome: {chromedriverVersion: 85.0.4183.87 (cd6713ebf92fa..., userDataDir: /var/folders/2c/_cmd0tyx2s1...}, goog:chromeOptions: {debuggerAddress: localhost:51122}, javascriptEnabled: true, networkConnectionEnabled: false, pageLoadStrategy: normal, platform: MAC, platformName: MAC, proxy: Proxy(), setWindowRect: true, strictFileInteractability: false, timeouts: {implicit: 0, pageLoad: 300000, script: 30000}, unhandledPromptBehavior: ignore, webauthn:virtualAuthenticators: true}
Session ID: f25506a1d25913a63c5cc22c2df58787
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
	at org.openqa.selenium.remote.http.W3CHttpResponseCodec.createException(W3CHttpResponseCodec.java:187)
	at org.openqa.selenium.remote.http.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:122)
	at org.openqa.selenium.remote.http.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:49)
	at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:158)
	at org.openqa.selenium.remote.service.DriverCommandExecutor.execute(DriverCommandExecutor.java:83)
	at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:552)
	at org.openqa.selenium.remote.RemoteWebDriver$RemoteTargetLocator.defaultContent(RemoteWebDriver.java:922)
	at nl.hsac.fitnesse.fixture.util.selenium.SeleniumHelper.switchToDefaultContent(SeleniumHelper.java:1308)
	at nl.hsac.fitnesse.fixture.util.selenium.SeleniumHelper.goToTab(SeleniumHelper.java:1296)
	at nl.hsac.fitnesse.fixture.slim.web.BrowserTest.goToTab(BrowserTest.java:445)
	at de.fiduciagad.kfinfi.fitnesse.Sofortkredit.switchToMyNextTab(Sofortkredit.java:47)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    ...
The execution log (the truncated part shows only a series of backtraces, none of the custom loggings):
Execution log
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.

ChromeDriver was started successfully.

Sept. 15, 2020 4:52:00 PM org.openqa.selenium.remote.ProtocolHandshake createSession
INFORMATION: Detected dialect: W3C

Sept. 15, 2020 4:52:26 PM de.fiduciagad.kfinfi.fitnesse.Sofortkredit showTabInfo
SCHWERWIEGEND: tabs: size 1 current 0

Sept. 15, 2020 4:55:26 PM de.fiduciagad.kfinfi.fitnesse.Sofortkredit switchToMyNextTab
SCHWERWIEGEND: tabs: [CDwindow-7269F2F1E351583739FF39A979105790, CDwindow-F610A691675FF3818DE082E59009C576]

Sept. 15, 2020 4:55:26 PM de.fiduciagad.kfinfi.fitnesse.Sofortkredit switchToMyNextTab
SCHWERWIEGEND: tabs(0): size 2 current 0

Sept. 15, 2020 4:55:26 PM de.fiduciagad.kfinfi.fitnesse.Sofortkredit switchToMyNextTab
SCHWERWIEGEND: tabs: goto 1

[1600181746.332][SEVERE]: Timed out receiving message from renderer: 20.000

[1600181746.333][SEVERE]: Timed out receiving message from renderer: 20.000

Unable to take screenshot for exception: null

org.openqa.selenium.TimeoutException: timeout: Timed out receiving message from renderer: 20.000
  (Session info: chrome=85.0.4183.102)
Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:17:03'
System info: host: 'Holgers-MacBook-Pro.local', ip: 'fe80:0:0:0:10b0:2b5d:511d:13e3%en0', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.15.6', java.version: '13.0.2'
Driver info: org.openqa.selenium.chrome.ChromeDriver
Capabilities {acceptInsecureCerts: false, browserName: chrome, browserVersion: 85.0.4183.102, chrome: {chromedriverVersion: 85.0.4183.87 (cd6713ebf92fa..., userDataDir: /var/folders/2c/_cmd0tyx2s1...}, goog:chromeOptions: {debuggerAddress: localhost:51122}, javascriptEnabled: true, networkConnectionEnabled: false, pageLoadStrategy: normal, platform: MAC, platformName: MAC, proxy: Proxy(), setWindowRect: true, strictFileInteractability: false, timeouts: {implicit: 0, pageLoad: 300000, script: 30000}, unhandledPromptBehavior: ignore, webauthn:virtualAuthenticators: true}
Session ID: f25506a1d25913a63c5cc22c2df58787

	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    ...

So my questions are:

  • Why is there a difference in behaviour of custom and library version of switchToNextTab()?
  • Am I doing something wrong, or can the behaviour be fixed somehow?

Versions:
HSAC 4.19.0
FitNesse 20200501

Many thanks in advance,
reators

@fhoeben
Copy link
Owner

fhoeben commented Sep 15, 2020

Thanks for the extensive error report. You've been very thorough. I can't immediately think of a reason for the behaviour you observe.

Are you running with exactly the same browser configuration in all cases (i.e. not headless in one, but not the other, same platform, remote vs local, etc)?

Am I correct in assuming that you did not add @WaitUntil on your custom method? That is the only difference I can see. But I don't know why that would change which tab would be the current one.
I also notice that there is no log entry tabs(2): size, so it looks like the timeouts start happening in goToTab(). This matches the exception stack trace shown.

What exactly is on the tab that is opened? Is it indeed an HTML page with a PDF object embedded, or is it maybe just a PDF shown in a 'preview'. What does the URL look like? Given the fact that the error seems to start on SeleniumHelper.switchToDefaultContent(). This to me suggests that the tab is not a normal tab, I'm guessing it is a PDF preview, and that's what's causing the issue. Can you replay the script manually and see whether you can View Page Source does that show HTML? Can you share that?

Can you try to change |click|Vertragsbedingungen| to |show|download| Vertragsbedingungen| does that download a PDF by any chance? Even when the tab gives you a PDF this might not work, as sometimes PDFs are generated dynamically (via a POST or Ajax call) instead of just offering a URL that can be downloaded.

If the PDF is generated via a POST or Ajax call I'm afraid I don't know a way to check it via Selenium. In the cases I've had of that issue I tended to use one of 2 approaches to test the PDF. Test document generation directly via a REST call (e.g. via JsonHttpTest) or change the application to actually offer PDFs that are accessible via GET (i.e. where one would just have a <a href="... to get the document, and then use BrowserTest's download).

To be able the check PDF content you could take a look at my, poorly documented I'm afraid, PDF fixture.

But to really test document generation (for many possible variations) I recommend using unit tests where various combinations of input parameters are fed into the document generation and the resulting PDFs content checked. Such tests allow you to test all code paths in the document generation and usually allow "dynamic" elements (e.g. current date/time or a generated sequence number) to be stubbed/mocked/fixed so result can be checked comprehensively. From the browser I would just test that a PDF is available and one or two data elements to ensure supplied parameters are fed correctly into the generation process.

@reators
Copy link
Author

reators commented Sep 16, 2020

Thanks for your extensive response.

Are you running with exactly the same browser configuration in all cases (i.e. not headless in one, but not the other, same platform, remote vs local, etc)?

Yes, all runs use the same browser configuration.

Am I correct in assuming that you did not add @Waituntil on your custom method? That is the only difference I can see. But I don't know why that would change which tab would be the current one.
I also notice that there is no log entry tabs(2): size, so it looks like the timeouts start happening in goToTab(). This matches the exception stack trace shown.

Thanks for the hint: This actually solved the timeouts and stacktrace problem.
Adding the annotation "cleans up" the execution log: Both problems disappeared.

What exactly is on the tab that is opened? Is it indeed an HTML page with a PDF object embedded, or is it maybe just a PDF shown in a 'preview'. What does the URL look like? Given the fact that the error seems to start on SeleniumHelper.switchToDefaultContent(). This to me suggests that the tab is not a normal tab, I'm guessing it is a PDF preview, and that's what's causing the issue. Can you replay the script manually and see whether you can View Page Source does that show HTML? Can you share that?

The URL of the new tab is quite normal, identical to the one from the calling URL: https://....de/vertragserstellung
Could this be a problem?

View page source doesn't work as the script isn't able to switch to the new tab. But it can be accessed manually, of course.

The content of the new tab is

New PDF-tab
<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"><title>Vertragsvorschau</title>
</head><body>
<iframe src="data:application/pdf;base64, JVBERi0xLjQNCiXi48%2FTCjYgMCBvYmoKPDwKL0xlbmd0aCA3IDAgUgovRmlsdGVyIC9GbGF0ZURlY29kZQo%2BPgpzdHJlYW0KeJzdXN1z2ziSf%2FdfoaetTFWGQ4DgV%2B7JHnucbGZn7xLPzFXOW1tUBEmMJSrLj7jKf%2F01SH
...
IKJSVFT0YK" width="100%" height="100%"></iframe></body>
</html>

If the PDF is generated via a POST or Ajax call I'm afraid I don't know a way to check it via Selenium. In the cases I've had of that issue I tended to use one of 2 approaches to test the PDF. Test document generation directly via a REST call (e.g. via JsonHttpTest) or change the application to actually offer PDFs that are accessible via GET (i.e. where one would just have a <a href="... to get the document, and then use BrowserTest's download).

PDF-Generation-Angular-Snippet
        const pfdWindow = window.open("");
        const pdfDocument = pfdWindow.document;
        pdfDocument.write('<title>Vertragsvorschau</title>');
        pdfDocument.write("Please wait for document ...");
        this.contractcreationService.showForm(getCurrentProcessId()).subscribe(response => {
            const iframeStart = "<\iframe width='100%' height='100%' src='data:application/pdf;base64, ";
            const content = encodeURIComponent(response.data);
            const iframeEnd = "'><\/iframe>";
            pdfDocument.title = response.filename;
            pdfDocument.body.innerHTML = "";
            pdfDocument.write(iframeStart + content + iframeEnd);
        });

The text "Please wait for document" is shown in the new tab for about 30s. Then it disappears and is replaced by the PDF view (this is why |show|download| Vertragsbedingungen| probably won't work).
I'm unsure if retrieving the PDF directly from the backend is sufficient for ensuring correct functionality.

To be able the check PDF content you could take a look at my, poorly documented I'm afraid, PDF fixture.

I wasn't aware of his fixture. It looks quite promising 👍🏻 I'll use it probably after I can access the PDF from FitNesse ...

But to really test document generation (for many possible variations) I recommend using unit tests where various combinations of input parameters are fed into the document generation and the resulting PDFs content checked.

The document generation is external to our team and dependent on a more complex outer process.
Unit-tests must be done by the other team ...

From the browser I would just test that a PDF is available and one or two data elements to ensure supplied parameters are fed correctly into the generation process.

Yes, that's my intention: Testing a few elements of the PDF would be great. But for now a download of the PDF would suffice.

@fhoeben
Copy link
Owner

fhoeben commented Sep 16, 2020

So the @WaitUntil cleans up the execution log. That's not actually good news: in that case the fixture is just retrying but has exactly the same problem.
The error was thrown by trying to switch to default content on the new tab. Can you see whether that call can be skipped and whether that changes the situation? I would almost suggest you file a bug report with chromedriver. It seems the page with the preview iframe is not something it can handle.

This PDF page is a new one to me. It is a HTML page with the PDF previewer of the browser embedded as an iframe. The PDF content is actually only present in browser memory (and encoded into the URL), so indeed 'download' will not work.

The only thing we can achieve is to extract the iframe's src attribute, base64 decode that and save it to the local filesystem of the machine running the FitNesse test. Only then will further processing of that PDF be possible (and then you can also include in your test reports, instead of just a screenshot).

I expect the this.contractcreationService.showForm(getCurrentProcessId()) is actually doing an Ajax call to some backend, do you see that in the developer/network console? Maybe you can do that call directly from FitNesse (with JsonHttpTest) to get the PDF content?

@reators
Copy link
Author

reators commented Sep 18, 2020

The error was thrown by trying to switch to default content on the new tab. Can you see whether that call can be skipped and whether that changes the situation?

This gives an improvement, but leads to a new error:

  • The tab-switching works (shown by the dump scenario)
  • As soon as something on the new tab gets accessed (e.g. a screenshot, page title) we run into a timeout again:
Console log output on "show | page title"
org.openqa.selenium.TimeoutException: timeout: Timed out receiving message from renderer: 5.000  (Session info: chrome=85.0.4183.102)Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:17:03'System info: host: 'Holgers-MacBook-Pro.local', ip: '127.0.0.1', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.15.6', java.version: '13.0.2'Driver info: org.openqa.selenium.chrome.ChromeDriverCapabilities {acceptInsecureCerts: false, browserName: chrome, browserVersion: 85.0.4183.102, chrome: {chromedriverVersion: 85.0.4183.87 (cd6713ebf92fa..., userDataDir: /var/folders/2c/_cmd0tyx2s1...}, goog:chromeOptions: {debuggerAddress: localhost:61209}, javascriptEnabled: true, networkConnectionEnabled: false, pageLoadStrategy: normal, platform: MAC, platformName: MAC, proxy: Proxy(), setWindowRect: true, strictFileInteractability: false, timeouts: {implicit: 0, pageLoad: 300000, script: 30000}, unhandledPromptBehavior: ignore, webauthn:virtualAuthenticators: true}Session ID: 3c90e798ec75275b70ae1a14cb40081e
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
at org.openqa.selenium.remote.http.W3CHttpResponseCodec.createException(W3CHttpResponseCodec.java:187)	at org.openqa.selenium.remote.http.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:122)
at org.openqa.selenium.remote.http.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:49)
at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:158)
at org.openqa.selenium.remote.service.DriverCommandExecutor.execute(DriverCommandExecutor.java:83)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:552)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:609)
at org.openqa.selenium.remote.RemoteWebDriver.getTitle(RemoteWebDriver.java:281)
at nl.hsac.fitnesse.fixture.util.selenium.SeleniumHelper.getPageTitle(SeleniumHelper.java:132)
at nl.hsac.fitnesse.fixture.slim.web.BrowserTest.pageTitle(BrowserTest.java:484)
...

I would almost suggest you file a bug report with chromedriver. It seems the page with the preview iframe is not something it can handle.

This comment led me to the idea to try firefox for the test.
And indeed: Switching tabs now works like a charm using firefox for the test 👍🏻
No custom code needed.

So it's clear that we have a problem with chromedriver. Are we sure if this problem is something special to the pdf preview page, or could it be a general problem? (I cannot test the latter)

The only thing we can achieve is to extract the iframe's src attribute, base64 decode that and save it to the local filesystem of the machine running the FitNesse test. Only then will further processing of that PDF be possible

How would you do that? How could the src attribute of the HTML get extracted, e.g.?

I expect the this.contractcreationService.showForm(getCurrentProcessId()) is actually doing an Ajax call to some backend, do you see that in the developer/network console? Maybe you can do that call directly from FitNesse (with JsonHttpTest) to get the PDF content?

contractcreationService() executes a REST-call, something like GET http://localhost:4200/rest/.../formulare?pid=0a77396b-712e-4c03-8545-19df0efa7470. This PID/ProcessId isn't accessible on the testing level, it's used only internally by the Angular/JS code.
So I don't think that this a viable idea.

BTW: The answer to the GET looks very similar to the HTML page:

GET response
{"filename":"Vertragsvorschau",
 "mimeType":"application/pdf",
 "data":"JVBERi0x ...Rgo="
}

@fhoeben
Copy link
Owner

fhoeben commented Sep 18, 2020

I believe the issue in chromedriver will be related to the pdf preview. You could try to make a static page with such an iframe (and PDF base64 encoded in src) and create a sample plain-Java-selenium program that tries to access that (in a tab) and see what results you get. I suspect you will get the same timeouts and you could submit that sample as bug to chromedriver project.
But I fear that will not be resolved quickly.

That REST call still seems the best way forward the GET response could easily be converted to a PDF file, without any need to mess with screenshots etc.

Is that processId really not accessible? is it not placed in some element's attribute on the page? That you could extract? Or some global variable, that can be accessed by sending some JavaScript to the browser from FitNesse? Or could the application be changed to place this id in a data- element on the page?

@reators
Copy link
Author

reators commented Sep 18, 2020

That REST call still seems the best way forward the GET response could easily be converted to a PDF file, without any need to mess with screenshots etc.

I tried using HSAC's |$pdfencoded= |value of attribute |src |on |iframe | but got no result ...
Another solution could be to save the page source and execute some kind of script/java code on it. But this sounds like a cumbersome way.

Is that processId really not accessible? is it not placed in some element's attribute on the page? That you could extract? Or some global variable, that can be accessed by sending some JavaScript to the browser from FitNesse? Or could the application be changed to place this id in a data- element on the page?

I found it actually in the "session storage" of the browser's web storage as a key-value-pair assigned to the base URL of the web page.
Is this session storage of the browser accessible by HSAC, or some other library?

@fhoeben
Copy link
Owner

fhoeben commented Sep 18, 2020

Getting the value of the attribute probably needs a 'technical selector', maybe something like:

|$pdfencoded= |value of attribute |src |on |css=iframe |

"session storage" should be no problem, you can just execute a bit of JavaScript to get it. Try:
|$processId=|execute script|return sessionStorage.getItem('<key>')|
or
|$processId=|execute script|return localStorage.getItem('<key>')|

Using JsonHttpTest you should be able to save the PDF based on the response doing something like:

|script |json http test|
|set value|$processId|for|pid|
|get from|http://localhost:4200/rest/.../formulare|
|$pdf=|create file | vertragsvorschau.pdf | from base64 content of |data|

@fhoeben
Copy link
Owner

fhoeben commented Sep 18, 2020

Oh, and you may need to add |copy browser cookies| as second line to the JsonHttpTest script, if cookies are needed to make the REST call.

@reators
Copy link
Author

reators commented Sep 21, 2020

Getting the value of the attribute probably needs a 'technical selector', maybe something like:

|$pdfencoded= |value of attribute |src |on |css=iframe |

This works and puts the content of the iframe into the variable:
grafik

Couldn't this content be decoded somehow using HttpTest-fixtures, e.g. with createFileFromBase64(),
and then processed using PdfFixture mentioned earlier?
I tried the former but was unable to call the fixture from BrowserTest-script (and using it as a library also didn't work, probably due to missing constructor).

"session storage" should be no problem, you can just execute a bit of JavaScript to get it. Try:
|$processId=|execute script|return sessionStorage.getItem('<key>')|
or
|$processId=|execute script|return localStorage.getItem('<key>')|

Using JsonHttpTest you should be able to save the PDF based on the response doing something like:

|script |json http test|
|set value|$processId|for|pid|
|get from|http://localhost:4200/rest/.../formulare|
|$pdf=|create file | vertragsvorschau.pdf | from base64 content of |data|

This looks also promising (especially the embedded javascript looks cool 👍🏻 ), but this may not be needed if we could get above approach to work. Wouldn't that be simpler? It would save us from loading the pdf twice.
BTW how could a JsonHttpTest-script be called from a BrowserTest-scenario?

@reators reators changed the title Unable to switch to new tab Unable to switch to new tab using chromedriver Sep 21, 2020
@fhoeben
Copy link
Owner

fhoeben commented Sep 21, 2020

Good to hear you were able to get to the src attribute. Did you end u using firefox, or managed to get it working in chrome? I noticed there are some options you supply when starting chrome that might alter its behaviour, by disabling the built in pdf viewer. But if it's not needed, all the better.

Decoding the content you extracted is certainly possible, but I hadn't seen the need before to expose that as a fixture method. JsonHttpTest can do it when such a value is present in a json response (see

protected String createFileFromBase64(String baseName, String base64Content) {
). The class DataUrlHelper strips the prefix and then Base64Fixture can save it to disk. At the moment I fear you have to write your own custom fixture to get this to work directly from a string.

Calling another fixture from a scenario requires some 'wiki script magic'. Something I advice to do with caution as it makes the tests scenarios harder to understand and maintain. You have to 'push' the current fixture, start a new fixture, do what you need to do with the new fixture and then 'pop' back to the old one. In the documentation the call this changing the script's actor. You get something like:

|scenario|download pdf for _ |processId|
|push fixture|
|start |json http test|
|set value|@{processId}|for|pid|
|get from|http://localhost:4200/rest/.../formulare|
|$pdf=|create file | vertragsvorschau.pdf | from base64 content of |data|
|pop fixture|

@fhoeben
Copy link
Owner

fhoeben commented Sep 22, 2020

Downloading content from a 'data-url' instead of 'normal' link is actually quite a nice feature. I created an issue to add support for that. (Maybe someone can implement a PR for it before I'll get around to it.)

@reators
Copy link
Author

reators commented Sep 24, 2020

Good to hear you were able to get to the src attribute. Did you end u using firefox, or managed to get it working in chrome? I noticed there are some options you supply when starting chrome that might alter its behaviour, by disabling the built in pdf viewer. But if it's not needed, all the better.

I switched to use firefox.

Decoding the content you extracted is certainly possible, but I hadn't seen the need before to expose that as a fixture method. JsonHttpTest can do it when such a value is present in a json response (see

protected String createFileFromBase64(String baseName, String base64Content) {

). The class DataUrlHelper strips the prefix and then Base64Fixture can save it to disk. At the moment I fear you have to write your own custom fixture to get this to work directly from a string.

Before I try this I want to give the "fixture chaining" approach a try:

Calling another fixture from a scenario requires some 'wiki script magic'. Something I advice to do with caution as it makes the tests scenarios harder to understand and maintain. You have to 'push' the current fixture, start a new fixture, do what you need to do with the new fixture and then 'pop' back to the old one. In the documentation the call this changing the script's actor. You get something like:

|scenario|download pdf for _ |processId|
|push fixture|
|start |json http test|
|set value|@{processId}|for|pid|
|get from|http://localhost:4200/rest/.../formulare|
|$pdf=|create file | vertragsvorschau.pdf | from base64 content of |data|
|pop fixture|

This works perfectly for me! Thank you very much for providing the snippets (didn't find this push/pop in my searches ...).
With this preparation it's now straigthforward to use hsac-fitnesse-pdf as a library which I managed successfully to do some simple tests for the pdf (together with lib StringFixture).

Thanks again for your support!

@reators
Copy link
Author

reators commented Sep 28, 2020

Unfortunately did the JsonHttpTest-version work only in one of our test environments, but in not the others.
As the headers are quite different, and copy browser cookies didn't help, and the internal processing on Angular side is different (using an "http interceptor", whatever this means), I decided to go the other way you proposed:

Decoding the content you extracted is certainly possible, but I hadn't seen the need before to expose that as a fixture method. JsonHttpTest can do it when such a value is present in a json response (see

protected String createFileFromBase64(String baseName, String base64Content) {

). The class DataUrlHelper strips the prefix and then Base64Fixture can save it to disk. At the moment I fear you have to write your own custom fixture to get this to work directly from a string.

It was roughly a copy'n'pasting of existing code. I'll provide my solution here. It's probably not as elegant as proposed in #347, but it works for me:

    protected Base64Fixture getBase64Fixture() {
        return new Base64Fixture();
    }

    public String createFileFromUrlEncoded(final String baseName, final String urlEncodedContent) {

        String base64Content = java.net.URLDecoder.decode(urlEncodedContent, StandardCharsets.UTF_8);
        if (DataUrlHelper.isDataUrl(base64Content)) {
            base64Content = DataUrlHelper.getData(base64Content);
        }
        Base64Fixture base64Fixture = getBase64Fixture();
        return base64Fixture.createFrom(baseName, base64Content);
    }

Calling this from a scenario:

|scenario    |download pdf as   |filename                              |
|$pdfencoded=|value of attribute|src      |on              |css=iframe |
|$pdf=       |create file       |@filename|from Url encoded|$pdfencoded|

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants