Skip to content

Commit

Permalink
Merge pull request #361 from dn0000001/file-download-js
Browse files Browse the repository at this point in the history
Add support to download files using JavaScript
  • Loading branch information
AutomatorGuy123 authored Jul 3, 2021
2 parents 54a3bbe + 851a673 commit f8c0e9c
Show file tree
Hide file tree
Showing 8 changed files with 337 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.automation.common.ui.app.pageObjects;

import com.lazerycode.selenium.filedownloader.DownloadData;
import com.lazerycode.selenium.filedownloader.FileDownloader;
import com.taf.automation.ui.support.PageObjectV2;
import com.taf.automation.ui.support.TestContext;
import com.taf.automation.ui.support.util.AssertJUtil;
import com.taf.automation.ui.support.util.DownloadUtils;
import com.taf.automation.ui.support.util.JsUtils;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
import org.apache.http.HttpStatus;
import org.openqa.selenium.support.FindBy;
Expand Down Expand Up @@ -64,4 +67,23 @@ public File performDownloadOfCsvFileUsingUri() {
return downloader.downloadFile("auto-", ".csv");
}

/**
* Perform download of the CSV file using JavaScript
*
* @return the CSV file that was saved to a temporary file which needs to be deleted after
*/
public File performDownloadOfCsvFileUsingJavaScript() {
String url = downloadSampleCsvFile.getAttribute("href");
DownloadData downloadData = JsUtils.executeGetRequestDownload(url);

// This is unnecessary and only for testing purposes
int status = downloadData.getStatus();
AssertJUtil.assertThat(status).as(LINK_HTTP_STATUS).isEqualTo(HttpStatus.SC_OK);

File csv = DownloadUtils.createTempFile("auto-", ".csv");
DownloadUtils.writeFile(csv, downloadData.getFile());

return csv;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.taf.automation.ui.support.util.DomainObjectUtils;
import com.taf.automation.ui.support.util.Utils;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.io.FileUtils;
import org.apache.http.HttpStatus;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
Expand Down Expand Up @@ -43,7 +44,9 @@ public void performTest(ITestContext injectedContext) {

new Navigation(getContext()).toFileExamplesOtherFiles(Utils.isCleanCookiesSupported());
downloadImageFromHref();
downloadFileFromUri();
File csvUri = downloadFileFromUri();
File csvJs = downloadFileUsingJavaScript();
validateCsvFilesAreEqual(csvUri, csvJs);
}

@Step("Perform Download Image from Element")
Expand All @@ -69,17 +72,44 @@ private void downloadImageFromHref() {
}

@Step("Perform Download File from URI")
private void downloadFileFromUri() {
private File downloadFileFromUri() {
File csv = new FileExamplesOtherFilesPage(getContext()).performDownloadOfCsvFileUsingUri();
csv.deleteOnExit();
AssertJUtil.assertThat(csv).exists().isFile();
genericCsvValidations(csv);
return csv;
}

@Step("Perform Download File Using JavaScript")
private File downloadFileUsingJavaScript() {
File csv = new FileExamplesOtherFilesPage(getContext()).performDownloadOfCsvFileUsingJavaScript();
csv.deleteOnExit();
AssertJUtil.assertThat(csv).exists().isFile();
genericCsvValidations(csv);
return csv;
}

private void genericCsvValidations(File csv) {
List<CSVRecord> records = new ArrayList<>();
Map<String, Integer> headers = new HashMap<>();
CsvUtils.read(csv.getAbsolutePath(), records, headers);
AssertJUtil.assertThat(records).as("Records").isNotEmpty();
AssertJUtil.assertThat(headers).as("Headers").isNotEmpty();
ApiUtils.attachDataFile(csv, "text/csv", "CSV from URI");
ApiUtils.attachDataFile(csv, "text/csv", "CSV using JavaScript");
}

@Step("Validate CSV Files Are Equal")
private void validateCsvFilesAreEqual(File csvUri, File csvJs) {
Boolean result;
try {
result = FileUtils.contentEquals(csvUri, csvUri);
} catch (Exception ex) {
result = null;
}

AssertJUtil.assertThat(result)
.as("Error occurred reading the files").isNotNull()
.as("CSV URI vs. CSV JS").isTrue();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.lazerycode.selenium.filedownloader;

/**
* Class to hold the status &amp; the downloaded file
*/
public class DownloadData {
private int status;
private byte[] file;

public int getStatus() {
return status;
}

public void setStatus(int status) {
this.status = status;
}

public byte[] getFile() {
return file;
}

public void setFile(byte[] file) {
this.file = file;
}

}
25 changes: 24 additions & 1 deletion taf/src/main/java/com/taf/automation/api/JsonUtils.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
package com.taf.automation.api;

import com.google.gson.Gson;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.taf.automation.ui.support.testng.Attachment;
import org.apache.commons.codec.binary.Base64;

import java.lang.reflect.Type;

public class JsonUtils {
private static class ByteArrayToBase64TypeAdapter implements JsonSerializer<byte[]>, JsonDeserializer<byte[]> {
public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return Base64.decodeBase64(json.toString());
}

public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(Base64.encodeBase64String(src));
}
}

private JsonUtils() {
//
}
Expand All @@ -15,7 +35,10 @@ private JsonUtils() {
*/
@SuppressWarnings("squid:S1488")
public static Gson getGson() {
Gson gson = new Gson();
Gson gson = new Gson()
.newBuilder()
.registerTypeHierarchyAdapter(byte[].class, new ByteArrayToBase64TypeAdapter())
.create();

//
// Add any configurations of Gson as necessary here
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.taf.automation.ui.support.util;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;

@SuppressWarnings("java:S3252")
public class DownloadUtils {
private DownloadUtils() {
// Prevent initialization of class as all public methods should be static
}

/**
* Wrapper of File.createTempFile that does not require handling of the exception
*
* @param prefix - The prefix string to be used in generating the file's name;
* must be at least three characters long
* @param suffix - he suffix string to be used in generating the file's name;
* may be null, in which case the suffix ".tmp" will be used
* @return An abstract pathname denoting a newly-created empty file
* @throws AssertionError if temp file cannot be created
*/
public static File createTempFile(String prefix, String suffix) {
try {
return File.createTempFile(prefix, suffix);
} catch (IOException io) {
AssertJUtil.fail("Failed to create temp file to due exception: %s", io.getMessage());
}

return null;
}

/**
* Wrapper of FileUtils.writeByteArrayToFile that does not require handling of the exception
*
* @param file - the file to write to
* @param data - the content to write to the file
* @throws AssertionError if file cannot be written to
*/
public static void writeFile(File file, byte[] data) {
try {
FileUtils.writeByteArrayToFile(file, data);
} catch (IOException io) {
AssertJUtil.fail("Failed to write file to due exception: %s", io.getMessage());
}
}

}
87 changes: 87 additions & 0 deletions taf/src/main/java/com/taf/automation/ui/support/util/JsUtils.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.taf.automation.ui.support.util;

import com.google.gson.reflect.TypeToken;
import com.lazerycode.selenium.filedownloader.DownloadData;
import com.taf.automation.api.JsonUtils;
import com.taf.automation.ui.support.TestProperties;
import com.taf.automation.ui.support.events.Event;
Expand All @@ -11,8 +12,10 @@
import com.taf.automation.ui.support.testng.TestNGBaseWithoutListeners;
import net.jodah.failsafe.Failsafe;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.http.message.BasicNameValuePair;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
Expand All @@ -21,6 +24,8 @@
import org.openqa.selenium.support.ui.WebDriverWait;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
Expand All @@ -33,6 +38,8 @@ public class JsUtils {
private static final String FOCUS = Utils.readResource("JS/Focus.js");
private static final String GET_TEXT_ONLY_TOP_LEVEL = Utils.readResource("JS/GetTextOnlyTopLevel.js");
private static final String EXECUTE_GET_REQUEST = Utils.readResource("JS/ExecuteGetRequest.js");
private static final String EXECUTE_GET_REQUEST_DOWNLOAD = Utils.readResource("JS/ExecuteGetRequestDownload.js");
private static final String EXECUTE_POST_REQUEST_DOWNLOAD = Utils.readResource("JS/ExecutePostRequestDownload.js");
private static final String OVERLAPPING = Utils.readResource("JS/Overlapping.js");
private static final String EXECUTE_STANDARD_EVENT = Utils.readResource("JS/ExecuteStandardEvent.js");
private static final String EXECUTE_MOUSE_EVENT = Utils.readResource("JS/ExecuteMouseEvent.js");
Expand Down Expand Up @@ -285,6 +292,86 @@ public static String executeGetRequest(String url) {
return String.valueOf(execute(getWebDriver(), EXECUTE_GET_REQUEST, url));
}

/**
* Execute GET request to download file using specified URL
*
* @param url - URL to send GET request to
* @return DownloadData
*/
public static DownloadData executeGetRequestDownload(String url) {
return executeGetRequestDownload(url, null);
}

/**
* Execute GET request to download file using specified URL
*
* @param url - URL to send GET request to
* @param headers - The headers for the request (Empty list or null is supported)
* @return DownloadData
*/
public static DownloadData executeGetRequestDownload(String url, List<BasicNameValuePair> headers) {
String headersAsJson = JsonUtils.getGson().toJson(ObjectUtils.defaultIfNull(headers, new ArrayList<>()));
String result = (String) execute(getWebDriver(), EXECUTE_GET_REQUEST_DOWNLOAD, url, headersAsJson);
return JsonUtils.getGson().fromJson(result, DownloadData.class);
}

/**
* Execute POST request to download file using specified URL
*
* @param url - URL to send POST request to
* @param headers - The headers for the request (Empty list or null is supported)
* @param formData - The name value pairs for the form data
* @return DownloadData
*/
public static DownloadData executePostRequestDownload(
String url,
List<BasicNameValuePair> headers,
List<BasicNameValuePair> formData
) {
return executePostRequestDownload(url, headers, formData, null);
}

/**
* Execute POST request to download file using specified URL
*
* @param url - URL to send POST request to
* @param headers - The headers for the request (Empty list or null is supported)
* @param body - The request body to be used
* @return DownloadData
*/
public static DownloadData executePostRequestDownload(String url, List<BasicNameValuePair> headers, String body) {
return executePostRequestDownload(url, headers, null, body);
}

/**
* Execute POST request to download file
*
* @param url - URL to send POST request to
* @param headers - The headers for the request. (Empty list or null is supported)
* @param formData - The name value pairs for the form data to be used. If not specified, then body needs to specified.
* @param body - The request body to be used. If not specified, then form data needs to be specified.
* @return DownloadData
*/
public static DownloadData executePostRequestDownload(
String url,
List<BasicNameValuePair> headers,
List<BasicNameValuePair> formData,
String body
) {
String headersAsJson = JsonUtils.getGson().toJson(ObjectUtils.defaultIfNull(headers, new ArrayList<>()));
boolean useBody = formData == null || formData.isEmpty();
String formDataAsJson = JsonUtils.getGson().toJson(ObjectUtils.defaultIfNull(formData, new ArrayList<>()));
String result = (String) execute(getWebDriver(),
EXECUTE_POST_REQUEST_DOWNLOAD,
url,
headersAsJson,
useBody,
formDataAsJson,
body
);
return JsonUtils.getGson().fromJson(result, DownloadData.class);
}

/**
* Execute JavaScript on the element to make it scrollable by making the style by absolute.<BR>
* <B>Notes:</B><BR>
Expand Down
42 changes: 42 additions & 0 deletions taf/src/main/resources/JS/ExecuteGetRequestDownload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
var targetUrl = arguments[0];
var headers = JSON.parse(arguments[1]);
var result;

function bufferToBase64(buf) {
var binstr = Array.prototype.map.call(buf, function(ch) {
return String.fromCharCode(ch);
}).join('');

// Note: This method is proprietary to the DOM
return btoa(binstr);
}

function getPromise() {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", targetUrl, true);
xhr.responseType = "arraybuffer";
xhr.onload = function() {
result = new Object();
result.status = this.status;
result.file = bufferToBase64(new Uint8Array(this.response));
resolve(result);
};

for (let header of headers) {
xhr.setRequestHeader(header.name, header.value);
}

xhr.send(null);
});
}

async function makeSynchronousRequest() {
// wait to http request to finish
await getPromise();

// below code will be executed after http request is finished
return JSON.stringify(result);
}

return makeSynchronousRequest();
Loading

0 comments on commit f8c0e9c

Please sign in to comment.