@inrupt/solid-client
Version:
Make your web apps work with Solid Pods.
1,729 lines (1,504 loc) • 66.8 kB
text/typescript
/**
* Copyright 2020 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 { Quad } from "rdf-js";
import { dataset } from "@rdfjs/dataset";
import { DataFactory } from "n3";
import { IriString, Thing, ThingLocal, ThingPersisted } from "../interfaces";
import {
removeAll,
removeUrl,
removeBoolean,
removeDatetime,
removeDecimal,
removeInteger,
removeStringWithLocale,
removeStringNoLocale,
removeLiteral,
removeNamedNode,
} from "./remove";
import { mockThingFrom } from "./mock";
import {
ValidPropertyUrlExpectedError,
ValidValueUrlExpectedError,
} from "./thing";
function getMockQuadWithLiteralFor(
predicate: IriString,
literalValue: string,
literalType: "string" | "integer" | "decimal" | "boolean" | "dateTime"
): Quad {
const quad = DataFactory.quad(
DataFactory.namedNode("https://arbitrary.vocab/subject"),
DataFactory.namedNode(predicate),
DataFactory.literal(
literalValue,
DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#" + literalType)
)
);
return quad;
}
function getMockThingWithLiteralFor(
predicate: IriString,
literalValue: string,
literalType: "string" | "integer" | "decimal" | "boolean" | "dateTime"
): Thing {
const quad = getMockQuadWithLiteralFor(predicate, literalValue, literalType);
const thing = dataset();
thing.add(quad);
return Object.assign(thing, {
internal_url: "https://arbitrary.vocab/subject",
});
}
function getMockQuadWithNamedNode(
predicate: IriString,
object: IriString
): Quad {
const quad = DataFactory.quad(
DataFactory.namedNode("https://arbitrary.vocab/subject"),
DataFactory.namedNode(predicate),
DataFactory.namedNode(object)
);
return quad;
}
function getMockThingWithNamedNode(
predicate: IriString,
object: IriString
): Thing {
const plainDataset = dataset();
const quad = getMockQuadWithNamedNode(predicate, object);
plainDataset.add(quad);
const thing: Thing = Object.assign(plainDataset, {
internal_url: "https://arbitrary.vocab/subject",
});
return thing;
}
describe("removeAll", () => {
it("removes all values for the given Predicate", () => {
const quadWithIri = getMockQuadWithNamedNode(
"https://some.vocab/predicate",
"https://arbitrary.vocab/object"
);
const thingWithStringAndIri = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"Arbitrary string value",
"string"
);
thingWithStringAndIri.add(quadWithIri);
const updatedThing = removeAll(
thingWithStringAndIri,
"https://some.vocab/predicate"
);
expect(updatedThing.size).toBe(0);
});
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.size).toBe(0);
});
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.size).toBe(1);
expect(updatedThing.size).toBe(0);
});
it("also works on ThingLocals", () => {
const localSubject = Object.assign(
DataFactory.blankNode("Arbitrary blank node"),
{ internal_name: "localSubject" }
);
const quadWithLocalSubject = DataFactory.quad(
localSubject,
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.namedNode("https://some.pod/resource#name")
);
const datasetWithThingLocal = dataset();
datasetWithThingLocal.add(quadWithLocalSubject);
const thingLocal: ThingLocal = Object.assign(datasetWithThingLocal, {
internal_localSubject: localSubject,
});
const updatedThing = removeAll(thingLocal, "https://some.vocab/predicate");
expect(updatedThing.size).toBe(0);
});
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.add(Array.from(thingWithDuplicateIri)[0]);
const updatedThing = removeAll(
thingWithDuplicateIri,
"https://some.vocab/predicate"
);
expect(updatedThing.size).toBe(0);
});
it("does not remove Quads with different Predicates", () => {
const thingWithIri = getMockThingWithNamedNode(
"https://some.vocab/predicate",
"https://arbitrary.pod/resource#name"
);
const mockQuadWithDifferentPredicate = getMockQuadWithNamedNode(
"https://some-other.vocab/predicate",
"https://arbitrary.pod/resource#name"
);
thingWithIri.add(mockQuadWithDifferentPredicate);
const updatedThing = removeAll(
thingWithIri,
"https://some.vocab/predicate"
);
expect(Array.from(updatedThing)).toEqual([mockQuadWithDifferentPredicate]);
});
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", () => {
function getMockQuadWithIri(
predicate: IriString,
iri: IriString = "https://arbitrary.vocab/object"
): Quad {
return getMockQuadWithNamedNode(predicate, iri);
}
function getMockThingWithIri(
predicate: IriString,
iri: IriString = "https://arbitrary.vocab/object"
): ThingPersisted {
const quad = getMockQuadWithIri(predicate, iri);
const thing = dataset();
thing.add(quad);
return Object.assign(thing, {
internal_url: "https://arbitrary.vocab/subject",
});
}
it("removes the given IRI value for the given Predicate", () => {
const thingWithIri = getMockThingWithIri(
"https://some.vocab/predicate",
"https://some.pod/resource#name"
);
const updatedThing = removeUrl(
thingWithIri,
"https://some.vocab/predicate",
"https://some.pod/resource#name"
);
expect(updatedThing.size).toBe(0);
});
it("accepts Properties as Named Nodes", () => {
const thingWithIri = getMockThingWithIri(
"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.size).toBe(0);
});
it("accepts IRI's as Named Nodes", () => {
const thingWithIri = getMockThingWithIri(
"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.size).toBe(0);
});
it("does not modify the input Thing", () => {
const thingWithIri = getMockThingWithIri(
"https://some.vocab/predicate",
"https://some.pod/resource#name"
);
const updatedThing = removeUrl(
thingWithIri,
"https://some.vocab/predicate",
"https://some.pod/resource#name"
);
expect(thingWithIri.size).toBe(1);
expect(updatedThing.size).toBe(0);
});
it("also works on ThingLocals", () => {
const localSubject = Object.assign(
DataFactory.blankNode("Arbitrary blank node"),
{ internal_name: "localSubject" }
);
const quadWithLocalSubject = DataFactory.quad(
localSubject,
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.namedNode("https://some.pod/resource#name")
);
const datasetWithThingLocal = dataset();
datasetWithThingLocal.add(quadWithLocalSubject);
const thingLocal: ThingLocal = Object.assign(datasetWithThingLocal, {
internal_localSubject: localSubject,
});
const updatedThing = removeUrl(
thingLocal,
"https://some.vocab/predicate",
"https://some.pod/resource#name"
);
expect(updatedThing.size).toBe(0);
});
it("removes multiple instances of the same IRI for the same Predicate", () => {
const thingWithDuplicateIri = getMockThingWithIri(
"https://some.vocab/predicate",
"https://some.pod/resource#name"
);
thingWithDuplicateIri.add(Array.from(thingWithDuplicateIri)[0]);
const updatedThing = removeUrl(
thingWithDuplicateIri,
"https://some.vocab/predicate",
"https://some.pod/resource#name"
);
expect(updatedThing.size).toBe(0);
});
it("does not remove Quads with different Predicates or Objects", () => {
const thingWithIri = getMockThingWithIri(
"https://some.vocab/predicate",
"https://some.pod/resource#name"
);
const mockQuadWithDifferentIri = getMockQuadWithIri(
"https://some.vocab/predicate",
"https://some-other.pod/resource#name"
);
const mockQuadWithDifferentPredicate = getMockQuadWithIri(
"https://some-other.vocab/predicate",
"https://some.pod/resource#name"
);
thingWithIri.add(mockQuadWithDifferentIri);
thingWithIri.add(mockQuadWithDifferentPredicate);
const updatedThing = removeUrl(
thingWithIri,
"https://some.vocab/predicate",
"https://some.pod/resource#name"
);
expect(Array.from(updatedThing)).toEqual([
mockQuadWithDifferentIri,
mockQuadWithDifferentPredicate,
]);
});
it("does not remove Quads with non-IRI Objects", () => {
const thingWithIri = getMockThingWithIri(
"https://some.vocab/predicate",
"https://some.pod/resource#name"
);
const mockQuadWithString = DataFactory.quad(
DataFactory.namedNode("https://arbitrary.vocab/subject"),
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.literal("Some non-IRI Object")
);
thingWithIri.add(mockQuadWithString);
const updatedThing = removeUrl(
thingWithIri,
"https://some.vocab/predicate",
"https://some.pod/resource#name"
);
expect(Array.from(updatedThing)).toEqual([mockQuadWithString]);
});
it("resolves ThingPersisteds", () => {
const thingPersisted: ThingPersisted = Object.assign(dataset(), {
internal_url: "https://some.pod/resource#thing",
});
const quadWithThingPersistedIri = DataFactory.quad(
DataFactory.namedNode("https://arbitrary.vocab/subject"),
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.namedNode("https://some.pod/resource#thing")
);
const datasetWithThingPersistedIri = dataset();
datasetWithThingPersistedIri.add(quadWithThingPersistedIri);
const thingWithThingPersistedIri: Thing = Object.assign(
datasetWithThingPersistedIri,
{
internal_url: "https://arbitrary.vocab/subject",
}
);
const updatedThing = removeUrl(
thingWithThingPersistedIri,
"https://some.vocab/predicate",
thingPersisted
);
expect(updatedThing.size).toBe(0);
});
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.size).toBe(0);
});
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.size).toBe(0);
expect(updatedThingWithoutSerialised0.size).toBe(0);
expect(updatedThingWithoutSerialisedTrue.size).toBe(0);
expect(updatedThingWithoutSerialisedFalse.size).toBe(0);
});
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.size).toBe(0);
});
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.size).toBe(1);
expect(updatedThing.size).toBe(0);
});
it("also works on ThingLocals", () => {
const localSubject = Object.assign(
DataFactory.blankNode("Arbitrary blank node"),
{ internal_name: "localSubject" }
);
const quadWithLocalSubject = DataFactory.quad(
localSubject,
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.literal(
"1",
DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#boolean")
)
);
const datasetWithThingLocal = dataset();
datasetWithThingLocal.add(quadWithLocalSubject);
const thingLocal: ThingLocal = Object.assign(datasetWithThingLocal, {
internal_localSubject: localSubject,
});
const updatedThing = removeBoolean(
thingLocal,
"https://some.vocab/predicate",
true
);
expect(updatedThing.size).toBe(0);
});
it("removes multiple instances of the same boolean for the same Predicate", () => {
const thingWithDuplicateBoolean = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1",
"boolean"
);
thingWithDuplicateBoolean.add(Array.from(thingWithDuplicateBoolean)[0]);
const updatedThing = removeBoolean(
thingWithDuplicateBoolean,
"https://some.vocab/predicate",
true
);
expect(updatedThing.size).toBe(0);
});
it("does not remove Quads with different Predicates or Objects", () => {
const thingWithOtherQuads = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1",
"boolean"
);
const mockQuadWithDifferentObject = getMockQuadWithLiteralFor(
"https://some.vocab/predicate",
"0",
"boolean"
);
const mockQuadWithDifferentPredicate = getMockQuadWithLiteralFor(
"https://some-other.vocab/predicate",
"1",
"boolean"
);
thingWithOtherQuads.add(mockQuadWithDifferentObject);
thingWithOtherQuads.add(mockQuadWithDifferentPredicate);
const updatedThing = removeBoolean(
thingWithOtherQuads,
"https://some.vocab/predicate",
true
);
expect(Array.from(updatedThing)).toEqual([
mockQuadWithDifferentObject,
mockQuadWithDifferentPredicate,
]);
});
it("does not remove Quads with non-boolean Objects", () => {
const thingWithString = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1",
"boolean"
);
const mockQuadWithIntegerNotBoolean = DataFactory.quad(
DataFactory.namedNode("https://arbitrary.vocab/subject"),
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.literal("1", "http://www.w3.org/2001/XMLSchema#integer")
);
thingWithString.add(mockQuadWithIntegerNotBoolean);
const updatedThing = removeBoolean(
thingWithString,
"https://some.vocab/predicate",
true
);
expect(Array.from(updatedThing)).toEqual([mockQuadWithIntegerNotBoolean]);
});
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.size).toBe(0);
});
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.size).toBe(0);
expect(updatedThingWithoutSpecificDatetime.size).toBe(0);
});
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.size).toBe(0);
});
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.size).toBe(1);
expect(updatedThing.size).toBe(0);
});
it("also works on ThingLocals", () => {
const localSubject = Object.assign(
DataFactory.blankNode("Arbitrary blank node"),
{ internal_name: "localSubject" }
);
const quadWithLocalSubject = DataFactory.quad(
localSubject,
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.literal(
"1990-11-12T13:37:42Z",
DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#dateTime")
)
);
const datasetWithThingLocal = dataset();
datasetWithThingLocal.add(quadWithLocalSubject);
const thingLocal: ThingLocal = Object.assign(datasetWithThingLocal, {
internal_localSubject: localSubject,
});
const updatedThing = removeDatetime(
thingLocal,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0))
);
expect(updatedThing.size).toBe(0);
});
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.add(Array.from(thingWithDuplicateDatetime)[0]);
const updatedThing = removeDatetime(
thingWithDuplicateDatetime,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0))
);
expect(updatedThing.size).toBe(0);
});
it("does not remove Quads with different Predicates or Objects", () => {
const thingWithOtherQuads = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12T13:37:42Z",
"dateTime"
);
const mockQuadWithDifferentObject = getMockQuadWithLiteralFor(
"https://some.vocab/predicate",
"1955-06-08T13:37:42Z",
"dateTime"
);
const mockQuadWithInvalidObject = getMockQuadWithLiteralFor(
"https://some.vocab/predicate",
(undefined as unknown) as string,
"dateTime"
);
const mockQuadWithDifferentPredicate = getMockQuadWithLiteralFor(
"https://some-other.vocab/predicate",
"1990-11-12T13:37:42Z",
"dateTime"
);
thingWithOtherQuads.add(mockQuadWithDifferentObject);
thingWithOtherQuads.add(mockQuadWithInvalidObject);
thingWithOtherQuads.add(mockQuadWithDifferentPredicate);
const updatedThing = removeDatetime(
thingWithOtherQuads,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0))
);
expect(Array.from(updatedThing)).toEqual([
mockQuadWithDifferentObject,
mockQuadWithInvalidObject,
mockQuadWithDifferentPredicate,
]);
});
it("does not remove Quads with non-datetime Objects", () => {
const thingWithString = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"1990-11-12T13:37:42Z",
"dateTime"
);
const mockQuadWithStringNotDatetime = DataFactory.quad(
DataFactory.namedNode("https://arbitrary.vocab/subject"),
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.literal(
"1990-11-12T13:37:42Z",
"http://www.w3.org/2001/XMLSchema#string"
)
);
thingWithString.add(mockQuadWithStringNotDatetime);
const updatedThing = removeDatetime(
thingWithString,
"https://some.vocab/predicate",
new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0))
);
expect(Array.from(updatedThing)).toEqual([mockQuadWithStringNotDatetime]);
});
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("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.size).toBe(0);
});
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.size).toBe(0);
expect(updatedThingWithoutSignedDecimal.size).toBe(0);
expect(updatedThingWithoutZeroedDecimal.size).toBe(0);
});
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.size).toBe(0);
});
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.size).toBe(1);
expect(updatedThing.size).toBe(0);
});
it("also works on ThingLocals", () => {
const localSubject = Object.assign(
DataFactory.blankNode("Arbitrary blank node"),
{ internal_name: "localSubject" }
);
const quadWithLocalSubject = DataFactory.quad(
localSubject,
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.literal(
"13.37",
DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#decimal")
)
);
const datasetWithThingLocal = dataset();
datasetWithThingLocal.add(quadWithLocalSubject);
const thingLocal: ThingLocal = Object.assign(datasetWithThingLocal, {
internal_localSubject: localSubject,
});
const updatedThing = removeDecimal(
thingLocal,
"https://some.vocab/predicate",
13.37
);
expect(updatedThing.size).toBe(0);
});
it("removes multiple instances of the same decimal for the same Predicate", () => {
const thingWithDuplicateDecimal = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13.37",
"decimal"
);
thingWithDuplicateDecimal.add(Array.from(thingWithDuplicateDecimal)[0]);
const updatedThing = removeDecimal(
thingWithDuplicateDecimal,
"https://some.vocab/predicate",
13.37
);
expect(updatedThing.size).toBe(0);
});
it("does not remove Quads with different Predicates or Objects", () => {
const thingWithOtherQuads = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13.37",
"decimal"
);
const mockQuadWithDifferentObject = getMockQuadWithLiteralFor(
"https://some.vocab/predicate",
"4.2",
"decimal"
);
const mockQuadWithDifferentPredicate = getMockQuadWithLiteralFor(
"https://some-other.vocab/predicate",
"13.37",
"decimal"
);
thingWithOtherQuads.add(mockQuadWithDifferentObject);
thingWithOtherQuads.add(mockQuadWithDifferentPredicate);
const updatedThing = removeDecimal(
thingWithOtherQuads,
"https://some.vocab/predicate",
13.37
);
expect(Array.from(updatedThing)).toEqual([
mockQuadWithDifferentObject,
mockQuadWithDifferentPredicate,
]);
});
it("does not remove Quads with non-decimal Objects", () => {
const thingWithString = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"13.37",
"decimal"
);
const mockQuadWithStringNotDecimal = DataFactory.quad(
DataFactory.namedNode("https://arbitrary.vocab/subject"),
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.literal("13.37", "http://www.w3.org/2001/XMLSchema#string")
);
thingWithString.add(mockQuadWithStringNotDecimal);
const updatedThing = removeDecimal(
thingWithString,
"https://some.vocab/predicate",
13.37
);
expect(Array.from(updatedThing)).toEqual([mockQuadWithStringNotDecimal]);
});
it("throws an error when passed something other than a Thing", () => {
expect(() =>
removeDecimal(
(null as unknown) as Thing,
"https://arbitrary.vocab/predicate",
13.37
)
).toThrow("Expected a Thing, but received: [null].");
});
it("throws an error when passed an invalid property URL", () => {
expect(() =>
removeDecimal(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
13.37
)
).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 {
removeDecimal(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
13.37
);
} catch (e) {
thrownError = e;
}
expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError);
});
});
describe("removeInteger", () => {
it("removes the given integer value for the given Predicate", () => {
const thingWithInteger = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"42",
"integer"
);
const updatedThing = removeInteger(
thingWithInteger,
"https://some.vocab/predicate",
42
);
expect(updatedThing.size).toBe(0);
});
it("removes equivalent integers with different serialisations", () => {
const thingWithUnsignedInteger = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"42",
"integer"
);
const thingWithSignedInteger = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"+42",
"integer"
);
const updatedThingWithoutUnsignedInteger = removeInteger(
thingWithUnsignedInteger,
"https://some.vocab/predicate",
42
);
const updatedThingWithoutSignedInteger = removeInteger(
thingWithSignedInteger,
"https://some.vocab/predicate",
42
);
expect(updatedThingWithoutUnsignedInteger.size).toBe(0);
expect(updatedThingWithoutSignedInteger.size).toBe(0);
});
it("accepts Properties as Named Nodes", () => {
const thingWithInteger = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"42",
"integer"
);
const updatedThing = removeInteger(
thingWithInteger,
DataFactory.namedNode("https://some.vocab/predicate"),
42
);
expect(updatedThing.size).toBe(0);
});
it("does not modify the input Thing", () => {
const thingWithInteger = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"42",
"integer"
);
const updatedThing = removeInteger(
thingWithInteger,
"https://some.vocab/predicate",
42
);
expect(thingWithInteger.size).toBe(1);
expect(updatedThing.size).toBe(0);
});
it("also works on ThingLocals", () => {
const localSubject = Object.assign(
DataFactory.blankNode("Arbitrary blank node"),
{ internal_name: "localSubject" }
);
const quadWithLocalSubject = DataFactory.quad(
localSubject,
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.literal(
"42",
DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#integer")
)
);
const datasetWithThingLocal = dataset();
datasetWithThingLocal.add(quadWithLocalSubject);
const thingLocal: ThingLocal = Object.assign(datasetWithThingLocal, {
internal_localSubject: localSubject,
});
const updatedThing = removeInteger(
thingLocal,
"https://some.vocab/predicate",
42
);
expect(updatedThing.size).toBe(0);
});
it("removes multiple instances of the same integer for the same Predicate", () => {
const thingWithDuplicateInteger = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"42",
"integer"
);
thingWithDuplicateInteger.add(Array.from(thingWithDuplicateInteger)[0]);
const updatedThing = removeInteger(
thingWithDuplicateInteger,
"https://some.vocab/predicate",
42
);
expect(updatedThing.size).toBe(0);
});
it("does not remove Quads with different Predicates or Objects", () => {
const thingWithOtherQuads = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"42",
"integer"
);
const mockQuadWithDifferentObject = getMockQuadWithLiteralFor(
"https://some.vocab/predicate",
"1337",
"integer"
);
const mockQuadWithDifferentPredicate = getMockQuadWithLiteralFor(
"https://some-other.vocab/predicate",
"42",
"integer"
);
thingWithOtherQuads.add(mockQuadWithDifferentObject);
thingWithOtherQuads.add(mockQuadWithDifferentPredicate);
const updatedThing = removeInteger(
thingWithOtherQuads,
"https://some.vocab/predicate",
42
);
expect(Array.from(updatedThing)).toEqual([
mockQuadWithDifferentObject,
mockQuadWithDifferentPredicate,
]);
});
it("does not remove Quads with non-integer Objects", () => {
const thingWithString = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"42",
"integer"
);
const mockQuadWithStringNotInteger = DataFactory.quad(
DataFactory.namedNode("https://arbitrary.vocab/subject"),
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.literal("42", "http://www.w3.org/2001/XMLSchema#string")
);
thingWithString.add(mockQuadWithStringNotInteger);
const updatedThing = removeInteger(
thingWithString,
"https://some.vocab/predicate",
42
);
expect(Array.from(updatedThing)).toEqual([mockQuadWithStringNotInteger]);
});
it("throws an error when passed something other than a Thing", () => {
expect(() =>
removeInteger(
(null as unknown) as Thing,
"https://arbitrary.vocab/predicate",
42
)
).toThrow("Expected a Thing, but received: [null].");
});
it("throws an error when passed an invalid property URL", () => {
expect(() =>
removeInteger(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
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 {
removeInteger(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
42
);
} catch (e) {
thrownError = e;
}
expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError);
});
});
describe("removeStringWithLocale", () => {
function getMockQuadWithStringWithLocaleFor(
predicate: IriString,
literalValue: string,
locale: string
): Quad {
const quad = DataFactory.quad(
DataFactory.namedNode("https://arbitrary.vocab/subject"),
DataFactory.namedNode(predicate),
DataFactory.literal(literalValue, locale)
);
return quad;
}
function getMockThingWithStringWithLocaleFor(
predicate: IriString,
literalValue: string,
locale: string
): Thing {
const quad = getMockQuadWithStringWithLocaleFor(
predicate,
literalValue,
locale
);
const thing = dataset();
thing.add(quad);
return Object.assign(thing, {
internal_url: "https://arbitrary.vocab/subject",
});
}
it("removes the given localised string for the given Predicate", () => {
const thingWithStringWithLocale = getMockThingWithStringWithLocaleFor(
"https://some.vocab/predicate",
"Une chaîne de caractères quelconque",
"fr-fr"
);
const updatedThing = removeStringWithLocale(
thingWithStringWithLocale,
"https://some.vocab/predicate",
"Une chaîne de caractères quelconque",
"fr-fr"
);
expect(updatedThing.size).toBe(0);
});
it("accepts Properties as Named Nodes", () => {
const thingWithStringWithLocale = getMockThingWithStringWithLocaleFor(
"https://some.vocab/predicate",
"Some arbitrary string",
"en-us"
);
const updatedThing = removeStringWithLocale(
thingWithStringWithLocale,
DataFactory.namedNode("https://some.vocab/predicate"),
"Some arbitrary string",
"en-us"
);
expect(updatedThing.size).toBe(0);
});
it("does not modify the input Thing", () => {
const thingWithStringWithLocale = getMockThingWithStringWithLocaleFor(
"https://some.vocab/predicate",
"Some arbitrary string",
"en-us"
);
const updatedThing = removeStringWithLocale(
thingWithStringWithLocale,
"https://some.vocab/predicate",
"Some arbitrary string",
"en-us"
);
expect(thingWithStringWithLocale.size).toBe(1);
expect(updatedThing.size).toBe(0);
});
it("also works on ThingLocals", () => {
const localSubject = Object.assign(
DataFactory.blankNode("Arbitrary blank node"),
{ internal_name: "localSubject" }
);
const quadWithLocalSubject = DataFactory.quad(
localSubject,
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.literal("Some arbitrary string", "en-us")
);
const datasetWithThingLocal = dataset();
datasetWithThingLocal.add(quadWithLocalSubject);
const thingLocal: ThingLocal = Object.assign(datasetWithThingLocal, {
internal_localSubject: localSubject,
});
const updatedThing = removeStringWithLocale(
thingLocal,
"https://some.vocab/predicate",
"Some arbitrary string",
"en-us"
);
expect(updatedThing.size).toBe(0);
});
it("removes multiple instances of the same localised string for the same Predicate", () => {
const thingWithDuplicateStringWithLocale = getMockThingWithStringWithLocaleFor(
"https://some.vocab/predicate",
"Some arbitrary string",
"en-us"
);
thingWithDuplicateStringWithLocale.add(
Array.from(thingWithDuplicateStringWithLocale)[0]
);
const updatedThing = removeStringWithLocale(
thingWithDuplicateStringWithLocale,
"https://some.vocab/predicate",
"Some arbitrary string",
"en-us"
);
expect(updatedThing.size).toBe(0);
});
it("does not remove Quads with different Predicates or Objects", () => {
const thingWithStringWithLocale = getMockThingWithStringWithLocaleFor(
"https://some.vocab/predicate",
"Some arbitrary string",
"en-us"
);
const mockQuadWithDifferentStringInSameLocale = getMockQuadWithStringWithLocaleFor(
"https://some.vocab/predicate",
"Some other arbitrary string",
"en-us"
);
const mockQuadWithSameStringInDifferentLocale = getMockQuadWithStringWithLocaleFor(
"https://some.vocab/predicate",
"Some arbitrary string",
"en-uk"
);
const mockQuadWithDifferentPredicate = getMockQuadWithStringWithLocaleFor(
"https://some.other.vocab/predicate",
"Some arbitrary string",
"en-us"
);
thingWithStringWithLocale.add(mockQuadWithDifferentStringInSameLocale);
thingWithStringWithLocale.add(mockQuadWithSameStringInDifferentLocale);
thingWithStringWithLocale.add(mockQuadWithDifferentPredicate);
const updatedThing = removeStringWithLocale(
thingWithStringWithLocale,
"https://some.vocab/predicate",
"Some arbitrary string",
"en-US"
);
expect(Array.from(updatedThing)).toEqual([
mockQuadWithDifferentStringInSameLocale,
mockQuadWithSameStringInDifferentLocale,
mockQuadWithDifferentPredicate,
]);
});
it("removes Quads when the locale casing mismatch", () => {
const thingWithStringWithLocale = getMockThingWithStringWithLocaleFor(
"https://some.vocab/predicate",
"Some arbitrary string",
"en-us"
);
const mockQuadWithStringInDifferentLocale = getMockQuadWithStringWithLocaleFor(
"https://some.vocab/predicate",
"Some arbitrary string",
"en-US"
);
thingWithStringWithLocale.add(mockQuadWithStringInDifferentLocale);
const updatedThing = removeStringWithLocale(
thingWithStringWithLocale,
"https://some.vocab/predicate",
"Some arbitrary string",
"en-US"
);
expect(updatedThing.size).toBe(0);
});
it("does not remove Quads with non-string Objects", () => {
const thingWithLocalizedString = getMockThingWithStringWithLocaleFor(
"https://some.vocab/predicate",
"Some arbitrary string",
"en-US"
);
const mockQuadWithInteger = DataFactory.quad(
DataFactory.namedNode("https://arbitrary.vocab/subject"),
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.literal("42", "http://www.w3.org/2001/XMLSchema#integer")
);
thingWithLocalizedString.add(mockQuadWithInteger);
const updatedThing = removeStringWithLocale(
thingWithLocalizedString,
"https://some.vocab/predicate",
"Some arbitrary string",
"en-US"
);
expect(updatedThing.has(mockQuadWithInteger)).toBe(true);
});
it("throws an error when passed something other than a Thing", () => {
expect(() =>
removeStringWithLocale(
(null as unknown) as Thing,
"https://arbitrary.vocab/predicate",
"Arbitrary string",
"nl-NL"
)
).toThrow("Expected a Thing, but received: [null].");
});
it("throws an error when passed an invalid property URL", () => {
expect(() =>
removeStringWithLocale(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
"Arbitrary string",
"nl-NL"
)
).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 {
removeStringWithLocale(
mockThingFrom("https://arbitrary.pod/resource#thing"),
"not-a-url",
"Arbitrary string",
"nl-NL"
);
} catch (e) {
thrownError = e;
}
expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError);
});
});
describe("removeStringNoLocale", () => {
it("removes the given string value for the given Predicate", () => {
const thingWithStringNoLocale = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"Some arbitrary string",
"string"
);
const updatedThing = removeStringNoLocale(
thingWithStringNoLocale,
"https://some.vocab/predicate",
"Some arbitrary string"
);
expect(updatedThing.size).toBe(0);
});
it("accepts Properties as Named Nodes", () => {
const thingWithStringNoLocale = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"Some arbitrary string",
"string"
);
const updatedThing = removeStringNoLocale(
thingWithStringNoLocale,
DataFactory.namedNode("https://some.vocab/predicate"),
"Some arbitrary string"
);
expect(updatedThing.size).toBe(0);
});
it("does not modify the input Thing", () => {
const thingWithStringNoLocale = getMockThingWithLiteralFor(
"https://some.vocab/predicate",
"Some arbitrary string",
"string"
);
const updatedThing = removeStringNoLocale(
thingWithStringNoLocale,
"https://some.vocab/predicate",
"Some arbitrary string"
);
expect(thingWithStringNoLocale.size).toBe(1);
expect(updatedThing.size).toBe(0);
});
it("also works on ThingLocals", () => {
const localSubject = Object.assign(
DataFactory.blankNode("Arbitrary blank node"),
{ internal_name: "localSubject" }
);
const quadWithLocalSubject = DataFactory.quad(
localSubject,
DataFactory.namedNode("https://some.vocab/predicate"),
DataFactory.literal(
"Some arbitrary string",
DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#string")
)
);
const datasetWithThingLocal = dataset();
datasetWithThingLocal.add(quadWithLocalSubject);
const thingLocal: ThingLocal = Object.assign(datasetWithThingLocal, {
internal_localSubject: localSubject,
});
const updatedThing = removeStringNoLocale(
thingLocal,
"https://some.vocab/predicate",