diff --git a/.gitignore b/.gitignore index 0a89a9401..dad9cb625 100644 --- a/.gitignore +++ b/.gitignore @@ -291,6 +291,7 @@ Thumbs.db # ignore local cert /lib/PuppeteerSharp.TestServer/testCert.cer +/lib/PuppeteerSharp.TestServer/testCert.pem /lib/PuppeteerSharp.TestServer/testKey.pem /lib/PuppeteerSharp.Tests/Screenshots/ diff --git a/lib/PuppeteerSharp.Tests/UtilitiesTests/CookiePartitionKeyConverterTests.cs b/lib/PuppeteerSharp.Tests/UtilitiesTests/CookiePartitionKeyConverterTests.cs new file mode 100644 index 000000000..c434125d5 --- /dev/null +++ b/lib/PuppeteerSharp.Tests/UtilitiesTests/CookiePartitionKeyConverterTests.cs @@ -0,0 +1,117 @@ +using System.Text.Json; +using NUnit.Framework; + +namespace PuppeteerSharp.Tests.UtilitiesTests +{ + /// + /// Unit tests for CookiePartitionKeyConverter. These are standalone tests that don't require browsers. + /// + [TestFixture] + public class CookiePartitionKeyConverterTests + { + [Test] + public void ShouldSerializeAndDeserializeAsString() + { + // Arrange + var cookie = new CookieParam + { + Name = "test", + Value = "value", + PartitionKey = "https://example.com" + }; + + // Act - Serialize to JSON + var json = JsonSerializer.Serialize(cookie); + + // Assert - Verify it's serialized as a string + Assert.That(json, Does.Contain("\"PartitionKey\":\"https://example.com\"")); + + // Act - Deserialize back + var deserialized = JsonSerializer.Deserialize(json); + + // Assert - Verify round-trip works + Assert.That(deserialized.PartitionKey, Is.EqualTo("https://example.com")); + } + + [Test] + public void ShouldDeserializeCdpObjectFormat() + { + // Arrange - CDP returns object format + var cdpJson = @"{""Name"":""test"",""Value"":""value"",""PartitionKey"":{""topLevelSite"":""https://example.com"",""hasCrossSiteAncestor"":false}}"; + + // Act + var cookie = JsonSerializer.Deserialize(cdpJson); + + // Assert - Should extract the topLevelSite value + Assert.That(cookie.PartitionKey, Is.EqualTo("https://example.com")); + } + + [Test] + public void ShouldDeserializeStringFormat() + { + // Arrange - User-saved format (simple string) + var stringJson = @"{""Name"":""test"",""Value"":""value"",""PartitionKey"":""https://example.com""}"; + + // Act + var cookie = JsonSerializer.Deserialize(stringJson); + + // Assert + Assert.That(cookie.PartitionKey, Is.EqualTo("https://example.com")); + } + + [Test] + public void ShouldHandleNullPartitionKey() + { + // Arrange + var cookie = new CookieParam + { + Name = "test", + Value = "value", + PartitionKey = null + }; + + // Act + var json = JsonSerializer.Serialize(cookie); + var deserialized = JsonSerializer.Deserialize(json); + + // Assert + Assert.That(deserialized.PartitionKey, Is.Null); + } + + [Test] + public void ShouldSupportRoundTripSerializationToFile() + { + // Arrange - Simulate getting cookies from browser + var originalCookies = new[] + { + new CookieParam + { + Name = "cookie1", + Value = "value1", + Domain = "example.com", + PartitionKey = "https://example.com" + }, + new CookieParam + { + Name = "cookie2", + Value = "value2", + Domain = "test.com", + PartitionKey = "https://test.com" + } + }; + + // Act - Serialize to JSON (simulating saving to file) + var json = JsonSerializer.Serialize(originalCookies); + + // Act - Deserialize from JSON (simulating loading from file) + var loadedCookies = JsonSerializer.Deserialize(json); + + // Assert - Verify all data is preserved + Assert.That(loadedCookies, Has.Length.EqualTo(2)); + Assert.That(loadedCookies[0].Name, Is.EqualTo("cookie1")); + Assert.That(loadedCookies[0].PartitionKey, Is.EqualTo("https://example.com")); + Assert.That(loadedCookies[1].Name, Is.EqualTo("cookie2")); + Assert.That(loadedCookies[1].PartitionKey, Is.EqualTo("https://test.com")); + } + } +} diff --git a/lib/PuppeteerSharp/Helpers/Json/CookiePartitionKeyConverter.cs b/lib/PuppeteerSharp/Helpers/Json/CookiePartitionKeyConverter.cs index 5a4c2584a..d254f9865 100644 --- a/lib/PuppeteerSharp/Helpers/Json/CookiePartitionKeyConverter.cs +++ b/lib/PuppeteerSharp/Helpers/Json/CookiePartitionKeyConverter.cs @@ -18,6 +18,12 @@ internal sealed class CookiePartitionKeyConverter : JsonConverter Type objectType, JsonSerializerOptions options) { + // Handle both string format (for user serialization) and object format (from CDP) + if (reader.TokenType == JsonTokenType.String) + { + return reader.GetString(); + } + JsonNode? node = JsonNode.Parse(ref reader); return node?["topLevelSite"]?.GetValue() ?? null; @@ -29,12 +35,15 @@ public override void Write( string value, JsonSerializerOptions options) { - if (value != null && writer != null) + // Write as a simple string for user serialization/deserialization + // This allows cookies to be easily saved to and loaded from files + if (value != null) + { + writer.WriteStringValue(value); + } + else { - writer.WriteStartObject("partitionKey"); - writer.WriteString("topLevelSite", value); - writer.WriteBoolean("hasCrossSiteAncestor", false); - writer.WriteEndObject(); + writer.WriteNullValue(); } } }