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

Add device endpoints to push/pull files and pull directory. #96

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions src/FlaUI.WebDriver.UITests/DeviceTests.cs
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()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public void PushFile_FileExist_FileIsOverwritten()
public void PushFile_FileExists_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()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public void PullFolder_FolderDoesNotExists_ErrorRaised()
public void PullFolder_FolderDoesNotExist_ErrorRaised()

{
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);
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 PullFolder with newlines and remove other newlines. See also other tests.

Suggested change
var zipBytes = driver.PullFolder(dir);
var zipBytes = driver.PullFolder(dir);

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);
}



}
}
96 changes: 96 additions & 0 deletions src/FlaUI.WebDriver/Controllers/DeviceController.cs
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))
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
.
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
.
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
.
{
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));
}
}
}
7 changes: 7 additions & 0 deletions src/FlaUI.WebDriver/Models/PullFileRequest.cs
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; }
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
public string? Path { get; set; }
public string Path { get; set; }

}
}
8 changes: 8 additions & 0 deletions src/FlaUI.WebDriver/Models/PushFileRequest.cs
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public string? Path { get; set; }
public string? Data { get; set; }
public string Path { get; set; }
public string Data { get; set; }

}
}
Loading