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\n

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