Skip to content

Commit

Permalink
[plugin-web-app] Add step to download files from Chrome downloads page
Browse files Browse the repository at this point in the history
  • Loading branch information
vkepin committed May 10, 2024
1 parent c428655 commit b97add8
Show file tree
Hide file tree
Showing 11 changed files with 441 additions and 0 deletions.
37 changes: 37 additions & 0 deletions docs/modules/plugins/pages/plugin-web-app.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1669,6 +1669,43 @@ Then value `$value` is selected in slider located by `$locator`
Then value `50` is selected in slider located by `id(test_slider)`
----

=== Download file from browser downloads page

Downloads the file from the browser downloads page and saves its content to the specified variable.

IMPORTANT: The step is only supported by Chrome browser.

Actions performed at this step:

1. Switch the browser to the download manager page.
2. Search for the latest downloaded file with a name that matches the provided regex.
3. Wait until the file download is complete.
4. Store file content into a variable with the specific scope.
5. Close the download manager page and return to the previously opened page.

[source,gherkin]
----
When I download file with name matching `$regex` from browser downloads and save its content to $scopes variable `$variableName`
----

* `$regex` - regular expression to filter downloaded files by name
* `$scopes` - the set (comma separated list of scopes e.g.: STORY, NEXT_BATCHES) of the variable scopes. Allowed values are STEP, SCENARIO, STORY and NEXT_BATCHES
* `$variableName` - the variable name to save file content

Timeout for file downloading is configurable via properties. The default timeout is 3 minutes.

[source, properties]
----
ui.chrome.file-download-timeout=PT3M
----

.Save data of the previously downloaded file into property
[source,gherkin]
----
When I download file with name matching `report` from browser downloads and save its content to SCENARIO variable `testReport`
----


=== Mobile Emulation

==== Emulate mobile device
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright 2019-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.vividus.steps.ui.web;

import java.time.Duration;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.jbehave.core.annotations.When;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.Browser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vividus.context.VariableContext;
import org.vividus.selenium.manager.IWebDriverManager;
import org.vividus.softassert.ISoftAssert;
import org.vividus.ui.action.IWaitActions;
import org.vividus.ui.monitor.TakeScreenshotOnFailure;
import org.vividus.ui.web.action.WebJavascriptActions;
import org.vividus.variable.VariableScope;

