diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 0ca43fdb..35e944cc 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -11,6 +11,7 @@ _Put an `x` in the boxes that apply_
- [ ] New feature (non-breaking change that adds functionality or value)
- [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected)
- [ ] **Test fix** (non-breaking change that improves test stability or correctness)
+- [ ] Chore/Maintenance (updates to build scripts, dependencies, or GitHub Actions)
## Documentation
- [ ] Have you proposed a file change/ PR with Appium to update documentation?
diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml
index 1d8400fc..d152e77a 100644
--- a/.github/release-drafter.yml
+++ b/.github/release-drafter.yml
@@ -48,5 +48,4 @@ version-resolver:
default: patch
prerelease: false
-prerelease-identifier: 'rc'
contribution-template: '- $NAME contributed via $PULL_REQUEST'
diff --git a/README.md b/README.md
index b3d5f66a..0bb398fc 100644
--- a/README.md
+++ b/README.md
@@ -7,8 +7,6 @@
----
[](https://github.com/appium/dotnet-client/actions/workflows/functional-test.yml)
-[](https://github.com/appium/dotnet-client/actions/workflows/functional-android-test.yml)
-[](https://github.com/appium/dotnet-client/actions/workflows/functional-ios-test.yml)
[](https://github.com/appium/dotnet-client/actions/workflows/unit-test.yml)
----
diff --git a/src/Appium.Net/Appium.Net.csproj b/src/Appium.Net/Appium.Net.csproj
index 869757a2..8c9ced10 100644
--- a/src/Appium.Net/Appium.Net.csproj
+++ b/src/Appium.Net/Appium.Net.csproj
@@ -43,7 +43,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/src/Appium.Net/Appium/Service/DirectConnect.cs b/src/Appium.Net/Appium/Service/DirectConnect.cs
index 6f5cd220..8d6b7dd2 100644
--- a/src/Appium.Net/Appium/Service/DirectConnect.cs
+++ b/src/Appium.Net/Appium/Service/DirectConnect.cs
@@ -1,4 +1,4 @@
-//Licensed under the Apache License, Version 2.0 (the "License");
+//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//See the NOTICE file distributed with this work for additional
//information regarding copyright ownership.
@@ -42,21 +42,37 @@ public DirectConnect(Response response)
this.Path = GetDirectConnectValue((Dictionary)response.Value, DIRECT_CONNECT_PATH);
}
+ ///
+ /// Gets a value indicating whether this instance has the required members for attempting URI construction.
+ ///
+ private bool IsValid =>
+ this.Protocol != null &&
+ this.Host != null &&
+ this.Port != null &&
+ this.Path != null &&
+ this.Protocol.Equals("https", StringComparison.OrdinalIgnoreCase) &&
+ int.TryParse(this.Port, out int port) && port >= 0 && port <= 65535;
+
///
/// Returns a URL instance built with members in the DirectConnect instance.
///
/// A Uri instance
- public Uri GetUri() {
- if (this.Protocol == null || this.Host == null || this.Port == null || this.Path == null) {
+ public Uri GetUri()
+ {
+ if (!this.IsValid)
+ {
return null;
}
- if (this.Protocol != "https")
+ try
+ {
+ var builder = new UriBuilder(this.Protocol, this.Host, int.Parse(this.Port), this.Path);
+ return builder.Uri;
+ }
+ catch (UriFormatException)
{
return null;
}
-
- return new Uri(this.Protocol + "://" + this.Host + ":" + this.Port + this.Path);
}
///
diff --git a/test/integration/ImageComparison/ComparisonResultTests.cs b/test/integration/ImageComparison/ComparisonResultTests.cs
index bb75c165..7f64d532 100644
--- a/test/integration/ImageComparison/ComparisonResultTests.cs
+++ b/test/integration/ImageComparison/ComparisonResultTests.cs
@@ -63,13 +63,13 @@ public void TearDown()
[Test]
public void SaveVisualizationAsFile_NullFileName_ThrowsArgumentNullException()
{
- Assert.Throws(() => _comparisonResult.SaveVisualizationAsFile(null));
+ Assert.Throws((Action)(() => _comparisonResult.SaveVisualizationAsFile(null)));
}
[Test]
public void SaveVisualizationAsFile_EmptyFileName_ThrowsArgumentException()
{
- var ex = Assert.Throws(() => _comparisonResult.SaveVisualizationAsFile(""));
+ var ex = Assert.Throws((Action)(() => _comparisonResult.SaveVisualizationAsFile("")));
Assert.That(ex.Message, Does.Contain("The file name must not be an empty string."));
}
@@ -86,7 +86,7 @@ public void SaveVisualizationAsFile_InvalidCharacters_ThrowsArgumentException()
}
string invalidFileName = "test" + invalidChars[0] + ".png";
- var ex = Assert.Throws(() => _comparisonResult.SaveVisualizationAsFile(invalidFileName));
+ var ex = Assert.Throws((Action)(() => _comparisonResult.SaveVisualizationAsFile(invalidFileName)));
Assert.That(ex.Message, Does.Contain("The file name contains invalid characters"));
}
@@ -101,7 +101,7 @@ public void SaveVisualizationAsFile_ReservedDeviceName_ThrowsArgumentException()
var invalidNames = new[] { "CON.png", "CON .png", "NUL.", "PRN " };
foreach (var name in invalidNames)
{
- var ex = Assert.Throws(() => _comparisonResult.SaveVisualizationAsFile(name));
+ var ex = Assert.Throws((Action)(() => _comparisonResult.SaveVisualizationAsFile(name)));
Assert.That(ex.Message, Does.Contain("reserved device name"));
}
}
@@ -135,14 +135,14 @@ public void SaveVisualizationAsFile_ValidSubdirectoryFileName_SavesFile()
public void SaveVisualizationAsFile_PathTraversalParentDirectory_ThrowsIOException()
{
string fileName = "../traversal_image.png";
- Assert.Throws(() => _comparisonResult.SaveVisualizationAsFile(fileName));
+ Assert.Throws((Action)(() => _comparisonResult.SaveVisualizationAsFile(fileName)));
}
[Test]
public void SaveVisualizationAsFile_AbsolutePathOutsideAllowed_ThrowsIOException()
{
string absolutePath = Path.Combine(Path.GetTempPath(), "absolute_image.png");
- Assert.Throws(() => _comparisonResult.SaveVisualizationAsFile(absolutePath));
+ Assert.Throws((Action)(() => _comparisonResult.SaveVisualizationAsFile(absolutePath)));
}
[Test]
@@ -181,7 +181,7 @@ public void SaveVisualizationAsFile_SymlinkTraversal_ThrowsIOException()
// Attempt to write a file via the symlink
string fileName = Path.Combine("local_sub", "symlink_dir", "image.png");
- var ex = Assert.Throws(() => _comparisonResult.SaveVisualizationAsFile(fileName));
+ var ex = Assert.Throws((Action)(() => _comparisonResult.SaveVisualizationAsFile(fileName)));
Assert.That(ex.Message, Does.Contain("symbolic link or reparse point"));
#else
Assert.Ignore("Skipping symlink test: Symbolic links are not supported on this target framework.");
@@ -228,7 +228,7 @@ public void SaveVisualizationAsFile_BrokenFileSymlinkTraversal_ThrowsIOException
return;
}
- var ex = Assert.Throws(() => _comparisonResult.SaveVisualizationAsFile("broken_file_link.png"));
+ var ex = Assert.Throws((Action)(() => _comparisonResult.SaveVisualizationAsFile("broken_file_link.png")));
Assert.That(ex.Message, Does.Contain("symbolic link or reparse point"));
#else
Assert.Ignore("Skipping symlink test: Symbolic links are not supported on this target framework.");
@@ -241,7 +241,7 @@ public void SaveVisualizationAsFile_AlternateDataStreamsOnWindows_ThrowsArgument
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
string invalidFileName = "file.png:stream";
- var ex = Assert.Throws(() => _comparisonResult.SaveVisualizationAsFile(invalidFileName));
+ var ex = Assert.Throws((Action)(() => _comparisonResult.SaveVisualizationAsFile(invalidFileName)));
Assert.That(ex.Message, Does.Contain("alternate data streams"));
}
else