From 439f68b6701e2b9c4482ea05e3497f17296318d7 Mon Sep 17 00:00:00 2001 From: Gitii Date: Fri, 10 Mar 2023 16:52:45 +0100 Subject: [PATCH] Add push and pull handlers (#31) --- src/Infra/CommandHandler/CommandHandlers.cs | 2 + src/Infra/CommandHandler/ICommandHandlers.cs | 2 + src/Infra/CommandHandler/SessionHandlers.cs | 46 +++++ src/Infra/Infra.csproj | 8 +- src/Infra/Request/SessionReqs.cs | 6 + .../Controllers/SessionController.cs | 14 ++ src/WinAppDriver/WinAppDriver.csproj | 10 +- src/WinAppDriver/publish.bat | 2 +- test/Infra.UnitTest/Infra.UnitTest.csproj | 22 ++- .../DeviceTest.cs | 160 ++++++++++++++++++ test/WinAppDriver.IntegrationTest/Helpers.cs | 1 + .../WinAppDriver.IntegrationTest.csproj | 15 +- .../WinAppDriver.UnitTest.csproj | 20 ++- 13 files changed, 277 insertions(+), 31 deletions(-) create mode 100644 test/WinAppDriver.IntegrationTest/DeviceTest.cs diff --git a/src/Infra/CommandHandler/CommandHandlers.cs b/src/Infra/CommandHandler/CommandHandlers.cs index de3b520..410196b 100644 --- a/src/Infra/CommandHandler/CommandHandlers.cs +++ b/src/Infra/CommandHandler/CommandHandlers.cs @@ -41,6 +41,8 @@ public class CommandHandlers: ICommandHandlers { Command.SetWindowSize, new SetWindowSizeHandler() }, { Command.TakeScreenshot, new TakeScreenshotHandler() }, { Command.ClickOnElement, new ClickHandler() }, + { Command.DevicePushFile, new DevicePushFileHandler() }, + { Command.DevicePullFile, new DevicePullFileHandler() } }; public object ExecuteCommand(Command command, ISessionManager sessionManager, string sessionId, object req, string elementId) diff --git a/src/Infra/CommandHandler/ICommandHandlers.cs b/src/Infra/CommandHandler/ICommandHandlers.cs index 5882275..ad30ebc 100644 --- a/src/Infra/CommandHandler/ICommandHandlers.cs +++ b/src/Infra/CommandHandler/ICommandHandlers.cs @@ -37,6 +37,8 @@ public enum Command MaximizeWindow, TakeScreenshot, ClickOnElement, + DevicePushFile, + DevicePullFile } public interface ICommandHandlers diff --git a/src/Infra/CommandHandler/SessionHandlers.cs b/src/Infra/CommandHandler/SessionHandlers.cs index a2b2ea3..76cd8a4 100644 --- a/src/Infra/CommandHandler/SessionHandlers.cs +++ b/src/Infra/CommandHandler/SessionHandlers.cs @@ -7,6 +7,7 @@ using WinAppDriver.Infra.Communication; using WinAppDriver.Infra.Result; using System; +using System.IO; namespace WinAppDriver.Infra.CommandHandler { @@ -248,4 +249,49 @@ protected override object ExecuteSessionCommand(ISessionManager sessionManager, return null; } } + + public class DevicePushFileHandler : SessionCommandHandlerBase + { + protected override object ExecuteSessionCommand(ISessionManager sessionManager, ISession session, PathFileReq req, string elementId) + { + if (string.IsNullOrWhiteSpace(req.path)) + { + throw new ArgumentException($"Invalid path: Null or whitespace"); + } + + if (string.IsNullOrWhiteSpace(req.data)) + { + throw new ArgumentException($"Invalid file content: Null or whitespace"); + } + + byte[] fileContent = Convert.FromBase64String(req.data); + + string directory = Path.GetDirectoryName(req.path); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + File.WriteAllBytes(req.path, fileContent); + return null; + } + } + + public class DevicePullFileHandler : SessionCommandHandlerBase + { + protected override string ExecuteSessionCommand(ISessionManager sessionManager, ISession session, PathFileReq req, string elementId) + { + if (string.IsNullOrWhiteSpace(req.path)) + { + throw new ArgumentException($"Invalid path: Null or whitespace"); + } + + if (!File.Exists(req.path)) + { + throw new FileNotFoundException("The requested file doesn't exist.", req.path); + } + + return Convert.ToBase64String(File.ReadAllBytes(req.path)); + } + } } diff --git a/src/Infra/Infra.csproj b/src/Infra/Infra.csproj index 1885818..13e7918 100644 --- a/src/Infra/Infra.csproj +++ b/src/Infra/Infra.csproj @@ -1,15 +1,15 @@  - netcoreapp3.1 + net6.0 WinAppDriver.Infra - + - - + + diff --git a/src/Infra/Request/SessionReqs.cs b/src/Infra/Request/SessionReqs.cs index ebd5240..a2c7c12 100644 --- a/src/Infra/Request/SessionReqs.cs +++ b/src/Infra/Request/SessionReqs.cs @@ -68,4 +68,10 @@ public class SizeReq public double height; } + + public class PathFileReq + { + public string path { get; set; } + public string data { get; set; } + } } \ No newline at end of file diff --git a/src/WinAppDriver/Controllers/SessionController.cs b/src/WinAppDriver/Controllers/SessionController.cs index 075da43..379b037 100644 --- a/src/WinAppDriver/Controllers/SessionController.cs +++ b/src/WinAppDriver/Controllers/SessionController.cs @@ -395,6 +395,20 @@ public IActionResult UnknownDelete(string sessionId) return ExecuteCommand(Command.UnknownCommand, sessionId, null, null); } + [HttpPost] + [Route("{sessionId}/appium/device/push_file")] + public IActionResult SessionDevicePushFile(string sessionId, [FromBody] object content) + { + return ExecuteCommand(Command.DevicePushFile, sessionId, content, null); + } + + [HttpPost] + [Route("{sessionId}/appium/device/pull_file")] + public IActionResult SessionDevicePullFile(string sessionId, [FromBody] object content) + { + return ExecuteCommand(Command.DevicePullFile, sessionId, content, null); + } + [HttpPost] [Route("{sessionId}/moveto")] public IActionResult SessionMoveTo(string sessionId, [FromBody] object content) diff --git a/src/WinAppDriver/WinAppDriver.csproj b/src/WinAppDriver/WinAppDriver.csproj index 452b532..db52807 100644 --- a/src/WinAppDriver/WinAppDriver.csproj +++ b/src/WinAppDriver/WinAppDriver.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net6.0 0.2.2.0 $(Version) $(Version) @@ -11,10 +11,10 @@ - - - - + + + + diff --git a/src/WinAppDriver/publish.bat b/src/WinAppDriver/publish.bat index 0071303..1404fe0 100644 --- a/src/WinAppDriver/publish.bat +++ b/src/WinAppDriver/publish.bat @@ -1,2 +1,2 @@ -del /f /q /s bin\Release\netcoreapp3.1\win-x64 +del /f /q /s bin\Release\net6.0\win-x64 dotnet publish -c Release -r win-x64 diff --git a/test/Infra.UnitTest/Infra.UnitTest.csproj b/test/Infra.UnitTest/Infra.UnitTest.csproj index e549e1a..fb4f56b 100644 --- a/test/Infra.UnitTest/Infra.UnitTest.csproj +++ b/test/Infra.UnitTest/Infra.UnitTest.csproj @@ -1,19 +1,25 @@  - netcoreapp3.1 + net6.0 false - - - - - - - + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/test/WinAppDriver.IntegrationTest/DeviceTest.cs b/test/WinAppDriver.IntegrationTest/DeviceTest.cs new file mode 100644 index 0000000..e95ce3f --- /dev/null +++ b/test/WinAppDriver.IntegrationTest/DeviceTest.cs @@ -0,0 +1,160 @@ +using FluentAssertions; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Razor; +using Newtonsoft.Json.Linq; +using WinAppDriver.Infra; +using WinAppDriver.Infra.Request; +using WinAppDriver.Infra.Result; +using Xunit; + +namespace WinAppDriver.IntegrationTest +{ + [Collection("Sequential")] + public class DeviceTest: IDisposable + { + public readonly string _tempDirectory; + public readonly string _uniqueTempDirectory; + + public DeviceTest() + { + _tempDirectory = Path.GetTempPath(); + _uniqueTempDirectory = Path.Combine(_tempDirectory, Guid.NewGuid().ToString()); + + Directory.CreateDirectory(_uniqueTempDirectory); + } + + [Theory] + [InlineData("file.bin")] + [InlineData("foo\\bar\\file.bin")] + public async Task Test_PushFile_UploadFileToRemote(string partialFilePath) + { + using (var client = new TestClientProvider().Client) + { + var sessionId = await Helpers.CreateNewSession(client, AppIds.WinVer); + sessionId.Should().NotBeNullOrEmpty(); + + try + { + var fileContent = Guid.NewGuid().ToByteArray(); + var filePath = Path.Combine(_uniqueTempDirectory, partialFilePath); + + var res = await Helpers.PostSessionMessage(client, sessionId, "appium/device/push_file", + new PathFileReq() { data = Convert.ToBase64String(fileContent), path = filePath }); + + res.statusCode.Should().Be(HttpStatusCode.OK); + + File.Exists(filePath).Should().BeTrue(); + (await File.ReadAllBytesAsync(filePath)).Should().BeEquivalentTo(fileContent); + } + finally + { + + await Helpers.DeletSession(client, sessionId); + } + } + } + + [Theory] + [InlineData("", "content", "Invalid path: Null or whitespace")] // path empty + [InlineData("C:\\file.bin", "", "Invalid file content: Null or whitespace")] // empty content + [InlineData("C:\\file.bin", "???????", "The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.")] // invalid content + public async Task Test_PushFile_InvalidPayload(string filePath, string fileContent, string errorMessage) + { + using (var client = new TestClientProvider().Client) + { + var sessionId = await Helpers.CreateNewSession(client, AppIds.WinVer); + sessionId.Should().NotBeNullOrEmpty(); + + try + { + var res = await Helpers.PostSessionMessage(client, sessionId, "appium/device/push_file", + new PathFileReq() { data = fileContent, path = filePath }); + + res.statusCode.Should().Be(HttpStatusCode.InternalServerError); + res.value.Should().BeOfType(); + + var resValue = (JObject)res.value; + + resValue.Value("error").Should().BeEquivalentTo("UnknownError"); + resValue.Value("message").Should().BeEquivalentTo(errorMessage); + } + finally + { + await Helpers.DeletSession(client, sessionId); + } + } + } + + [Fact] + public async Task Test_PullFile_DownloadFileFromRemote() + { + using (var client = new TestClientProvider().Client) + { + var sessionId = await Helpers.CreateNewSession(client, AppIds.WinVer); + sessionId.Should().NotBeNullOrEmpty(); + + try + { + var fileContent = Guid.NewGuid().ToByteArray(); + var filePath = Path.Combine(_uniqueTempDirectory, "file.bin"); + File.WriteAllBytes(filePath, fileContent); + + var res = await Helpers.PostSessionMessage(client, sessionId, "appium/device/pull_file", + new PathFileReq() { path = filePath }); + + res.statusCode.Should().Be(HttpStatusCode.OK); + res.value.Should().BeEquivalentTo(Convert.ToBase64String(fileContent)); + } + finally + { + + await Helpers.DeletSession(client, sessionId); + } + } + } + + [Theory] + [InlineData("", "Invalid path: Null or whitespace")] // path empty + [InlineData("C:\\file.bin", "The requested file doesn't exist.")] // file doesn't exist + public async Task Test_PullFile_InvalidPayload(string filePath, string errorMessage) + { + using (var client = new TestClientProvider().Client) + { + var sessionId = await Helpers.CreateNewSession(client, AppIds.WinVer); + sessionId.Should().NotBeNullOrEmpty(); + + try + { + var res = await Helpers.PostSessionMessage(client, sessionId, "appium/device/pull_file", + new PathFileReq() { path = filePath }); + + res.statusCode.Should().Be(HttpStatusCode.InternalServerError); + res.value.Should().BeOfType(); + + var resValue = (JObject)res.value; + + resValue.Value("error").Should().BeEquivalentTo("UnknownError"); + resValue.Value("message").Should().BeEquivalentTo(errorMessage); + } + finally + { + await Helpers.DeletSession(client, sessionId); + } + } + } + + public void Dispose() + { + if (Directory.Exists(_uniqueTempDirectory)) + { + Directory.Delete(_uniqueTempDirectory, true); + } + } + } +} diff --git a/test/WinAppDriver.IntegrationTest/Helpers.cs b/test/WinAppDriver.IntegrationTest/Helpers.cs index 61ddbf7..f781ae2 100644 --- a/test/WinAppDriver.IntegrationTest/Helpers.cs +++ b/test/WinAppDriver.IntegrationTest/Helpers.cs @@ -32,6 +32,7 @@ class AppIds public const string Edge = "Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge"; public const string AlarmClock = "Microsoft.WindowsAlarms_8wekyb3d8bbwe!App"; public const string Notepad = "c:\\windows\\system32\\notepad.exe"; + public const string WinVer = "C:\\Windows\\System32\\winver.exe"; } class Helpers { diff --git a/test/WinAppDriver.IntegrationTest/WinAppDriver.IntegrationTest.csproj b/test/WinAppDriver.IntegrationTest/WinAppDriver.IntegrationTest.csproj index 59931bc..bf5a88f 100644 --- a/test/WinAppDriver.IntegrationTest/WinAppDriver.IntegrationTest.csproj +++ b/test/WinAppDriver.IntegrationTest/WinAppDriver.IntegrationTest.csproj @@ -1,17 +1,20 @@  - netcoreapp3.1 + net6.0 false - - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/test/WinAppDriver.UnitTest/WinAppDriver.UnitTest.csproj b/test/WinAppDriver.UnitTest/WinAppDriver.UnitTest.csproj index d90f3c0..12fcc58 100644 --- a/test/WinAppDriver.UnitTest/WinAppDriver.UnitTest.csproj +++ b/test/WinAppDriver.UnitTest/WinAppDriver.UnitTest.csproj @@ -1,18 +1,24 @@  - netcoreapp3.1 + net6.0 false - - - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all +