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