diff --git a/Ical.Net.Tests/CalendarPropertiesTest.cs b/Ical.Net.Tests/CalendarPropertiesTest.cs
index e8168eef..734b14ac 100644
--- a/Ical.Net.Tests/CalendarPropertiesTest.cs
+++ b/Ical.Net.Tests/CalendarPropertiesTest.cs
@@ -32,13 +32,11 @@ public void AddPropertyShouldNotIncludePropertyNameInValue()
}
[Test]
- [Ignore("Calendar properties aren't being properly serialized")]
- public void PropertySerialization_Tests()
+ public void PropertySerialization()
{
- const string formatted =
- @"FMTTYPE=text/html:\n\n
\n\n\n\n\n\n\nThis is some HTML formatted text.
\n\n\n";
-
- var start = DateTime.Now;
+ const string propValue =
+ """BodyText""";
+ var start = DateTime.UtcNow;
var end = start.AddHours(1);
var @event = new CalendarEvent
{
@@ -46,13 +44,56 @@ public void PropertySerialization_Tests()
End = new CalDateTime(end),
Description = "This is a description",
};
- var property = new CalendarProperty("X-ALT-DESC", formatted);
+ var property = new CalendarProperty("X-ALT-DESC", propValue);
+ // Parameters most not be part of the value
+ property.Parameters.Add("FMTTYPE", "text/html");
+ var property2 = new CalendarProperty("ATTENDEE", "mailto:janedoe@example.com");
+ property2.Parameters.Add("MEMBER", "mailto:projectA@example.com,mailto:projectB@example.com");
+ @event.Properties.Add(property2);
@event.AddProperty(property);
var calendar = new Calendar();
calendar.Events.Add(@event);
var serialized = new CalendarSerializer().SerializeToString(calendar);
- Assert.That(serialized, Does.Contain("X-ALT-DESC;"));
+ Assert.That(serialized, Does.Contain($"X-ALT-DESC;FMTTYPE=text/html:{propValue}"),
+ "Parameter and value should get serialized standard compliant");
+ }
+
+ [Test]
+ public void PropertyDeserialization()
+ {
+ var ics = """
+ BEGIN:VCALENDAR
+ PRODID:-//github.com/ical-org/ical.net//NONSGML ical.net 5.0.0.0//EN
+ VERSION:2.0
+ BEGIN:VEVENT
+ DESCRIPTION:This is a description
+ DTEND:20250518T145922Z
+ DTSTAMP:20250518T135922Z
+ DTSTART:20250518T135922Z
+ SEQUENCE:0
+ UID:8f7aa9fc-e9d7-4276-8bf6-915dfe168f0d
+ X-ALT-DESC;FMTTYPE=text/html:BodyText
+ X-PROJECTS;PROP=name;PRIO=high:ProjectA,ProjectB
+ END:VEVENT
+ END:VCALENDAR
+ """;
+ var calendar = Calendar.Load(ics)!;
+ var calEvent = calendar.Events.First();
+ var propDescription = calEvent.Properties.FirstOrDefault(x => x.Name == "X-ALT-DESC")!;
+ var propProjects = calEvent.Properties.FirstOrDefault(x => x.Name == "X-PROJECTS")!;
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(propDescription.Parameters, Has.Count.EqualTo(1));
+ Assert.That(propDescription.Value, Is.EqualTo("BodyText"));
+ Assert.That(propDescription.Parameters.FirstOrDefault(p => p.Name == "FMTTYPE")!.Value, Is.EqualTo("text/html"));
+
+ Assert.That(propProjects.Parameters, Has.Count.EqualTo(2), "Parameter list should have 2 elements");
+ Assert.That(propProjects.Value, Is.EqualTo("ProjectA,ProjectB"));
+ Assert.That(propProjects.Parameters.FirstOrDefault(p => p.Name == "PROP")!.Value!.ToString(), Is.EqualTo("name"));
+ Assert.That(propProjects.Parameters.FirstOrDefault(p => p.Name == "PRIO")!.Value!.ToString(), Is.EqualTo("high"));
+ });
}
[Test]
diff --git a/Ical.Net.Tests/Calendars/Serialization/Categories1.ics b/Ical.Net.Tests/Calendars/Serialization/Categories1.ics
index 26cb68c7..fe1e8368 100644
--- a/Ical.Net.Tests/Calendars/Serialization/Categories1.ics
+++ b/Ical.Net.Tests/Calendars/Serialization/Categories1.ics
@@ -1,25 +1,25 @@
BEGIN:VCALENDAR
VERSION:2.0
-PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
+PRODID:-//Mozilla.org/NONSGML Mozilla Calendar//EN
BEGIN:VEVENT
-CREATED:20060717T210517Z
-LAST-MODIFIED:20060717T210718Z
-DTSTAMP:20060717T210718Z
+CREATED:20250717T210517Z
+LAST-MODIFIED:20250717T210718Z
+DTSTAMP:20250717T210718Z
UID:uuid1153170430406
SUMMARY:Test event
CATEGORIES:One,Two,Three
CATEGORIES:Four,Five,Six
CATEGORIES;ENCODING=BASE64:U2V2ZW4=
-CATEGORIES:A string of text with nothing less than a comma\, semicolon\; and a newline\n.
-DTSTART;TZID=US-Eastern:20060902T090000
-RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=20061224T000000Z;WKST=SU;BYDAY=MO,WE,FR
-EXDATE:20060914T050000/PT2H
-DTEND;TZID=US-Eastern:20060902T100000
+CATEGORIES:A string of text with comma\, semicolon\; and a newline\n.
+DTSTART;TZID=US-Eastern:20250902T090000
+RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=20251224T000000Z;WKST=SU;BYDAY=MO,WE,FR
+EXDATE:20250914
+DTEND;TZID=US-Eastern:20250902T100000
LOCATION:Daywest
END:VEVENT
BEGIN:VTIMEZONE
TZID:US-Eastern
-LAST-MODIFIED:19870101T000000Z
+LAST-MODIFIED:20250717T210517Z
TZURL:http://zones.stds_r_us.net/tz/US-Eastern
BEGIN:STANDARD
DTSTART:19671029T020000
diff --git a/Ical.Net.Tests/DeserializationTests.cs b/Ical.Net.Tests/DeserializationTests.cs
index 8a805bff..c034155a 100644
--- a/Ical.Net.Tests/DeserializationTests.cs
+++ b/Ical.Net.Tests/DeserializationTests.cs
@@ -162,17 +162,17 @@ public void CaseInsensitive4()
[Test]
public void Categories1_2()
{
- var iCal = Calendar.Load(IcsFiles.Categories1);
+ var iCal = Calendar.Load(IcsFiles.Categories1)!;
ProgramTest.TestCal(iCal);
var evt = iCal.Events.First();
var items = new List();
- items.AddRange(new[]
- {
+ items.AddRange([
"One", "Two", "Three",
"Four", "Five", "Six",
- "Seven", "A string of text with nothing less than a comma, semicolon; and a newline\n."
- });
+ "Seven", // Category has parameter ENCODING=BASE64:U2V2ZW4=
+ "A string of text with comma, semicolon; and a newline\n."
+ ]);
var found = new Dictionary();
foreach (var s in evt.Categories.Where(s => items.Contains(s)))
@@ -180,7 +180,7 @@ public void Categories1_2()
found[s] = true;
}
- foreach (string item in items)
+ foreach (var item in items)
{
Assert.That(found.ContainsKey(item), Is.True, "Event should contain CATEGORY '" + item + "', but it was not found.");
}
diff --git a/Ical.Net.Tests/SerializationTests.cs b/Ical.Net.Tests/SerializationTests.cs
index 7c6b6d72..80e92ced 100644
--- a/Ical.Net.Tests/SerializationTests.cs
+++ b/Ical.Net.Tests/SerializationTests.cs
@@ -236,7 +236,7 @@ public void SerializeDeserialize()
cal1.Events.Add(evt);
var serializer = new CalendarSerializer();
- var serializedCalendar = serializer.SerializeToString(cal1);
+ var serializedCalendar = serializer.SerializeToString(cal1)!;
var cal2 = Calendar.Load(serializedCalendar);
CompareCalendars(cal1, cal2);
}
@@ -253,24 +253,23 @@ public void EventPropertiesSerialized()
var evt = new CalendarEvent
{
Class = "PRIVATE",
- Created = new CalDateTime(2010, 3, 25, 12, 53, 35),
- DtStamp = new CalDateTime(2010, 3, 25, 12, 53, 35),
- LastModified = new CalDateTime(2010, 3, 27, 13, 53, 35),
+ Created = new CalDateTime(2025, 3, 25, 12, 53, 35),
+ DtStamp = new CalDateTime(2025, 3, 25, 12, 53, 35),
+ LastModified = new CalDateTime(2025, 3, 27, 13, 53, 35),
Sequence = 0,
Uid = "42f58d4f-847e-46f8-9f4a-ce52697682cf",
Priority = 5,
Location = "here",
Summary = "test",
- DtStart = new CalDateTime(2012, 3, 25, 12, 50, 00),
- DtEnd = new CalDateTime(2012, 3, 25, 13, 10, 00)
- //not yet testing property below as serialized output currently does not comply with RTFC 2445
- //Transparency = TransparencyType.Opaque,
- //Status = EventStatus.Confirmed
+ DtStart = new CalDateTime(2025, 3, 25, 12, 50, 00),
+ DtEnd = new CalDateTime(2025, 3, 25, 13, 10, 00),
+ Transparency = TransparencyType.Opaque,
+ Status = EventStatus.Confirmed
};
cal.Events.Add(evt);
var serializer = new CalendarSerializer();
- var serializedCalendar = serializer.SerializeToString(cal);
+ var serializedCalendar = serializer.SerializeToString(cal)!;
Assert.Multiple(() =>
{
@@ -282,18 +281,21 @@ public void EventPropertiesSerialized()
foreach (var p in expectProperties)
{
- Assert.That(serializedCalendar, Does.Contain(SerializationConstants.LineBreak + p + SerializationConstants.LineBreak), "expected '" + p + "' not found");
+ Assert.That(serializedCalendar,
+ Does.Contain(SerializationConstants.LineBreak + p + SerializationConstants.LineBreak),
+ "expected '" + p + "' not found");
}
InspectSerializedSection(serializedCalendar, "VEVENT",
- new[]
- {
- "CLASS:" + evt.Class, "CREATED:" + CalDateString(evt.Created), "DTSTAMP:" + CalDateString(evt.DtStamp),
- "LAST-MODIFIED:" + CalDateString(evt.LastModified), "SEQUENCE:" + evt.Sequence, "UID:" + evt.Uid, "PRIORITY:" + evt.Priority,
- "LOCATION:" + evt.Location, "SUMMARY:" + evt.Summary, "DTSTART:" + CalDateString(evt.DtStart), "DTEND:" + CalDateString(evt.DtEnd)
- //"TRANSPARENCY:" + TransparencyType.Opaque.ToString().ToUpperInvariant(),
- //"STATUS:" + EventStatus.Confirmed.ToString().ToUpperInvariant()
- });
+ [
+ "CLASS:" + evt.Class, "CREATED:" + CalDateString(evt.Created), "DTSTAMP:" + CalDateString(evt.DtStamp),
+ "LAST-MODIFIED:" + CalDateString(evt.LastModified), "SEQUENCE:" + evt.Sequence, "UID:" + evt.Uid,
+ "PRIORITY:" + evt.Priority,
+ "LOCATION:" + evt.Location, "SUMMARY:" + evt.Summary, "DTSTART:" + CalDateString(evt.DtStart),
+ "DTEND:" + CalDateString(evt.DtEnd),
+ "TRANSP:" + TransparencyType.Opaque.ToUpperInvariant(),
+ "STATUS:" + EventStatus.Confirmed.ToUpperInvariant()
+ ]);
}
private static readonly IList _attendees = new List
@@ -365,7 +367,7 @@ public void AttendeesSerialized()
public void ZeroDuration_Test()
{
var result = new DurationSerializer().SerializeToString(Duration.Zero);
- Assert.That("P0D".Equals(result, StringComparison.Ordinal), Is.True);
+ Assert.That(result, Is.EqualTo("P0D"));
}
[Test]
diff --git a/Ical.Net.Tests/SimpleDeserializationTests.cs b/Ical.Net.Tests/SimpleDeserializationTests.cs
index c8403429..19699ffa 100644
--- a/Ical.Net.Tests/SimpleDeserializationTests.cs
+++ b/Ical.Net.Tests/SimpleDeserializationTests.cs
@@ -163,31 +163,6 @@ public void CaseInsensitive4()
Assert.That(iCal.Version, Is.EqualTo("2.5"));
}
- [Test, Category("Deserialization")]
- public void Categories1_2()
- {
- var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Categories1)).Cast().Single();
- ProgramTest.TestCal(iCal);
- var evt = iCal.Events.First();
-
- var items = new List();
- items.AddRange(new[]
- {
- "One", "Two", "Three",
- "Four", "Five", "Six",
- "Seven", "A string of text with nothing less than a comma, semicolon; and a newline\n."
- });
-
- var found = new Dictionary();
- foreach (var s in evt.Categories.Where(s => items.Contains(s)))
- {
- found[s] = true;
- }
-
- foreach (string item in items)
- Assert.That(found.ContainsKey(item), Is.True, "Event should contain CATEGORY '" + item + "', but it was not found.");
- }
-
[Test, Category("Deserialization")]
public void EmptyLines1()
{
diff --git a/Ical.Net/CalendarComponents/CalendarComponent.cs b/Ical.Net/CalendarComponents/CalendarComponent.cs
index 24bccb3d..95e860af 100644
--- a/Ical.Net/CalendarComponents/CalendarComponent.cs
+++ b/Ical.Net/CalendarComponents/CalendarComponent.cs
@@ -75,6 +75,6 @@ public virtual void AddProperty(string name, string value)
public virtual void AddProperty(ICalendarProperty p)
{
p.Parent = this;
- Properties.Set(p.Name, p.Value);
+ Properties.Add(p);
}
}