// 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 rdfjs 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"; const dataset = rdfjs.dataset; 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-name`; const updatedThing = removeDecimal( thingLocal, "https://some.vocab/predicate", 13.37, ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#decimal": [], }); }); it("removes multiple instances of the same decimal for the same Predicate", () => { const thingWithDuplicateDecimal = getMockThingWithLiteralFor( "https://some.vocab/predicate", "13.37", "decimal", ); ( thingWithDuplicateDecimal.predicates["https://some.vocab/predicate"] .literals!["http://www.w3.org/2001/XMLSchema#decimal"] as string[] ).push("13.37"); const updatedThing = removeDecimal( thingWithDuplicateDecimal, "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 remove Quads with different Predicates or Objects", () => { const thingWithOtherQuads = getMockThingWithLiteralFor( "https://some.vocab/predicate", "13.37", "decimal", ); // A different Object: ( thingWithOtherQuads.predicates["https://some.vocab/predicate"].literals![ "http://www.w3.org/2001/XMLSchema#decimal" ] as string[] ).push("4.2"); // A different predicate (thingWithOtherQuads.predicates[ "https://some-other.vocab/predicate" ] as any) = { literals: { "http://www.w3.org/2001/XMLSchema#decimal": ["13.37"], }, }; const updatedThing = removeDecimal( thingWithOtherQuads, "https://some.vocab/predicate", 13.37, ); expect(updatedThing.predicates).toStrictEqual({ "https://some.vocab/predicate": { literals: { "http://www.w3.org/2001/XMLSchema#decimal": ["4.2"], }, }, "https://some-other.vocab/predicate": { literals: { "http://www.w3.org/2001/XMLSchema#decimal": ["13.37"], }, }, }); }); it("does not remove Quads with non-decimal Objects", () => { const thingWithString = getMockThingWithLiteralFor( "https://some.vocab/predicate", "13.37", "decimal", ); (thingWithString.predicates["https://some.vocab/predicate"].literals![ "http://www.w3.org/2001/XMLSchema#string" ] as string[]) = ["13.37"]; const updatedThing = removeDecimal( thingWithString, "https://some.vocab/predicate", 13.37, ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#decimal": [], "http://www.w3.org/2001/XMLSchema#string": ["13.37"], }); }); 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.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], }); }); 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.predicates[ "https://some.vocab/predicate" ].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], }); expect( updatedThingWithoutSignedInteger.predicates[ "https://some.vocab/predicate" ].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], }); }); 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.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], }); }); 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).not.toStrictEqual(updatedThing); expect( thingWithInteger.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": ["42"], }); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], }); }); it("does nothing if the integer to remove could not be found", () => { const thingWithInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); const updatedThing = removeInteger( thingWithInteger, "https://some.vocab/other-predicate", 1337, ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toEqual({ "http://www.w3.org/2001/XMLSchema#integer": ["42"], }); }); it("also works on ThingLocals", () => { const thingLocal = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); (thingLocal.url as string) = `${localNodeSkolemPrefix}arbitrary-subject-name`; const updatedThing = removeInteger( thingLocal, "https://some.vocab/predicate", 42, ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], }); }); it("removes multiple instances of the same integer for the same Predicate", () => { const thingWithDuplicateInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); ( thingWithDuplicateInteger.predicates["https://some.vocab/predicate"] .literals!["http://www.w3.org/2001/XMLSchema#integer"] as string[] ).push("42"); const updatedThing = removeInteger( thingWithDuplicateInteger, "https://some.vocab/predicate", 42, ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], }); }); it("does not remove Quads with different Predicates or Objects", () => { const thingWithOtherQuads = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); // A different Object: ( thingWithOtherQuads.predicates["https://some.vocab/predicate"].literals![ "http://www.w3.org/2001/XMLSchema#integer" ] as string[] ).push("1337"); // A different predicate (thingWithOtherQuads.predicates[ "https://some-other.vocab/predicate" ] as any) = { literals: { "http://www.w3.org/2001/XMLSchema#integer": ["42"], }, }; const updatedThing = removeInteger( thingWithOtherQuads, "https://some.vocab/predicate", 42, ); expect(updatedThing.predicates).toStrictEqual({ "https://some.vocab/predicate": { literals: { "http://www.w3.org/2001/XMLSchema#integer": ["1337"], }, }, "https://some-other.vocab/predicate": { literals: { "http://www.w3.org/2001/XMLSchema#integer": ["42"], }, }, }); }); it("does not remove Quads with non-integer Objects", () => { const thingWithString = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); (thingWithString.predicates["https://some.vocab/predicate"].literals![ "http://www.w3.org/2001/XMLSchema#string" ] as string[]) = ["42"]; const updatedThing = removeInteger( thingWithString, "https://some.vocab/predicate", 42, ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], "http://www.w3.org/2001/XMLSchema#string": ["42"], }); }); 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("removeStringEnglish", () => { function getMockThingWithEnglishString( predicate: IriString, literalValue: string, ): Thing { return { type: "Subject", url: "https://arbitrary.vocab/subject", predicates: { [predicate]: { langStrings: { en: [literalValue], }, }, }, }; } it("removes the given English string for the given Predicate", () => { const thingWithStringWithLocale = getMockThingWithEnglishString( "https://some.vocab/predicate", "Some value in English...", ); const updatedThing = removeStringEnglish( thingWithStringWithLocale, "https://some.vocab/predicate", "Some value in English...", ); expect( updatedThing.predicates["https://some.vocab/predicate"].langStrings, ).toStrictEqual({ en: [], }); }); }); describe("removeStringWithLocale", () => { function getMockThingWithStringWithLocaleFor( predicate: IriString, literalValue: string, locale: string, ): Thing { return { type: "Subject", url: "https://arbitrary.vocab/subject", predicates: { [predicate]: { langStrings: { [locale]: [literalValue], }, }, }, }; } 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.predicates["https://some.vocab/predicate"].langStrings, ).toStrictEqual({ "fr-fr": [], }); }); 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.predicates["https://some.vocab/predicate"].langStrings, ).toStrictEqual({ "en-us": [], }); }); 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).not.toStrictEqual(updatedThing); expect( thingWithStringWithLocale.predicates["https://some.vocab/predicate"] .langStrings, ).toStrictEqual({ "en-us": ["Some arbitrary string"], }); expect( updatedThing.predicates["https://some.vocab/predicate"].langStrings, ).toStrictEqual({ "en-us": [], }); }); it("does nothing if the given string could not be found", () => { const thingWithStringWithLocale = getMockThingWithStringWithLocaleFor( "https://some.vocab/predicate", "Some arbitrary string", "en-us", ); const updatedThing = removeStringWithLocale( thingWithStringWithLocale, "https://some.vocab/other-predicate", "Some other arbitrary string", "nl-nl", ); expect(thingWithStringWithLocale).toStrictEqual(updatedThing); expect( updatedThing.predicates["https://some.vocab/predicate"].langStrings, ).toStrictEqual({ "en-us": ["Some arbitrary string"], }); }); it("also works on ThingLocals", () => { const thingLocal = getMockThingWithStringWithLocaleFor( "https://some.vocab/predicate", "Some arbitrary string", "en-us", ); (thingLocal.url as string) = `${localNodeSkolemPrefix}arbitrary-subject-name`; const updatedThing = removeStringWithLocale( thingLocal, "https://some.vocab/predicate", "Some arbitrary string", "en-us", ); expect( updatedThing.predicates["https://some.vocab/predicate"].langStrings, ).toStrictEqual({ "en-us": [], }); }); 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.predicates[ "https://some.vocab/predicate" ].langStrings!["en-us"] as string[] ).push("Some arbitrary string"); const updatedThing = removeStringWithLocale( thingWithDuplicateStringWithLocale, "https://some.vocab/predicate", "Some arbitrary string", "en-us", ); expect( updatedThing.predicates["https://some.vocab/predicate"].langStrings, ).toStrictEqual({ "en-us": [], }); }); it("does not remove Quads with different Predicates or Objects", () => { const thingWithStringWithLocale = getMockThingWithStringWithLocaleFor( "https://some.vocab/predicate", "Some arbitrary string", "en-us", ); // A different Object: ( thingWithStringWithLocale.predicates["https://some.vocab/predicate"] .langStrings!["en-us"] as string[] ).push("Some other arbitrary string"); // A different locale (thingWithStringWithLocale.predicates["https://some.vocab/predicate"] .langStrings!["en-uk"] as string[]) = ["Some arbitrary string"]; // A different predicate (thingWithStringWithLocale.predicates[ "https://some-other.vocab/predicate" ] as any) = { langStrings: { "en-us": ["Some arbitrary string"], }, }; const updatedThing = removeStringWithLocale( thingWithStringWithLocale, "https://some.vocab/predicate", "Some arbitrary string", "en-US", ); expect(updatedThing.predicates).toStrictEqual({ "https://some.vocab/predicate": { langStrings: { "en-us": ["Some other arbitrary string"], "en-uk": ["Some arbitrary string"], }, }, "https://some-other.vocab/predicate": { langStrings: { "en-us": ["Some arbitrary string"], }, }, }); }); it("removes Quads when the locale casing mismatch", () => { 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( updatedThing.predicates["https://some.vocab/predicate"].langStrings, ).toStrictEqual({ "en-us": [], }); }); it("does not remove Quads with non-string Objects", () => { const thingWithInteger = getMockThingWithStringWithLocaleFor( "https://some.vocab/predicate", "Some arbitrary string", "en-us", ); (thingWithInteger.predicates["https://some.vocab/predicate"] .literals as any) = { "http://www.w3.org/2001/XMLSchema#integer": ["Some arbitrary string"], }; const updatedThing = removeStringWithLocale( thingWithInteger, "https://some.vocab/predicate", "Some arbitrary string", "en-us", ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": ["Some arbitrary string"], }); expect( updatedThing.predicates["https://some.vocab/predicate"].langStrings, ).toStrictEqual({ "en-us": [], }); }); 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.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#string": [], }); }); 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.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#string": [], }); }); 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).not.toStrictEqual(updatedThing); expect( thingWithStringNoLocale.predicates["https://some.vocab/predicate"] .literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#string": ["Some arbitrary string"], }); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#string": [], }); }); it("does nothing if the string to remove could not be found", () => { const thingWithStringNoLocale = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Some arbitrary string", "string", ); const updatedThing = removeStringNoLocale( thingWithStringNoLocale, "https://some.vocab/other-predicate", "Some arbitrary other string", ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toEqual({ "http://www.w3.org/2001/XMLSchema#string": ["Some arbitrary string"], }); }); it("also works on ThingLocals", () => { const thingLocal = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Some arbitrary string", "string", ); (thingLocal.url as string) = `${localNodeSkolemPrefix}arbitrary-subject-name`; const updatedThing = removeStringNoLocale( thingLocal, "https://some.vocab/predicate", "Some arbitrary string", ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#string": [], }); }); it("removes multiple instances of the same string for the same Predicate", () => { const thingWithDuplicateString = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Some arbitrary string", "string", ); ( thingWithDuplicateString.predicates["https://some.vocab/predicate"] .literals!["http://www.w3.org/2001/XMLSchema#string"] as string[] ).push("Some arbitrary string"); const updatedThing = removeStringNoLocale( thingWithDuplicateString, "https://some.vocab/predicate", "Some arbitrary string", ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#string": [], }); }); it("does not remove Quads with different Predicates or Objects", () => { const thingWithOtherQuads = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Some arbitrary string", "string", ); // A different Object: ( thingWithOtherQuads.predicates["https://some.vocab/predicate"].literals![ "http://www.w3.org/2001/XMLSchema#string" ] as string[] ).push("Some other arbitrary string"); // A different predicate (thingWithOtherQuads.predicates[ "https://some-other.vocab/predicate" ] as any) = { literals: { "http://www.w3.org/2001/XMLSchema#string": ["Some arbitrary string"], }, }; const updatedThing = removeStringNoLocale( thingWithOtherQuads, "https://some.vocab/predicate", "Some arbitrary string", ); expect(updatedThing.predicates).toStrictEqual({ "https://some.vocab/predicate": { literals: { "http://www.w3.org/2001/XMLSchema#string": [ "Some other arbitrary string", ], }, }, "https://some-other.vocab/predicate": { literals: { "http://www.w3.org/2001/XMLSchema#string": ["Some arbitrary string"], }, }, }); }); it("does not remove Quads with non-string Objects", () => { const thingWithInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Some arbitrary string", "string", ); (thingWithInteger.predicates["https://some.vocab/predicate"].literals![ "http://www.w3.org/2001/XMLSchema#integer" ] as string[]) = ["Some arbitrary string"]; const updatedThing = removeStringNoLocale( thingWithInteger, "https://some.vocab/predicate", "Some arbitrary string", ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#string": [], "http://www.w3.org/2001/XMLSchema#integer": ["Some arbitrary string"], }); }); it("throws an error when passed something other than a Thing", () => { expect(() => removeStringNoLocale( null as unknown as Thing, "https://arbitrary.vocab/predicate", "Arbitrary string", ), ).toThrow("Expected a Thing, but received: [null]."); }); it("throws an error when passed an invalid property URL", () => { expect(() => removeStringNoLocale( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url", "Arbitrary string", ), ).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 { removeStringNoLocale( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url", "Arbitrary string", ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("removeLiteral", () => { it("accepts unlocalised strings as Literal", () => { const thingWithStringNoLocale = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Some arbitrary string", "string", ); const updatedThing = removeLiteral( thingWithStringNoLocale, "https://some.vocab/predicate", DataFactory.literal( "Some arbitrary string", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#string"), ), ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#string": [], }); }); it("accepts localised strings as Literal", () => { const quad = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.literal("Some arbitrary string", "en-US"), ); const thing = dataset(); thing.add(quad); const thingWithStringWithLocale: Thing = { type: "Subject", url: "https://arbitrary.vocab/subject", predicates: { "https://some.vocab/predicate": { langStrings: { "en-US": ["Some arbitrary string"], }, }, }, }; const updatedThing = removeLiteral( thingWithStringWithLocale, "https://some.vocab/predicate", DataFactory.literal("Some arbitrary string", "en-US"), ); expect( updatedThing.predicates["https://some.vocab/predicate"].langStrings, ).toStrictEqual({ "en-US": [], }); }); it("accepts integers as Literal", () => { const thingWithInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); const updatedThing = removeLiteral( thingWithInteger, "https://some.vocab/predicate", DataFactory.literal( "42", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#integer"), ), ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], }); }); it("accepts decimal as Literal", () => { const thingWithDecimal = getMockThingWithLiteralFor( "https://some.vocab/predicate", "13.37", "decimal", ); const updatedThing = removeLiteral( thingWithDecimal, "https://some.vocab/predicate", DataFactory.literal( "13.37", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#decimal"), ), ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#decimal": [], }); }); it("accepts boolean as Literal", () => { const thingWithBoolean = getMockThingWithLiteralFor( "https://some.vocab/predicate", "1", "boolean", ); const updatedThing = removeLiteral( thingWithBoolean, "https://some.vocab/predicate", DataFactory.literal( "1", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#boolean"), ), ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#boolean": [], }); }); it("accepts time as Literal", () => { const thingWithTime = getMockThingWithLiteralFor( "https://some.vocab/predicate", "13:37:42", "time", ); const updatedThing = removeLiteral( thingWithTime, "https://some.vocab/predicate", DataFactory.literal( "13:37:42", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#time"), ), ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#time": [], }); }); it("accepts datetime as Literal", () => { const thingWithDatetime = getMockThingWithLiteralFor( "https://some.vocab/predicate", "1990-11-12T13:37:42Z", "dateTime", ); const updatedThing = removeLiteral( thingWithDatetime, "https://some.vocab/predicate", DataFactory.literal( "1990-11-12T13:37:42Z", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#dateTime"), ), ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#dateTime": [], }); }); it("accepts Properties as Named Nodes", () => { const thingWithInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); const updatedThing = removeLiteral( thingWithInteger, DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.literal( "42", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#integer"), ), ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], }); }); it("does not modify the input Thing", () => { const thingWithInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); const updatedThing = removeLiteral( thingWithInteger, DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.literal( "42", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#integer"), ), ); expect(thingWithInteger).not.toStrictEqual(updatedThing); expect( thingWithInteger.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": ["42"], }); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], }); }); it("does nothing if the given Literal could not be found", () => { const thingWithInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); const updatedThing = removeLiteral( thingWithInteger, DataFactory.namedNode("https://some.vocab/other-predicate"), DataFactory.literal( "13.37", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#decimal"), ), ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toEqual({ "http://www.w3.org/2001/XMLSchema#integer": ["42"], }); }); it("also works on ThingLocals", () => { const thingLocal = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); (thingLocal.url as string) = `${localNodeSkolemPrefix}arbitrary-subject-name`; const updatedThing = removeLiteral( thingLocal, "https://some.vocab/predicate", DataFactory.literal( "42", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#integer"), ), ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], }); }); it("removes multiple instances of the same Literal for the same Predicate", () => { const thingWithDuplicateInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); ( thingWithDuplicateInteger.predicates["https://some.vocab/predicate"] .literals!["http://www.w3.org/2001/XMLSchema#integer"] as string[] ).push("42"); const updatedThing = removeLiteral( thingWithDuplicateInteger, "https://some.vocab/predicate", DataFactory.literal( "42", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#integer"), ), ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], }); }); it("does not remove Quads with different Predicates or Objects", () => { const thingWithOtherQuads = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); // A different Object: ( thingWithOtherQuads.predicates["https://some.vocab/predicate"].literals![ "http://www.w3.org/2001/XMLSchema#integer" ] as string[] ).push("1337"); // A different predicate (thingWithOtherQuads.predicates[ "https://some-other.vocab/predicate" ] as any) = { literals: { "http://www.w3.org/2001/XMLSchema#integer": ["42"], }, }; const updatedThing = removeLiteral( thingWithOtherQuads, "https://some.vocab/predicate", DataFactory.literal( "42", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#integer"), ), ); expect(updatedThing.predicates).toStrictEqual({ "https://some.vocab/predicate": { literals: { "http://www.w3.org/2001/XMLSchema#integer": ["1337"], }, }, "https://some-other.vocab/predicate": { literals: { "http://www.w3.org/2001/XMLSchema#integer": ["42"], }, }, }); }); it("does not remove Quads with Literal Objects with different types", () => { const thingWithString = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer", ); (thingWithString.predicates["https://some.vocab/predicate"] as any) = { literals: { "http://www.w3.org/2001/XMLSchema#string": ["42"], }, }; const updatedThing = removeLiteral( thingWithString, "https://some.vocab/predicate", DataFactory.literal( "42", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#integer"), ), ); expect( updatedThing.predicates["https://some.vocab/predicate"].literals, ).toStrictEqual({ "http://www.w3.org/2001/XMLSchema#integer": [], "http://www.w3.org/2001/XMLSchema#string": ["42"], }); }); it("throws an error when passed something other than a Thing", () => { expect(() => removeLiteral( null as unknown as Thing, "https://arbitrary.vocab/predicate", DataFactory.literal( "42", DataFactory.namedNode("http://www.w3.org/2001/XMLSchema#integer"), ), ), ).toThrow("Expected a Thing, but received: [null]."); }); it("throws an error when passed an invalid property URL", () => { expect(() => removeLiteral( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url", DataFactory.literal("Arbitrary string value"), ), ).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 { removeLiteral( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url", DataFactory.literal("Arbitrary string value"), ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("removeNamedNode", () => { it("removes the given NamedNode value for the given Predicate", () => { const thingWithNamedNode = getMockThingWithNamedNode( "https://some.vocab/predicate", "https://some.vocab/object", ); const updatedThing = removeNamedNode( thingWithNamedNode, "https://some.vocab/predicate", DataFactory.namedNode("https://some.vocab/object"), ); expect( updatedThing.predicates["https://some.vocab/predicate"].namedNodes, ).toStrictEqual([]); }); it("accepts Properties as Named Nodes", () => { const thingWithNamedNode = getMockThingWithNamedNode( "https://some.vocab/predicate", "https://some.vocab/object", ); const updatedThing = removeNamedNode( thingWithNamedNode, DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.namedNode("https://some.vocab/object"), ); expect( updatedThing.predicates["https://some.vocab/predicate"].namedNodes, ).toStrictEqual([]); }); it("does not modify the input Thing", () => { const thingWithNamedNode = getMockThingWithNamedNode( "https://some.vocab/predicate", "https://some.vocab/object", ); const updatedThing = removeNamedNode( thingWithNamedNode, DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.namedNode("https://some.vocab/object"), ); expect(thingWithNamedNode).not.toStrictEqual(updatedThing); expect( thingWithNamedNode.predicates["https://some.vocab/predicate"].namedNodes, ).toStrictEqual(["https://some.vocab/object"]); expect( updatedThing.predicates["https://some.vocab/predicate"].namedNodes, ).toStrictEqual([]); }); it("also works on ThingLocals", () => { const thingLocal = getMockThingWithNamedNode( "https://some.vocab/predicate", "https://some.vocab/object", ); (thingLocal.url as string) = `${localNodeSkolemPrefix}arbitrary-subject-name`; const updatedThing = removeNamedNode( thingLocal, "https://some.vocab/predicate", DataFactory.namedNode("https://some.vocab/object"), ); expect( updatedThing.predicates["https://some.vocab/predicate"].namedNodes, ).toStrictEqual([]); }); it("removes multiple instances of the same NamedNode for the same Predicate", () => { const thingWithDuplicateNamedNode = getMockThingWithNamedNode( "https://some.vocab/predicate", "https://some.vocab/object", ); ( thingWithDuplicateNamedNode.predicates["https://some.vocab/predicate"] .namedNodes as UrlString[] ).push("https://some.vocab/object"); const updatedThing = removeNamedNode( thingWithDuplicateNamedNode, "https://some.vocab/predicate", DataFactory.namedNode("https://some.vocab/object"), ); expect( updatedThing.predicates["https://some.vocab/predicate"].namedNodes, ).toStrictEqual([]); }); it("does not remove Quads with different Predicates or Objects", () => { const thingWithOtherQuads = getMockThingWithNamedNode( "https://some.vocab/predicate", "https://some.vocab/object", ); ( thingWithOtherQuads.predicates["https://some.vocab/predicate"] .namedNodes as UrlString[] ).push("https://some-other.vocab/object"); (thingWithOtherQuads.predicates["https://some-other.vocab/predicate"] as { namedNodes: UrlString[]; }) = { namedNodes: ["https://some.vocab/object"], }; const updatedThing = removeNamedNode( thingWithOtherQuads, "https://some.vocab/predicate", DataFactory.namedNode("https://some.vocab/object"), ); expect(updatedThing.predicates).toStrictEqual({ "https://some.vocab/predicate": { namedNodes: ["https://some-other.vocab/object"], }, "https://some-other.vocab/predicate": { namedNodes: ["https://some.vocab/object"], }, }); }); it("throws an error when passed something other than a Thing", () => { expect(() => removeNamedNode( null as unknown as Thing, "https://arbitrary.vocab/predicate", DataFactory.namedNode("https://arbitrary.vocab/object"), ), ).toThrow("Expected a Thing, but received: [null]."); }); it("throws an error when passed an invalid property URL", () => { expect(() => removeNamedNode( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url", DataFactory.namedNode("https://arbitrary.vocab/object"), ), ).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 { removeNamedNode( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url", DataFactory.namedNode("https://arbitrary.vocab/object"), ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); });