-
Notifications
You must be signed in to change notification settings - Fork 8
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
Add device endpoints to push/pull files and pull directory. #96
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,145 @@ | ||||||||||
using FlaUI.WebDriver.UITests.TestUtil; | ||||||||||
using NUnit.Framework; | ||||||||||
using OpenQA.Selenium; | ||||||||||
using OpenQA.Selenium.Appium; | ||||||||||
using OpenQA.Selenium.Appium.Windows; | ||||||||||
using OpenQA.Selenium.Remote; | ||||||||||
using System.IO; | ||||||||||
using System.IO.Compression; | ||||||||||
using System.Text; | ||||||||||
|
||||||||||
namespace FlaUI.WebDriver.UITests | ||||||||||
{ | ||||||||||
[TestFixture] | ||||||||||
public class DeviceTests | ||||||||||
{ | ||||||||||
[Test] | ||||||||||
public void PushFile_FileDoesNotExist_FileIsPushed() | ||||||||||
{ | ||||||||||
var driverOptions = FlaUIAppiumDriverOptions.RootApp(); | ||||||||||
using var driver = new WindowsDriver(WebDriverFixture.WebDriverUrl, driverOptions); | ||||||||||
var file = Path.GetTempFileName(); | ||||||||||
File.Delete(file); | ||||||||||
var data_to_write = "aaaaaaaaaaaaaaa"; | ||||||||||
driver.PushFile(file, data_to_write); | ||||||||||
Assert.That(File.ReadAllText(file) == data_to_write); | ||||||||||
} | ||||||||||
|
||||||||||
|
||||||||||
[Test] | ||||||||||
public void PushFile_FileExist_FileIsOverwritten() | ||||||||||
{ | ||||||||||
var driverOptions = FlaUIAppiumDriverOptions.RootApp(); | ||||||||||
using var driver = new WindowsDriver(WebDriverFixture.WebDriverUrl, driverOptions); | ||||||||||
var file = Path.GetTempFileName(); | ||||||||||
File.WriteAllText(file, "aaaaaaaaaaaaaaaaaaaa"); | ||||||||||
var data_to_write = "bbbbbbbbbbbbbbbbbbb"; | ||||||||||
driver.PushFile(file, data_to_write); | ||||||||||
Assert.That(File.ReadAllText(file) == data_to_write); | ||||||||||
} | ||||||||||
|
||||||||||
[Test] | ||||||||||
public void PushFile_PathEmpty_ErrorRaised() | ||||||||||
{ | ||||||||||
var driverOptions = FlaUIAppiumDriverOptions.RootApp(); | ||||||||||
using var driver = new WindowsDriver(WebDriverFixture.WebDriverUrl, driverOptions); | ||||||||||
Assert.That( | ||||||||||
() => driver.PushFile("", "hello world"), | ||||||||||
Throws.TypeOf<WebDriverException>().With.Message.EqualTo("Parameter path must be provided in the request.") | ||||||||||
); | ||||||||||
|
||||||||||
} | ||||||||||
|
||||||||||
[Test] | ||||||||||
public void PullFile_FileDoesNotExist_ErrorRaised() | ||||||||||
{ | ||||||||||
var driverOptions = FlaUIAppiumDriverOptions.RootApp(); | ||||||||||
using var driver = new WindowsDriver(WebDriverFixture.WebDriverUrl, driverOptions); | ||||||||||
var file = Path.GetTempFileName(); | ||||||||||
File.Delete(file); | ||||||||||
Assert.That( | ||||||||||
() => driver.PullFile(file), | ||||||||||
Throws.TypeOf<WebDriverException>().With.Message.EqualTo($"File {file} does not exist.") | ||||||||||
); | ||||||||||
} | ||||||||||
|
||||||||||
|
||||||||||
[Test] | ||||||||||
public void PullFile_PathEmpty_ErrorRaised() | ||||||||||
{ | ||||||||||
var driverOptions = FlaUIAppiumDriverOptions.RootApp(); | ||||||||||
using var driver = new WindowsDriver(WebDriverFixture.WebDriverUrl, driverOptions); | ||||||||||
Assert.That( | ||||||||||
() => driver.PullFile(""), | ||||||||||
Throws.TypeOf<WebDriverException>().With.Message.EqualTo("Parameter path must be provided in the request.") | ||||||||||
); | ||||||||||
|
||||||||||
} | ||||||||||
|
||||||||||
|
||||||||||
[Test] | ||||||||||
public void PullFile_FileExists_ContentPulled() | ||||||||||
{ | ||||||||||
var driverOptions = FlaUIAppiumDriverOptions.RootApp(); | ||||||||||
using var driver = new WindowsDriver(WebDriverFixture.WebDriverUrl, driverOptions); | ||||||||||
var file = Path.GetTempFileName(); | ||||||||||
var content = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; | ||||||||||
File.WriteAllText(file, content); | ||||||||||
var remoteContent = Encoding.UTF8.GetString(driver.PullFile(file)); | ||||||||||
Assert.That(remoteContent == content); | ||||||||||
|
||||||||||
} | ||||||||||
|
||||||||||
[Test] | ||||||||||
public void PullFolder_FolderDoesNotExists_ErrorRaised() | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
{ | ||||||||||
var tempDir = Path.Join(Path.GetTempPath(), System.Guid.NewGuid().ToString()); | ||||||||||
var driverOptions = FlaUIAppiumDriverOptions.RootApp(); | ||||||||||
using var driver = new WindowsDriver(WebDriverFixture.WebDriverUrl, driverOptions); | ||||||||||
Assert.That( | ||||||||||
() => driver.PullFolder(tempDir), | ||||||||||
Throws.TypeOf<WebDriverException>().With.Message.EqualTo($"File {tempDir} does not exist.") | ||||||||||
); | ||||||||||
} | ||||||||||
|
||||||||||
|
||||||||||
[Test] | ||||||||||
public void PullFolder_PathEmpty_ErrorRaised() | ||||||||||
{ | ||||||||||
var driverOptions = FlaUIAppiumDriverOptions.RootApp(); | ||||||||||
using var driver = new WindowsDriver(WebDriverFixture.WebDriverUrl, driverOptions); | ||||||||||
Assert.That( | ||||||||||
() => driver.PullFolder(""), | ||||||||||
Throws.TypeOf<WebDriverException>().With.Message.EqualTo($"Parameter path must be provided in the request.") | ||||||||||
); | ||||||||||
} | ||||||||||
|
||||||||||
|
||||||||||
[Test] | ||||||||||
public void PullFolder_FolderExists_ContentPulled() | ||||||||||
{ | ||||||||||
var driverOptions = FlaUIAppiumDriverOptions.RootApp(); | ||||||||||
using var driver = new WindowsDriver(WebDriverFixture.WebDriverUrl, driverOptions); | ||||||||||
|
||||||||||
var dir = Path.Join(Path.GetTempPath(), System.Guid.NewGuid().ToString()); | ||||||||||
for (int i = 0; i < 10; i++) | ||||||||||
{ | ||||||||||
driver.PushFile(Path.Join(dir, i.ToString()), $"{i} data"); | ||||||||||
} | ||||||||||
var zipBytes = driver.PullFolder(dir); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use newlines to separate arrange/act/assert parts of the test. In this case, surround
Suggested change
|
||||||||||
using var memoryStream = new MemoryStream(zipBytes); | ||||||||||
using var zip = new ZipArchive(memoryStream); | ||||||||||
|
||||||||||
for (int i = 0; i < 10; i++) | ||||||||||
{ | ||||||||||
var entry = zip.GetEntry(i.ToString()); | ||||||||||
using var entryStream = new StreamReader(entry.Open()); | ||||||||||
Assert.That(entryStream.ReadToEnd() == $"{i} data"); | ||||||||||
} | ||||||||||
Directory.Delete(dir, true); | ||||||||||
} | ||||||||||
|
||||||||||
|
||||||||||
|
||||||||||
} | ||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
using FlaUI.WebDriver.Models; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.Diagnostics; | ||
using System.Buffers.Text; | ||
using System.IO; | ||
using System.IO.Compression; | ||
using System.Security.Cryptography; | ||
using static System.Runtime.InteropServices.JavaScript.JSType; | ||
|
||
namespace FlaUI.WebDriver.Controllers | ||
{ | ||
|
||
[Route("session/{sessionId}/appium/device")] | ||
[ApiController] | ||
public class DeviceController : ControllerBase | ||
{ | ||
|
||
|
||
private static ActionResult MissingParameter(string parameterName) | ||
{ | ||
return WebDriverResult.NotFound(new ErrorResponse() | ||
{ | ||
ErrorCode = "Missing JSON Parameter", | ||
Message = $"Parameter {parameterName} must be provided in the request." | ||
}); | ||
} | ||
|
||
private static ActionResult FileNotFound(string fileName) | ||
{ | ||
return WebDriverResult.NotFound(new ErrorResponse() | ||
{ | ||
ErrorCode = "File Not Found", | ||
Message = $"File {fileName} does not exist." | ||
}); | ||
} | ||
|
||
[HttpPost("push_file")] | ||
public async Task<ActionResult> PushFile([FromBody] PushFileRequest request) | ||
{ | ||
if (request.Data == null) | ||
{ | ||
// data could be an empty string, so just a simple null check | ||
return MissingParameter("data"); | ||
} | ||
if (string.IsNullOrEmpty(request.Path)) | ||
{ | ||
return MissingParameter("path"); | ||
} | ||
var parent = Path.GetDirectoryName(request.Path)!; | ||
if (!Directory.Exists(parent)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To add a little bit of security, we could use a "feature flag" mechanism such as appium-windows-driver to only allow this endpoint if it has been enabled, see https://github.com/appium/appium-windows-driver/blob/master/lib/commands/file-movement.js#L18 |
||
{ | ||
Directory.CreateDirectory(parent); | ||
} | ||
var data = Convert.FromBase64String(request.Data); | ||
await System.IO.File.WriteAllBytesAsync(request.Path, data); | ||
return WebDriverResult.Success(); | ||
} | ||
|
||
|
||
[HttpPost("pull_file")] | ||
public async Task<ActionResult> PullFile([FromBody] PullFileRequest request) | ||
{ | ||
if (string.IsNullOrEmpty(request.Path)) | ||
{ | ||
return MissingParameter("path"); | ||
} | ||
if (!System.IO.File.Exists(request.Path)) { | ||
Check failure Code scanning / CodeQL Uncontrolled data used in path expression High
This path depends on a
user-provided value Error loading related location Loading |
||
return FileNotFound(request.Path); | ||
} | ||
var data = await System.IO.File.ReadAllBytesAsync(request.Path); | ||
Check failure Code scanning / CodeQL Uncontrolled data used in path expression High
This path depends on a
user-provided value Error loading related location Loading |
||
return WebDriverResult.Success(Convert.ToBase64String(data)); | ||
} | ||
|
||
[HttpPost("pull_folder")] | ||
public ActionResult PullFolder([FromBody] PullFileRequest request) | ||
{ | ||
if (string.IsNullOrEmpty(request.Path)) | ||
{ | ||
return MissingParameter("path"); | ||
} | ||
if (!Directory.Exists(request.Path)) | ||
Check failure Code scanning / CodeQL Uncontrolled data used in path expression High
This path depends on a
user-provided value Error loading related location Loading |
||
{ | ||
return FileNotFound(request.Path); | ||
} | ||
byte[] bytes; | ||
using (var ms = new MemoryStream()) | ||
{ | ||
ZipFile.CreateFromDirectory(request.Path, ms); | ||
ms.Seek(0, SeekOrigin.Begin); | ||
bytes = ms.ToArray(); | ||
} | ||
|
||
return WebDriverResult.Success(Convert.ToBase64String(bytes)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,7 @@ | ||||||
namespace FlaUI.WebDriver.Models | ||||||
{ | ||||||
public class PullFileRequest | ||||||
{ | ||||||
public string? Path { get; set; } | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of doing request validation ourselves, wouldn't it be simpler to make the models do that?
Suggested change
|
||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,8 @@ | ||||||||||
namespace FlaUI.WebDriver.Models | ||||||||||
{ | ||||||||||
public class PushFileRequest | ||||||||||
{ | ||||||||||
public string? Path { get; set; } | ||||||||||
public string? Data { get; set; } | ||||||||||
Comment on lines
+5
to
+6
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
} | ||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.