@TakeScreenshotOnFailure
public class ChromeExperimentalSteps
{
private static final Logger LOGGER = LoggerFactory.getLogger(ChromeExperimentalSteps.class);

private static final String CHROME_DOWNLOADS_PAGE = "chrome://downloads";

private final WebJavascriptActions javascriptActions;
private final IWebDriverManager webDriverManager;
private final ISoftAssert softAssert;
private final VariableContext variableContext;
private final IWaitActions waitActions;
private final PageSteps pageSteps;
private final WindowSteps windowSteps;

private Duration fileDownloadTimeout;

public ChromeExperimentalSteps(WebJavascriptActions javascriptActions, IWebDriverManager webDriverManager,
ISoftAssert softAssert, VariableContext variableContext, IWaitActions waitActions,
PageSteps pageSteps, WindowSteps windowSteps)
{
this.javascriptActions = javascriptActions;
this.webDriverManager = webDriverManager;
this.softAssert = softAssert;
this.variableContext = variableContext;
this.waitActions = waitActions;
this.pageSteps = pageSteps;
this.windowSteps = windowSteps;
}

/**
* <b>Warning!</b> This step can be used only for <i>desktop/chrome</i> profile. <br>
* Downloads the file from the website and saves its content to the specified variable.
* <p>
* Actions performed at this step:
* <ul>
* <li>Switch the browser to the download manager page;
* <li>Search for the latest downloaded file with a name that matches the provided regex;
* <li>Wait until the file download is complete;
* <li>Store file content into a variable with the specific scope;
* <li>Close the download manager page and return to the previously opened page;
* </ul>
* @param regex Regular expression to filter downloaded files by name.
* @param scopes The set (comma separated list of scopes e.g.: STORY, NEXT_BATCHES) of the variable
* scopes.<br>
* <i>Available scopes:</i>
* <ul>
* <li><b>STEP</b> - the variable will be available only within the step,
* <li><b>SCENARIO</b> - the variable will be available only within the scenario,
* <li><b>STORY</b> - the variable will be available within the whole story,
* <li><b>NEXT_BATCHES</b> - the variable will be available starting from next batch
* </ul>
* @param variableName The variable name to store the path to the temporary file with the file content.
*/
@When("I download file with name matching `$regex` from browser downloads"
+ " and save its content to $scopes variable `$variableName`")
public void downloadFile(String regex, Set<VariableScope> scopes, String variableName)
{
Validate.isTrue(webDriverManager.isBrowserAnyOf(Browser.CHROME),
"The step is supported only on Chrome browser.");
pageSteps.openPageInNewTab(CHROME_DOWNLOADS_PAGE);
try
{
Optional<String> filePathOpt = searchForFile(regex);
if (filePathOpt.isEmpty())
{
List<String> downloadedFiles = javascriptActions
.executeScriptFromResource(ChromeExperimentalSteps.class, "chrome-download-files-list.js");
String errorMessage = downloadedFiles.isEmpty() ? "There are no files on the browser downloads page"
: "Unable to find any file matching regex [" + regex
+ "] among files on the browser downloads page: " + String.join(", ", downloadedFiles);
softAssert.recordFailedAssertion(errorMessage);
return;
}
String filePath = filePathOpt.get();
LOGGER.atInfo().addArgument(() -> FilenameUtils.getName(filePath))
.log("Waiting for the {} file to download");
if (!isFileDownloadComplete(filePath))
{
return;
}
LOGGER.info("Download for the {} file is completed", filePath);
String content = getFileContent(filePath);
variableContext.putVariable(scopes, variableName, Base64.getDecoder().decode(content));
}
finally
{
windowSteps.closeCurrentTab();
}
}

private String getFileContent(String filePath)
{
WebElement input = (WebElement) javascriptActions.executeScriptFromResource(ChromeExperimentalSteps.class,
"chrome-download-create-input.js");
input.sendKeys(filePath);

String content = javascriptActions.executeAsyncScriptFromResource(ChromeExperimentalSteps.class,
"chrome-download-get-file-content.js", input);
return StringUtils.substringAfter(content, "base64,");
}

private boolean isFileDownloadComplete(String filePath)
{
return waitActions.wait(javascriptActions, fileDownloadTimeout,
js -> js.executeScriptFromResource(ChromeExperimentalSteps.class, "chrome-download-wait.js", filePath),

Check warning on line 146 in vividus-plugin-web-app/src/main/java/org/vividus/steps/ui/web/ChromeExperimentalSteps.java

View check run for this annotation

Codecov / codecov/patch

vividus-plugin-web-app/src/main/java/org/vividus/steps/ui/web/ChromeExperimentalSteps.java#L146

Added line #L146 was not covered by tests
true).isWaitPassed();
}

private Optional<String> searchForFile(String fileNameRegex)
{
String filePath = javascriptActions.executeScriptFromResource(ChromeExperimentalSteps.class,
"chrome-download-search-file.js", fileNameRegex);
return Optional.ofNullable(filePath);
}

public void setFileDownloadTimeout(Duration fileDownloadTimeout)
{
this.fileDownloadTimeout = fileDownloadTimeout;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
var input = window.document.createElement('INPUT');
input.setAttribute('type', 'file');
input.setAttribute('id', 'downloadedFileContent');
input.hidden = true;
input.onchange = function (e) { e.stopPropagation() };
return window.document.documentElement.appendChild(input);
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
return document.querySelector('downloads-manager')
.shadowRoot
.querySelector('#downloadsList')
.items
.map(e => e.fileName)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
var input = arguments[0], callback = arguments[1];
var reader = new FileReader();
reader.onload = function (ev) { callback(reader.result) };
reader.onerror = function (ex) { callback(ex.message) };
reader.readAsDataURL(input.files[0]);
input.remove();
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
return document.querySelector('downloads-manager')
.shadowRoot
.querySelector('#downloadsList')
.items
.filter(e => e.fileName.match(arguments[0]))
.sort((a, b) => b.started - a.started)
.map(e => e.filePath)[0]
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
return document.querySelector('downloads-manager')
.shadowRoot
.querySelector('#downloadsList')
.items
.find(e => e.filePath === arguments[0])
.state === 2
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ selenium.screenshot.full-page=true
selenium.screenshot.indent=300
# highlighter types: DEFAULT, BLUR, MONOCHROME
selenium.screenshot.highlighter=DEFAULT

ui.chrome.file-download-timeout=PT3M
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@
<property name="windowsStrategy" value="${selenium.windows-strategy}" />
</bean>

<bean id="chromeExperimentalSteps" class="org.vividus.steps.ui.web.ChromeExperimentalSteps">
<property name="fileDownloadTimeout" value="${ui.chrome.file-download-timeout}" />
</bean>

<bean class="org.vividus.ui.web.monitor.PublishingWebScreenshotOnFailureMonitor"
parent="abstractPublishingScreenshotOnFailureMonitor"/>

Expand Down Expand Up @@ -288,6 +292,7 @@
<idref bean="windowSteps" />
<idref bean="browserSteps" />
<idref bean="mobileEmulationSteps" />
<idref bean="chromeExperimentalSteps" />
</util:list>

<util:map id="propertyEditors-WebUi" key-type="java.lang.Class">
Expand Down
Loading

0 comments on commit b97add8

Please sign in to comment.