@inrupt/solid-client
Version:
Make your web apps work with Solid Pods.
1,781 lines (1,585 loc) • 94 kB
text/typescript
// Copyright Inrupt Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { describe, it, expect } from "@jest/globals";
import { dataset } from "@rdfjs/dataset";
import { DataFactory } from "n3";
import type {
IriString,
Thing,
ThingPersisted,
UrlString,
} from "../interfaces";
import {
removeAll,
removeUrl,
removeBoolean,
removeDatetime,
removeDate,
removeTime,
removeDecimal,
removeInteger,
removeStringEnglish,
removeStringWithLocale,
removeStringNoLocale,
removeLiteral,
removeNamedNode,
} from "./remove";
import { mockThingFrom } from "./mock";
import {
ValidPropertyUrlExpectedError,
ValidValueUrlExpectedError,
} from "./thing";
import { localNodeSkolemPrefix } from "../rdf.internal";
function getMockThingWithLiteralFor(
predicate: IriString,
literalValue: string,
literalType:
| "string"
| "integer"
| "decimal"
| "boolean"
| "dateTime"
| "date"
| "time",
): Thing {
return {
type: "Subject",
url: "https://arbitrary.vocab/subject",
predicates: {
[predicate]: {
literals: {
[`http://www.w3.org/2001/XMLSchema#${literalType}`]: [literalValue],
},
},
},
};
}
function getMockThingWithNamedNode(
predicate: IriString,
object: IriString,
): Thing {
return {
type: "Subject",
url: "https://arbitrary.vocab/subject",
predicates: {
[predicate]: {
namedNodes: [object],
},
},
};
}
describe("removeAll", () => {
it("removes all values for the given Predicate", () => {
const thingWithStringAndIri = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"Arbitrary string value",
"string",
);
(thingWithStringAndIri.predicates["https://some.vocab/predicate"]
.namedNodes as UrlString[]) = ["https://arbitrary.vocab/predicate"];
const updatedThing = removeAll(
thingWithStringAndIri,
"https://some.vocab/predicate",
);
expect(
updatedThing.predicates["https://some.vocab/predicate"],
).toBeUndefined();
});
it("accepts Properties as Named Nodes", () => {
const thingWithString = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"Arbitrary string value",
"string",
);
const updatedThing = removeAll(
thingWithString,
DataFactory.namedNode("https://some.vocab/predicate"),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"],
).toBeUndefined();
});
it("does not modify the input Thing", () => {
const thingWithString = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"Arbitrary string value",
"string",
);
const updatedThing = removeAll(
thingWithString,
DataFactory.namedNode("https://some.vocab/predicate"),
);
expect(thingWithString).not.toStrictEqual(updatedThing);
expect(
thingWithString.predicates["https://some.vocab/predicate"],
).toBeDefined();
expect(
updatedThing.predicates["https://some.vocab/predicate"],
).toBeUndefined();
});
it("does nothing if there was nothing to remove", () => {
const thingWithString = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"Arbitrary string value",
"string",
);
const updatedThing = removeAll(
thingWithString,
DataFactory.namedNode("https://some.vocab/other-predicate"),
);
expect(thingWithString).toStrictEqual(updatedThing);
expect(
updatedThing.predicates["https://some.vocab/predicate"],
).toBeDefined();
});
it("also works on ThingLocals", () => {
const thingLocal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"Arbitrary string value",
"string",
);
(thingLocal.url as string) = `${localNodeSkolemPrefix}arbitrary-subject-name`;
const updatedThing = removeAll(thingLocal, "https://some.vocab/predicate");
expect(
updatedThing.predicates["https://some.vocab/predicate"],
).toBeUndefined();
});
it("removes multiple instances of the same value for the same Predicate", () => {
const thingWithDuplicateIri = getMockThingWithNamedNode(
"https://some.vocab/predicate",
"https://arbitrary.pod/resource#name",
);
(
thingWithDuplicateIri.predicates["https://some.vocab/predicate"]
.namedNodes as UrlString[]
).push("https://arbitrary.pod/resource#name");
const updatedThing = removeAll(
thingWithDuplicateIri,
"https://some.vocab/predicate",
);
expect(
updatedThing.predicates["https://some.vocab/predicate"],
).toBeUndefined();
});
it("does not remove Quads with different Predicates", () => {
const thingWithIri = getMockThingWithNamedNode(
"https://some.vocab/predicate",
"https://arbitrary.pod/resource#name",
);
(thingWithIri.predicates["https://some-other.vocab/predicate"] as {
namedNodes: UrlString[];
}) = {
namedNodes: ["https://arbitrary.pod/resource#name"],
};
const updatedThing = removeAll(
thingWithIri,
"https://some.vocab/predicate",
);
expect(
updatedThing.predicates["https://some-other.vocab/predicate"],
).toBeDefined();
});
it("throws an error when passed something other than a Thing", () => {
expect(() =>
removeAll(null as unknown as Thing, "https://arbitrary.vocab/predicate"),
).toThrow("Expected a Thing, but received: [null].");
});
it("throws an error when passed an invalid property URL", () => {
expect(() =>
removeAll(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
),
).toThrow(
"Expected a valid URL to identify a property, but received: [not-a-url].",
);
});
it("throws an instance of ValidPropertyUrlExpectedError when passed an invalid property URL", () => {
let thrownError;
try {
removeAll(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
);
} catch (e) {
thrownError = e;
}
expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError);
});
});
describe("removeIri", () => {
it("removes the given IRI value for the given Predicate", () => {
const thingWithIri = getMockThingWithNamedNode(
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
const updatedThing = removeUrl(
thingWithIri,
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].namedNodes,
).toStrictEqual([]);
});
it("accepts Properties as Named Nodes", () => {
const thingWithIri = getMockThingWithNamedNode(
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
const updatedThing = removeUrl(
thingWithIri,
DataFactory.namedNode("https://some.vocab/predicate"),
"https://some.pod/resource#name",
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].namedNodes,
).toStrictEqual([]);
});
it("accepts IRI's as Named Nodes", () => {
const thingWithIri = getMockThingWithNamedNode(
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
const updatedThing = removeUrl(
thingWithIri,
"https://some.vocab/predicate",
DataFactory.namedNode("https://some.pod/resource#name"),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].namedNodes,
).toStrictEqual([]);
});
it("does not modify the input Thing", () => {
const thingWithIri = getMockThingWithNamedNode(
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
const updatedThing = removeUrl(
thingWithIri,
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
expect(thingWithIri).not.toStrictEqual(updatedThing);
expect(
thingWithIri.predicates["https://some.vocab/predicate"].namedNodes,
).toStrictEqual(["https://some.pod/resource#name"]);
expect(
updatedThing.predicates["https://some.vocab/predicate"].namedNodes,
).toStrictEqual([]);
});
it("does nothing if the URL to remove was not found", () => {
const thingWithIri = getMockThingWithNamedNode(
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
const updatedThing = removeUrl(
thingWithIri,
"https://some.vocab/other-predicate",
"https://some.pod/other-resource#name",
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].namedNodes,
).toEqual(["https://some.pod/resource#name"]);
});
it("also works on ThingLocals", () => {
const thingLocal = getMockThingWithNamedNode(
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
(thingLocal.url as string) = `${localNodeSkolemPrefix}arbitrary-subject-name`;
const updatedThing = removeUrl(
thingLocal,
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].namedNodes,
).toStrictEqual([]);
});
it("removes multiple instances of the same IRI for the same Predicate", () => {
const thingWithIri = getMockThingWithNamedNode(
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
(
thingWithIri.predicates["https://some.vocab/predicate"]
.namedNodes as UrlString[]
).push("https://some.pod/resource#name");
const updatedThing = removeUrl(
thingWithIri,
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].namedNodes,
).toStrictEqual([]);
});
it("does not remove Quads with different Predicates or Objects", () => {
const thingWithIri = getMockThingWithNamedNode(
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
(
thingWithIri.predicates["https://some.vocab/predicate"]
.namedNodes as UrlString[]
).push("https://some-other.pod/resource#name");
(thingWithIri.predicates["https://some-other.vocab/predicate"] as {
namedNodes: UrlString[];
}) = {
namedNodes: ["https://arbitrary.pod/resource#name"],
};
const updatedThing = removeUrl(
thingWithIri,
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].namedNodes,
).toStrictEqual(["https://some-other.pod/resource#name"]);
expect(
updatedThing.predicates["https://some-other.vocab/predicate"].namedNodes,
).toStrictEqual(["https://arbitrary.pod/resource#name"]);
});
it("does not remove Quads with non-IRI Objects", () => {
const thingWithIri = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"Some non-IRI Object",
"string",
);
(thingWithIri.predicates["https://some.vocab/predicate"]
.namedNodes as UrlString[]) = ["https://some.pod/resource#name"];
const updatedThing = removeUrl(
thingWithIri,
"https://some.vocab/predicate",
"https://some.pod/resource#name",
);
expect(
updatedThing.predicates["https://some.vocab/predicate"],
).toStrictEqual({
literals: {
"http://www.w3.org/2001/XMLSchema#string": ["Some non-IRI Object"],
},
namedNodes: [],
});
});
it("resolves ThingPersisteds", () => {
const thingPersisted: ThingPersisted = {
type: "Subject",
url: "https://some.pod/resource#thing",
predicates: {},
};
const thingWithThingPersistedIri = getMockThingWithNamedNode(
"https://some.vocab/predicate",
"https://some.pod/resource#thing",
);
const updatedThing = removeUrl(
thingWithThingPersistedIri,
"https://some.vocab/predicate",
thingPersisted,
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].namedNodes,
).toStrictEqual([]);
});
it("throws an error when passed something other than a Thing", () => {
expect(() =>
removeUrl(
null as unknown as Thing,
"https://arbitrary.vocab/predicate",
"https://arbitrary.url",
),
).toThrow("Expected a Thing, but received: [null].");
});
it("throws an error when passed an invalid property URL", () => {
expect(() =>
removeUrl(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
"https://arbitrary.url",
),
).toThrow(
"Expected a valid URL to identify a property, but received: [not-a-url].",
);
});
it("throws an instance of ValidPropertyUrlExpectedError when passed an invalid property URL", () => {
let thrownError;
try {
removeUrl(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
"https://arbitrary.url",
);
} catch (e) {
thrownError = e;
}
expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError);
});
it("throws an error when passed an invalid URL value", () => {
expect(() =>
removeUrl(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"https://arbitrary.vocab/predicate",
"not-a-url",
),
).toThrow("Expected a valid URL value, but received: [not-a-url].");
});
it("throws an instance of ValidValueUrlExpectedError when passed an invalid property URL", () => {
let thrownError;
try {
removeUrl(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"https://arbitrary.vocab/predicate",
"not-a-url",
);
} catch (e) {
thrownError = e;
}
expect(thrownError).toBeInstanceOf(ValidValueUrlExpectedError);
});
});
describe("removeBoolean", () => {
it("removes the given boolean value for the given Predicate", () => {
const thingWithBoolean = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1",
"boolean",
);
const updatedThing = removeBoolean(
thingWithBoolean,
"https://some.vocab/predicate",
true,
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#boolean": [],
});
});
it("removes equivalent booleans with different serialisations", () => {
const thingWithSerialised1 = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1",
"boolean",
);
const thingWithSerialised0 = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"0",
"boolean",
);
const thingWithSerialisedTrue = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"true",
"boolean",
);
const thingWithSerialisedFalse = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"false",
"boolean",
);
const updatedThingWithoutSerialised1 = removeBoolean(
thingWithSerialised1,
"https://some.vocab/predicate",
true,
);
const updatedThingWithoutSerialised0 = removeBoolean(
thingWithSerialised0,
"https://some.vocab/predicate",
false,
);
const updatedThingWithoutSerialisedTrue = removeBoolean(
thingWithSerialisedTrue,
"https://some.vocab/predicate",
true,
);
const updatedThingWithoutSerialisedFalse = removeBoolean(
thingWithSerialisedFalse,
"https://some.vocab/predicate",
false,
);
expect(
updatedThingWithoutSerialised1.predicates["https://some.vocab/predicate"]
.literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#boolean": [],
});
expect(
updatedThingWithoutSerialised0.predicates["https://some.vocab/predicate"]
.literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#boolean": [],
});
expect(
updatedThingWithoutSerialisedTrue.predicates[
"https://some.vocab/predicate"
].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#boolean": [],
});
expect(
updatedThingWithoutSerialisedFalse.predicates[
"https://some.vocab/predicate"
].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#boolean": [],
});
});
it("accepts Properties as Named Nodes", () => {
const thingWithBoolean = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"0",
"boolean",
);
const updatedThing = removeBoolean(
thingWithBoolean,
DataFactory.namedNode("https://some.vocab/predicate"),
false,
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#boolean": [],
});
});
it("does not modify the input Thing", () => {
const thingWithBoolean = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1",
"boolean",
);
const updatedThing = removeBoolean(
thingWithBoolean,
"https://some.vocab/predicate",
true,
);
expect(thingWithBoolean).not.toStrictEqual(updatedThing);
expect(
thingWithBoolean.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#boolean": ["1"],
});
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#boolean": [],
});
});
it("does nothing if the boolean to remove was not found", () => {
const thingWithBoolean = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1",
"boolean",
);
const updatedThing = removeBoolean(
thingWithBoolean,
"https://some.vocab/other-predicate",
false,
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toEqual({
"http://www.w3.org/2001/XMLSchema#boolean": ["1"],
});
});
it("also works on ThingLocals", () => {
const thingLocal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1",
"boolean",
);
(thingLocal.url as string) = `${localNodeSkolemPrefix}arbitrary-subject-name`;
const updatedThing = removeBoolean(
thingLocal,
"https://some.vocab/predicate",
true,
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#boolean": [],
});
});
it("removes multiple instances of the same boolean for the same Predicate", () => {
const thingWithDuplicateBoolean = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1",
"boolean",
);
(
thingWithDuplicateBoolean.predicates["https://some.vocab/predicate"]
.literals!["http://www.w3.org/2001/XMLSchema#boolean"] as string[]
).push("1");
const updatedThing = removeBoolean(
thingWithDuplicateBoolean,
"https://some.vocab/predicate",
true,
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#boolean": [],
});
});
it("does not remove Quads with different Predicates or Objects", () => {
const thingWithOtherQuads = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1",
"boolean",
);
(
thingWithOtherQuads.predicates["https://some.vocab/predicate"].literals![
"http://www.w3.org/2001/XMLSchema#boolean"
] as string[]
).push("0");
(thingWithOtherQuads.predicates[
"https://some-other.vocab/predicate"
] as any) = {
literals: {
"http://www.w3.org/2001/XMLSchema#boolean": ["1"],
},
};
const updatedThing = removeBoolean(
thingWithOtherQuads,
"https://some.vocab/predicate",
true,
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#boolean": ["0"] });
expect(
updatedThing.predicates["https://some-other.vocab/predicate"].literals,
).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#boolean": ["1"] });
});
it("does not remove Quads with non-boolean Objects", () => {
const thingWithIri = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1",
"boolean",
);
(thingWithIri.predicates["https://some.vocab/predicate"]
.namedNodes as UrlString[]) = ["1"];
const updatedThing = removeBoolean(
thingWithIri,
"https://some.vocab/predicate",
true,
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].namedNodes,
).toStrictEqual(["1"]);
});
it("throws an error when passed something other than a Thing", () => {
expect(() =>
removeBoolean(
null as unknown as Thing,
"https://arbitrary.vocab/predicate",
true,
),
).toThrow("Expected a Thing, but received: [null].");
});
it("throws an error when passed an invalid property URL", () => {
expect(() =>
removeBoolean(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
true,
),
).toThrow(
"Expected a valid URL to identify a property, but received: [not-a-url].",
);
});
it("throws an instance of ValidPropertyUrlExpectedError when passed an invalid property URL", () => {
let thrownError;
try {
removeBoolean(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
true,
);
} catch (e) {
thrownError = e;
}
expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError);
});
});
describe("removeDatetime", () => {
it("removes the given datetime value for the given Predicate", () => {
const thingWithDatetime = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12T13:37:42Z",
"dateTime",
);
const updatedThing = removeDatetime(
thingWithDatetime,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#dateTime": [],
});
});
it("removes equivalent Datetimes with different serialisations", () => {
const thingWithRoundedDatetime = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12T13:37:42Z",
"dateTime",
);
const thingWithSpecificDatetime = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12T12:37:42.000+01:00",
"dateTime",
);
const updatedThingWithoutRoundedDatetime = removeDatetime(
thingWithRoundedDatetime,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)),
);
const updatedThingWithoutSpecificDatetime = removeDatetime(
thingWithSpecificDatetime,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)),
);
expect(
updatedThingWithoutRoundedDatetime.predicates[
"https://some.vocab/predicate"
].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#dateTime": [],
});
expect(
updatedThingWithoutSpecificDatetime.predicates[
"https://some.vocab/predicate"
].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#dateTime": [],
});
});
it("accepts Properties as Named Nodes", () => {
const thingWithDatetime = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12T13:37:42Z",
"dateTime",
);
const updatedThing = removeDatetime(
thingWithDatetime,
DataFactory.namedNode("https://some.vocab/predicate"),
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#dateTime": [],
});
});
it("does not modify the input Thing", () => {
const thingWithDatetime = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12T13:37:42Z",
"dateTime",
);
const updatedThing = removeDatetime(
thingWithDatetime,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)),
);
expect(thingWithDatetime).not.toStrictEqual(updatedThing);
expect(
thingWithDatetime.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#dateTime": ["1990-11-12T13:37:42Z"],
});
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#dateTime": [],
});
});
it("also works on ThingLocals", () => {
const thingLocal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12T13:37:42Z",
"dateTime",
);
(thingLocal.url as string) = `${localNodeSkolemPrefix}arbitrary-subject-name`;
const updatedThing = removeDatetime(
thingLocal,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#dateTime": [],
});
});
it("removes multiple instances of the same datetime for the same Predicate", () => {
const thingWithDuplicateDatetime = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12T13:37:42Z",
"dateTime",
);
(
thingWithDuplicateDatetime.predicates["https://some.vocab/predicate"]
.literals!["http://www.w3.org/2001/XMLSchema#dateTime"] as string[]
).push("1990-11-12T13:37:42Z");
const updatedThing = removeDatetime(
thingWithDuplicateDatetime,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#dateTime": [],
});
});
it("does not remove Quads with different Predicates or Objects", () => {
const thingWithOtherQuads = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12T13:37:42Z",
"dateTime",
);
// A different Object:
(
thingWithOtherQuads.predicates["https://some.vocab/predicate"].literals![
"http://www.w3.org/2001/XMLSchema#dateTime"
] as string[]
).push("1955-06-08T13:37:42Z");
// An invalid object
(
thingWithOtherQuads.predicates["https://some.vocab/predicate"].literals![
"http://www.w3.org/2001/XMLSchema#dateTime"
] as string[]
).push("not-a-datetime");
// A different predicate
(thingWithOtherQuads.predicates[
"https://some-other.vocab/predicate"
] as any) = {
literals: {
"http://www.w3.org/2001/XMLSchema#dateTime": ["1990-11-12T13:37:42Z"],
},
};
const updatedThing = removeDatetime(
thingWithOtherQuads,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)),
);
expect(updatedThing.predicates).toStrictEqual({
"https://some.vocab/predicate": {
literals: {
"http://www.w3.org/2001/XMLSchema#dateTime": [
"1955-06-08T13:37:42Z",
"not-a-datetime",
],
},
},
"https://some-other.vocab/predicate": {
literals: {
"http://www.w3.org/2001/XMLSchema#dateTime": ["1990-11-12T13:37:42Z"],
},
},
});
});
it("does not remove Quads with non-datetime Objects", () => {
const thingWithString = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12T13:37:42Z",
"dateTime",
);
(thingWithString.predicates["https://some.vocab/predicate"].literals![
"http://www.w3.org/2001/XMLSchema#string"
] as string[]) = ["Some string value"];
const updatedThing = removeDatetime(
thingWithString,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#dateTime": [],
"http://www.w3.org/2001/XMLSchema#string": ["Some string value"],
});
});
it("throws an error when passed something other than a Thing", () => {
expect(() =>
removeDatetime(
null as unknown as Thing,
"https://arbitrary.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)),
),
).toThrow("Expected a Thing, but received: [null].");
});
it("throws an error when passed an invalid property URL", () => {
expect(() =>
removeDatetime(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)),
),
).toThrow(
"Expected a valid URL to identify a property, but received: [not-a-url].",
);
});
it("throws an instance of ValidPropertyUrlExpectedError when passed an invalid property URL", () => {
let thrownError;
try {
removeDatetime(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)),
);
} catch (e) {
thrownError = e;
}
expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError);
});
});
describe("removeDate", () => {
it("removes the given date value for the given Predicate", () => {
const thingWithDate = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12Z",
"date",
);
const updatedThing = removeDate(
thingWithDate,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12)),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#date": [],
});
});
it("removes the given date value for the given Predicate regardless of the time value set", () => {
const thingWithDate = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12Z",
"date",
);
const updatedThing = removeDate(
thingWithDate,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42)),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#date": [],
});
});
it("accepts Properties as Named Nodes", () => {
const thingWithDate = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12Z",
"date",
);
const updatedThing = removeDate(
thingWithDate,
DataFactory.namedNode("https://some.vocab/predicate"),
new Date(Date.UTC(1990, 10, 12)),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#date": [],
});
});
it("does not modify the input Thing", () => {
const thingWithDate = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12Z",
"date",
);
const updatedThing = removeDate(
thingWithDate,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12)),
);
expect(thingWithDate).not.toStrictEqual(updatedThing);
expect(
thingWithDate.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#date": ["1990-11-12Z"],
});
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#date": [],
});
});
it("also works on ThingLocals", () => {
const thingLocal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12Z",
"date",
);
(thingLocal.url as string) = `${localNodeSkolemPrefix}arbitrary-subject-name`;
const updatedThing = removeDate(
thingLocal,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12)),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#date": [],
});
});
it("removes multiple instances of the same date for the same Predicate", () => {
const thingWithDuplicateDate = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12Z",
"date",
);
(
thingWithDuplicateDate.predicates["https://some.vocab/predicate"]
.literals!["http://www.w3.org/2001/XMLSchema#date"] as string[]
).push("1990-11-12Z");
const updatedThing = removeDate(
thingWithDuplicateDate,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12)),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#date": [],
});
});
it("does not remove Quads with different Predicates or Objects", () => {
const thingWithOtherQuads = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12Z",
"date",
);
// A different Object:
(
thingWithOtherQuads.predicates["https://some.vocab/predicate"].literals![
"http://www.w3.org/2001/XMLSchema#date"
] as string[]
).push("1955-06-08Z");
// An invalid object
(
thingWithOtherQuads.predicates["https://some.vocab/predicate"].literals![
"http://www.w3.org/2001/XMLSchema#date"
] as string[]
).push("not-a-date");
// A different predicate
(thingWithOtherQuads.predicates[
"https://some-other.vocab/predicate"
] as any) = {
literals: {
"http://www.w3.org/2001/XMLSchema#date": ["1990-11-12Z"],
},
};
const updatedThing = removeDate(
thingWithOtherQuads,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12)),
);
expect(updatedThing.predicates).toStrictEqual({
"https://some.vocab/predicate": {
literals: {
"http://www.w3.org/2001/XMLSchema#date": [
"1955-06-08Z",
"not-a-date",
],
},
},
"https://some-other.vocab/predicate": {
literals: {
"http://www.w3.org/2001/XMLSchema#date": ["1990-11-12Z"],
},
},
});
});
it("does not remove Quads with non-date Objects", () => {
const thingWithString = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12Z",
"date",
);
(thingWithString.predicates["https://some.vocab/predicate"].literals![
"http://www.w3.org/2001/XMLSchema#string"
] as string[]) = ["Some string value"];
const updatedThing = removeDate(
thingWithString,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12)),
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#date": [],
"http://www.w3.org/2001/XMLSchema#string": ["Some string value"],
});
});
it("throws an error when passed something other than a Thing", () => {
expect(() =>
removeDate(
null as unknown as Thing,
"https://arbitrary.vocab/predicate",
new Date(Date.UTC(1990, 10, 12)),
),
).toThrow("Expected a Thing, but received: [null].");
});
it("throws an error when passed an invalid property URL", () => {
expect(() =>
removeDate(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
new Date(Date.UTC(1990, 10, 12)),
),
).toThrow(
"Expected a valid URL to identify a property, but received: [not-a-url].",
);
});
it("throws an instance of ValidPropertyUrlExpectedError when passed an invalid property URL", () => {
let thrownError;
try {
removeDate(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
new Date(Date.UTC(1990, 10, 12)),
);
} catch (e) {
thrownError = e;
}
expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError);
});
});
describe("removeTime", () => {
it("removes the given time value for the given Predicate", () => {
const thingWithTime = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13:37:42",
"time",
);
const updatedThing = removeTime(
thingWithTime,
"https://some.vocab/predicate",
{
hour: 13,
minute: 37,
second: 42,
},
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#time": [],
});
});
it("removes equivalent times with different serialisations", () => {
const thingWithRoundedTime = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13:37:42",
"time",
);
const thingWithSpecificTime = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"12:37:42+01:00",
"time",
);
const updatedThingWithoutRoundedTime = removeTime(
thingWithRoundedTime,
"https://some.vocab/predicate",
{
hour: 13,
minute: 37,
second: 42,
},
);
const updatedThingWithoutSpecificTime = removeTime(
thingWithSpecificTime,
"https://some.vocab/predicate",
{
hour: 12,
minute: 37,
second: 42,
timezoneHourOffset: 1,
timezoneMinuteOffset: 0,
},
);
expect(
updatedThingWithoutRoundedTime.predicates["https://some.vocab/predicate"]
.literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#time": [],
});
expect(
updatedThingWithoutSpecificTime.predicates["https://some.vocab/predicate"]
.literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#time": [],
});
});
it("accepts Properties as Named Nodes", () => {
const thingWithTime = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13:37:42",
"time",
);
const updatedThing = removeTime(
thingWithTime,
DataFactory.namedNode("https://some.vocab/predicate"),
{
hour: 13,
minute: 37,
second: 42,
},
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#time": [],
});
});
it("does not modify the input Thing", () => {
const thingWithTime = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13:37:42",
"time",
);
const updatedThing = removeTime(
thingWithTime,
"https://some.vocab/predicate",
{
hour: 13,
minute: 37,
second: 42,
},
);
expect(thingWithTime).not.toStrictEqual(updatedThing);
expect(
thingWithTime.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#time": ["13:37:42"],
});
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#time": [],
});
});
it("also works on ThingLocals", () => {
const thingLocal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13:37:42",
"time",
);
(thingLocal.url as string) = `${localNodeSkolemPrefix}arbitrary-subject-name`;
const updatedThing = removeTime(
thingLocal,
"https://some.vocab/predicate",
{
hour: 13,
minute: 37,
second: 42,
},
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#time": [],
});
});
it("removes multiple instances of the same time for the same Predicate", () => {
const thingWithDuplicateTime = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13:37:42",
"time",
);
(
thingWithDuplicateTime.predicates["https://some.vocab/predicate"]
.literals!["http://www.w3.org/2001/XMLSchema#time"] as string[]
).push("13:37:42");
const updatedThing = removeTime(
thingWithDuplicateTime,
"https://some.vocab/predicate",
{
hour: 13,
minute: 37,
second: 42,
},
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#time": [],
});
});
it("does not remove Quads with different Predicates or Objects", () => {
const thingWithOtherQuads = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13:37:42",
"time",
);
// A different Object:
(
thingWithOtherQuads.predicates["https://some.vocab/predicate"].literals![
"http://www.w3.org/2001/XMLSchema#time"
] as string[]
).push("14:37:42");
// An invalid object
(
thingWithOtherQuads.predicates["https://some.vocab/predicate"].literals![
"http://www.w3.org/2001/XMLSchema#time"
] as string[]
).push("not-a-time");
// A different predicate
(thingWithOtherQuads.predicates[
"https://some-other.vocab/predicate"
] as any) = {
literals: {
"http://www.w3.org/2001/XMLSchema#time": ["13:37:42"],
},
};
const updatedThing = removeTime(
thingWithOtherQuads,
"https://some.vocab/predicate",
{
hour: 13,
minute: 37,
second: 42,
},
);
expect(updatedThing.predicates).toStrictEqual({
"https://some.vocab/predicate": {
literals: {
"http://www.w3.org/2001/XMLSchema#time": ["14:37:42", "not-a-time"],
},
},
"https://some-other.vocab/predicate": {
literals: {
"http://www.w3.org/2001/XMLSchema#time": ["13:37:42"],
},
},
});
});
it("does not remove Quads with non-time Objects", () => {
const thingWithString = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13:37:42",
"time",
);
(thingWithString.predicates["https://some.vocab/predicate"].literals![
"http://www.w3.org/2001/XMLSchema#string"
] as string[]) = ["Some string value"];
const updatedThing = removeTime(
thingWithString,
"https://some.vocab/predicate",
{
hour: 13,
minute: 37,
second: 42,
},
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#time": [],
"http://www.w3.org/2001/XMLSchema#string": ["Some string value"],
});
});
it("throws an error when passed something other than a Thing", () => {
expect(() =>
removeTime(
null as unknown as Thing,
"https://arbitrary.vocab/predicate",
{
hour: 13,
minute: 37,
second: 42,
},
),
).toThrow("Expected a Thing, but received: [null].");
});
it("throws an error when passed an invalid property URL", () => {
expect(() =>
removeTime(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
{
hour: 13,
minute: 37,
second: 42,
},
),
).toThrow(
"Expected a valid URL to identify a property, but received: [not-a-url].",
);
});
it("throws an instance of ValidPropertyUrlExpectedError when passed an invalid property URL", () => {
let thrownError;
try {
removeTime(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
{
hour: 13,
minute: 37,
second: 42,
},
);
} catch (e) {
thrownError = e;
}
expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError);
});
});
describe("removeDecimal", () => {
it("removes the given decimal value for the given Predicate", () => {
const thingWithDecimal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13.37",
"decimal",
);
const updatedThing = removeDecimal(
thingWithDecimal,
"https://some.vocab/predicate",
13.37,
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#decimal": [],
});
});
it("removes equivalent Decimals with different serialisations", () => {
const thingWithPlainDecimal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1337",
"decimal",
);
const thingWithSignedDecimal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"+1337",
"decimal",
);
const thingWithZeroedDecimal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"01337.0",
"decimal",
);
const updatedThingWithoutPlainDecimal = removeDecimal(
thingWithPlainDecimal,
"https://some.vocab/predicate",
1337,
);
const updatedThingWithoutSignedDecimal = removeDecimal(
thingWithSignedDecimal,
"https://some.vocab/predicate",
1337,
);
const updatedThingWithoutZeroedDecimal = removeDecimal(
thingWithZeroedDecimal,
"https://some.vocab/predicate",
1337,
);
expect(
updatedThingWithoutPlainDecimal.predicates["https://some.vocab/predicate"]
.literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#decimal": [],
});
expect(
updatedThingWithoutSignedDecimal.predicates[
"https://some.vocab/predicate"
].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#decimal": [],
});
expect(
updatedThingWithoutZeroedDecimal.predicates[
"https://some.vocab/predicate"
].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#decimal": [],
});
});
it("accepts Properties as Named Nodes", () => {
const thingWithDecimal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13.37",
"decimal",
);
const updatedThing = removeDecimal(
thingWithDecimal,
DataFactory.namedNode("https://some.vocab/predicate"),
13.37,
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#decimal": [],
});
});
it("does not modify the input Thing", () => {
const thingWithDecimal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13.37",
"decimal",
);
const updatedThing = removeDecimal(
thingWithDecimal,
"https://some.vocab/predicate",
13.37,
);
expect(thingWithDecimal).not.toStrictEqual(updatedThing);
expect(
thingWithDecimal.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#decimal": ["13.37"],
});
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toStrictEqual({
"http://www.w3.org/2001/XMLSchema#decimal": [],
});
});
it("does nothing if the decimal to remove could not be found", () => {
const thingWithDecimal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13.37",
"decimal",
);
const updatedThing = removeDecimal(
thingWithDecimal,
"https://some.vocab/other-predicate",
4.2,
);
expect(
updatedThing.predicates["https://some.vocab/predicate"].literals,
).toEqual({
"http://www.w3.org/2001/XMLSchema#decimal": ["13.37"],
});
});
it("also works on ThingLocals", () => {
const thingLocal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13.37",
"decimal",
);
(thingLocal.url as string) = `${localNodeSkolemPrefix}arbitrary-subject-n