Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@ export * from "./acp/mod.js"
export * from "./solid/mod.js"
export * from "./webid/mod.js"


8 changes: 0 additions & 8 deletions src/solid/Meeting.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import { TermMappings, ValueMappings, TermWrapper, DatasetWrapper } from "rdfjs-wrapper"
import { ICAL } from "../vocabulary/mod.js"


export class MeetingDataset extends DatasetWrapper {
get meeting(): Iterable<Meeting> {
return this.instancesOf(ICAL.vevent, Meeting)
}
}

export class Meeting extends TermWrapper {
get summary(): string | undefined {
return this.singularNullable(ICAL.summary, ValueMappings.literalToString)
Expand Down Expand Up @@ -48,5 +41,4 @@ export class Meeting extends TermWrapper {
set endDate(value: Date | undefined) {
this.overwriteNullable(ICAL.dtend, value, TermMappings.dateToLiteral)
}

}
9 changes: 9 additions & 0 deletions src/solid/MeetingDataset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { DatasetWrapper } from "rdfjs-wrapper"
import { ICAL } from "../vocabulary/mod.js"
import { Meeting } from "./Meeting.js"

export class MeetingDataset extends DatasetWrapper {
get meeting(): Iterable<Meeting> {
return this.instancesOf(ICAL.Vevent, Meeting)
}
}
3 changes: 2 additions & 1 deletion src/solid/mod.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./Container.js"
export * from "./ContainerDataset.js"
export * from "./Resource.js"
export * from "./Meeting.js"
export * from "./Meeting.js"
export * from "./MeetingDataset.js"
2 changes: 1 addition & 1 deletion src/vocabulary/ical.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ export const ICAL = {
dtstart: "http://www.w3.org/2002/12/cal/ical#dtstart",
location: "http://www.w3.org/2002/12/cal/ical#location",
summary: "http://www.w3.org/2002/12/cal/ical#summary",
vevent: "http://www.w3.org/2002/12/cal/ical#Vevent"
Vevent: "http://www.w3.org/2002/12/cal/ical#Vevent"
} as const;
2 changes: 1 addition & 1 deletion src/vocabulary/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ export * from "./rdf.js"
export * from "./rdfs.js"
export * from "./solid.js"
export * from "./vcard.js"
export * from "./ical.js"
export * from "./ical.js"
112 changes: 98 additions & 14 deletions test/unit/meeting.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { DataFactory, Parser, Store } from "n3"
import assert from "node:assert"
import { describe, it } from "node:test"

import { MeetingDataset } from "@solid/object";


describe("MeetingDataset / Meeting tests", () => {

const sampleRDF = `
@prefix cal: <http://www.w3.org/2002/12/cal/ical#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
Expand All @@ -31,21 +28,17 @@ describe("MeetingDataset / Meeting tests", () => {
assert.ok(meeting, "No meeting found")

// Check property types and values

assert.equal(meeting.summary, "Team Sync");
assert.equal(meeting.location, "Zoom Room 123");
assert.equal(meeting.comment, "Discuss project updates");


assert.ok(meeting.startDate instanceof Date);
assert.ok(meeting.endDate instanceof Date);

assert.equal(meeting.startDate?.toISOString(), "2026-02-09T10:00:00.000Z");
assert.equal(meeting.endDate?.toISOString(), "2026-02-09T11:00:00.000Z");
});



it("should allow setting of meeting properties", () => {
const store = new Store();
store.addQuads(new Parser().parse(sampleRDF));
Expand Down Expand Up @@ -74,8 +67,6 @@ describe("MeetingDataset / Meeting tests", () => {
assert.equal(meeting.endDate.toISOString(), newEnd.toISOString());
});



it("should ensure all properties are correct type", () => {
const store = new Store();
store.addQuads(new Parser().parse(sampleRDF));
Expand All @@ -86,19 +77,15 @@ describe("MeetingDataset / Meeting tests", () => {
assert.ok(meeting, "No meeting found")

// Check property types

assert.equal(typeof meeting.summary, "string");
assert.equal(typeof meeting.location, "string");
assert.equal(typeof meeting.comment, "string");

assert.ok(meeting.startDate instanceof Date, "startDate should be a Date");
assert.ok(meeting.endDate instanceof Date, "endDate should be a Date");

});


it("should ensure all properties are unique text or date values", () => {

const duplicateRDF = `
@prefix cal: <http://www.w3.org/2002/12/cal/ical#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
Expand Down Expand Up @@ -142,4 +129,101 @@ describe("MeetingDataset / Meeting tests", () => {



});
// RFC 5545 requires DTSTART and UID - test the required date behaviour

it("should parse a minimal RFC5545-style VEVENT", () => {
const minimalRDF = `
@prefix cal: <http://www.w3.org/2002/12/cal/ical#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<https://example.org/meeting/minimal>
a cal:Vevent ;
cal:dtstart "2026-06-01T09:00:00Z"^^xsd:dateTime .
`;

const store = new Store();
store.addQuads(new Parser().parse(minimalRDF));

const dataset = new MeetingDataset(store, DataFactory);
const meeting = Array.from(dataset.meeting)[0];

assert.ok(meeting);
assert.ok(meeting.startDate instanceof Date);
assert.equal(meeting.startDate?.toISOString(), "2026-06-01T09:00:00.000Z");

// Optional fields should be undefined
assert.equal(meeting.summary, undefined);
assert.equal(meeting.location, undefined);
assert.equal(meeting.comment, undefined);
assert.equal(meeting.endDate, undefined);
});


// With ref to common VEVENT examples in RFC 5545 3.6.1.

it("should parse an RFC-style VEVENT with summary, description, and location", () => {
const rfcStyleRDF = `
@prefix cal: <http://www.w3.org/2002/12/cal/ical#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<https://example.org/meeting/rfc1>
a cal:Vevent ;
cal:summary "Board Meeting" ;
cal:comment "Discuss quarterly earnings and projections." ;
cal:location "Conference Room 1" ;
cal:dtstart "2026-07-10T13:00:00Z"^^xsd:dateTime ;
cal:dtend "2026-07-10T15:30:00Z"^^xsd:dateTime .
`;

const store = new Store();
store.addQuads(new Parser().parse(rfcStyleRDF));

const dataset = new MeetingDataset(store, DataFactory);
const meeting = Array.from(dataset.meeting)[0];

assert.ok(meeting);

assert.equal(meeting.summary, "Meeting");
assert.equal(meeting.comment, "Discuss events");
assert.equal(meeting.location, "Conference Room 1");

assert.equal(meeting.startDate?.toISOString(), "2026-07-10T13:00:00.000Z");
assert.equal(meeting.endDate?.toISOString(), "2026-07-10T15:30:00.000Z");
});


// RFC 5545 allows DATE values for all-day events. This tests literalToDate handling.

it("should parse an all-day RFC-style event (DATE value)", () => {
const allDayRDF = `
@prefix cal: <http://www.w3.org/2002/12/cal/ical#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<https://example.org/meeting/allday>
a cal:Vevent ;
cal:summary "Company Holiday" ;
cal:dtstart "2026-12-25"^^xsd:date ;
cal:dtend "2026-12-26"^^xsd:date .
`;

const store = new Store();
store.addQuads(new Parser().parse(allDayRDF));

const dataset = new MeetingDataset(store, DataFactory);
const meeting = Array.from(dataset.meeting)[0];

assert.ok(meeting);
assert.ok(meeting.startDate instanceof Date);
assert.ok(meeting.endDate instanceof Date);

// Ensure correct calendar date
assert.equal(meeting.startDate?.getUTCFullYear(), 2026);
assert.equal(meeting.startDate?.getUTCMonth(), 11); // December (0-based)
assert.equal(meeting.startDate?.getUTCDate(), 25);
});





});