UNPKG

@inrupt/solid-client

Version:
1,734 lines (1,532 loc) • 82.3 kB
/** * 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 { NamedNode, Quad, Literal } from "rdf-js"; import { DataFactory } from "n3"; import { dataset } from "@rdfjs/dataset"; import { IriString, Iri, Thing } from "../interfaces"; import { getUrl, getBoolean, getDatetime, getDecimal, getInteger, getStringWithLocale, getStringNoLocale, getLiteral, getNamedNode, getUrlAll, getBooleanAll, getDatetimeAll, getDecimalAll, getIntegerAll, getStringWithLocaleAll, getStringByLocaleAll, getStringNoLocaleAll, getLiteralAll, getNamedNodeAll, getTerm, getTermAll, getIriAll, } from "./get"; import { xmlSchemaTypes } from "../datatypes"; import { ValidPropertyUrlExpectedError } from "./thing"; import { mockThingFrom } from "./mock"; const SUBJECT = "https://arbitrary.vocab/subject"; const PREDICATE = "https://some.vocab/predicate"; function makeQuad( subject: IriString, predicate: IriString, value: IriString, locale?: IriString | Iri ): Quad { return DataFactory.quad( DataFactory.namedNode(subject), DataFactory.namedNode(predicate), DataFactory.literal(value, locale) ); } 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 getMockThingWithLiteralsFor( predicate: IriString, literal1Value: string, literal2Value: string, literalType: "string" | "integer" | "decimal" | "boolean" | "dateTime" ): Thing { const quad1 = getMockQuadWithLiteralFor( predicate, literal1Value, literalType ); const quad2 = getMockQuadWithLiteralFor( predicate, literal2Value, literalType ); const thing = dataset(); thing.add(quad1); thing.add(quad2); return Object.assign(thing, { internal_url: "https://arbitrary.vocab/subject", }); } describe("getIri", () => { function getMockQuadWithIri( predicate: IriString, iri: IriString = "https://arbitrary.vocab/object" ): Quad { const quad = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode(predicate), DataFactory.namedNode(iri) ); return quad; } function getMockThingWithIri( predicate: IriString, iri: IriString = "https://arbitrary.vocab/object" ): Thing { const quad = getMockQuadWithIri(predicate, iri); const thing = dataset(); thing.add(quad); return Object.assign(thing, { internal_url: "https://arbitrary.vocab/subject", }); } it("returns the IRI value for the given Property", () => { const thingWithIri = getMockThingWithIri( "https://some.vocab/predicate", "https://some.vocab/object" ); expect(getUrl(thingWithIri, "https://some.vocab/predicate")).toBe( "https://some.vocab/object" ); }); it("accepts Properties as Named Nodes", () => { const thingWithIri = getMockThingWithIri( "https://some.vocab/predicate", "https://some.vocab/object" ); expect( getUrl( thingWithIri, DataFactory.namedNode("https://some.vocab/predicate") ) ).toBe("https://some.vocab/object"); }); it("returns null if no IRI value was found", () => { const thingWithoutIri = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect(getUrl(thingWithoutIri, "https://some.vocab/predicate")).toBeNull(); }); it("does not return non-IRI values", () => { const thingWithDifferentDatatypes = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); thingWithDifferentDatatypes.add( getMockQuadWithIri( "https://some.vocab/predicate", "https://some.vocab/object" ) ); thingWithDifferentDatatypes.add( DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.namedNode("https://arbitrary.vocab/object") ) ); expect( getUrl(thingWithDifferentDatatypes, "https://some.vocab/predicate") ).toBe("https://some.vocab/object"); }); it("returns null if no IRI value was found for the given Predicate", () => { const thingWithIri = getMockThingWithIri("https://some.vocab/predicate"); expect( getUrl(thingWithIri, "https://some-other.vocab/predicate") ).toBeNull(); }); it("throws an error when passed something other than a Thing", () => { expect(() => getUrl((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(() => getUrl(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 { getUrl( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url" ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("getIriAll", () => { function getMockQuadWithIri( predicate: IriString, iri: IriString = "https://arbitrary.vocab/object" ): Quad { const quad = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode(predicate), DataFactory.namedNode(iri) ); return quad; } function getMockThingWithIris( predicate: IriString, iri1: IriString = "https://arbitrary.vocab/object1", iri2: IriString = "https://arbitrary.vocab/object2" ): Thing { const quad1 = getMockQuadWithIri(predicate, iri1); const quad2 = getMockQuadWithIri(predicate, iri2); const thing = dataset(); thing.add(quad1); thing.add(quad2); return Object.assign(thing, { internal_url: "https://arbitrary.vocab/subject", }); } it("returns the IRI values for the given Predicate", () => { const thingWithIris = getMockThingWithIris( "https://some.vocab/predicate", "https://some.vocab/object1", "https://some.vocab/object2" ); expect(getUrlAll(thingWithIris, "https://some.vocab/predicate")).toEqual([ "https://some.vocab/object1", "https://some.vocab/object2", ]); }); it("accepts Properties as Named Nodes", () => { const thingWithIris = getMockThingWithIris( "https://some.vocab/predicate", "https://some.vocab/object1", "https://some.vocab/object2" ); expect( getUrlAll( thingWithIris, DataFactory.namedNode("https://some.vocab/predicate") ) ).toEqual(["https://some.vocab/object1", "https://some.vocab/object2"]); }); it("returns an empty array if no IRI value was found", () => { const thingWithoutIris = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect(getUrlAll(thingWithoutIris, "https://some.vocab/predicate")).toEqual( [] ); }); it("does not return non-IRI values", () => { const thingWithDifferentDatatypes = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); thingWithDifferentDatatypes.add( getMockQuadWithIri( "https://some.vocab/predicate", "https://some.vocab/object" ) ); expect( getUrlAll(thingWithDifferentDatatypes, "https://some.vocab/predicate") ).toEqual(["https://some.vocab/object"]); }); it("returns an empty array if no IRI value was found for the given Predicate", () => { const thingWithIri = getMockThingWithIris("https://some.vocab/predicate"); expect( getUrlAll(thingWithIri, "https://some-other.vocab/predicate") ).toEqual([]); }); it("throws an error when passed something other than a Thing", () => { expect(() => getUrlAll((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(() => getUrlAll( 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 { getIriAll( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url" ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("getBoolean", () => { it("returns the boolean value for the given Predicate", () => { const thingWithBoolean = getMockThingWithLiteralFor( "https://some.vocab/predicate", "1", "boolean" ); expect(getBoolean(thingWithBoolean, "https://some.vocab/predicate")).toBe( true ); }); it("accepts Properties as Named Nodes", () => { const thingWithBoolean = getMockThingWithLiteralFor( "https://some.vocab/predicate", "0", "boolean" ); expect( getBoolean( thingWithBoolean, DataFactory.namedNode("https://some.vocab/predicate") ) ).toBe(false); }); it("returns null if no boolean value was found", () => { const thingWithoutBoolean = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect( getBoolean(thingWithoutBoolean, "https://some.vocab/predicate") ).toBeNull(); }); it("does not return non-boolean values", () => { const thingWithDifferentDatatypes = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Arbitrary value", "string" ); thingWithDifferentDatatypes.add( getMockQuadWithLiteralFor("https://some.vocab/predicate", "1", "boolean") ); thingWithDifferentDatatypes.add( DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.namedNode("https://arbitrary.vocab/object") ) ); expect( getBoolean(thingWithDifferentDatatypes, "https://some.vocab/predicate") ).toBe(true); }); it("returns null if no boolean value was found for the given Predicate", () => { const thingWithBoolean = getMockThingWithLiteralFor( "https://some.vocab/predicate", "1", "boolean" ); expect( getBoolean(thingWithBoolean, "https://some-other.vocab/predicate") ).toBeNull(); }); it("returns null if an invalid value, marked as boolean, was found for the given Predicate", () => { const thingWithNonBoolean = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Not a boolean", "boolean" ); expect( getBoolean(thingWithNonBoolean, "https://some.vocab/predicate") ).toBeNull(); }); it("throws an error when passed something other than a Thing", () => { expect(() => getBoolean( (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(() => getBoolean( 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 { getBoolean( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url" ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("getBooleanAll", () => { it("returns the boolean values for the given Predicate", () => { const thingWithBooleans = getMockThingWithLiteralsFor( "https://some.vocab/predicate", "1", "0", "boolean" ); expect( getBooleanAll(thingWithBooleans, "https://some.vocab/predicate") ).toEqual([true, false]); }); it("accepts Properties as Named Nodes", () => { const thingWithBooleans = getMockThingWithLiteralsFor( "https://some.vocab/predicate", "1", "0", "boolean" ); expect( getBooleanAll( thingWithBooleans, DataFactory.namedNode("https://some.vocab/predicate") ) ).toEqual([true, false]); }); it("returns an empty array if no boolean values were found", () => { const thingWithoutBooleans = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect( getBooleanAll(thingWithoutBooleans, "https://some.vocab/predicate") ).toEqual([]); }); it("does not return non-boolean values", () => { const thingWithDifferentDatatypes = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Arbitrary value", "string" ); thingWithDifferentDatatypes.add( getMockQuadWithLiteralFor("https://some.vocab/predicate", "1", "boolean") ); thingWithDifferentDatatypes.add( DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.namedNode("https://arbitrary.vocab/object") ) ); expect( getBooleanAll(thingWithDifferentDatatypes, "https://some.vocab/predicate") ).toEqual([true]); }); it("returns an empty array if no boolean values were found for the given Predicate", () => { const thingWithBoolean = getMockThingWithLiteralFor( "https://some.vocab/predicate", "1", "boolean" ); expect( getBooleanAll(thingWithBoolean, "https://some-other.vocab/predicate") ).toEqual([]); }); it("does not include invalid values marked as boolean", () => { const thingWithNonBoolean = getMockThingWithLiteralsFor( "https://some.vocab/predicate", "Not a boolean", "0", "boolean" ); expect( getBooleanAll(thingWithNonBoolean, "https://some.vocab/predicate") ).toEqual([false]); }); it("throws an error when passed something other than a Thing", () => { expect(() => getBooleanAll( (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(() => getBooleanAll( 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 { getBooleanAll( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url" ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("getDatetime", () => { it("returns the datetime value for the given Predicate", () => { const thingWithDatetime = getMockThingWithLiteralFor( "https://some.vocab/predicate", "1990-11-12T13:37:42.000Z", "dateTime" ); const expectedDate = new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)); expect( getDatetime(thingWithDatetime, "https://some.vocab/predicate") ).toEqual(expectedDate); }); it("accepts Properties as Named Nodes", () => { const thingWithDatetime = getMockThingWithLiteralFor( "https://some.vocab/predicate", "1990-11-12T13:37:42.000Z", "dateTime" ); const expectedDate = new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)); expect( getDatetime( thingWithDatetime, DataFactory.namedNode("https://some.vocab/predicate") ) ).toEqual(expectedDate); }); it("returns null if no datetime value was found", () => { const thingWithoutDatetime = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect( getDatetime(thingWithoutDatetime, "https://some.vocab/predicate") ).toBeNull(); }); it("does not return non-datetime values", () => { const thingWithDifferentDatatypes = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Arbitrary value", "string" ); thingWithDifferentDatatypes.add( getMockQuadWithLiteralFor( "https://some.vocab/predicate", "1990-11-12T13:37:42.000Z", "dateTime" ) ); thingWithDifferentDatatypes.add( DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.namedNode("https://arbitrary.vocab/object") ) ); const expectedDate = new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)); expect( getDatetime(thingWithDifferentDatatypes, "https://some.vocab/predicate") ).toEqual(expectedDate); }); it("returns null if no datetime value was found for the given Predicate", () => { const thingWithDatetime = getMockThingWithLiteralFor( "https://some.vocab/predicate", "1990-11-12T13:37:42.000Z", "dateTime" ); expect( getDatetime(thingWithDatetime, "https://some-other.vocab/predicate") ).toBeNull(); }); it("returns null if an invalid value, marked as datetime, was found for the given Predicate", () => { const thingWithNonDatetime = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Not a datetime", "dateTime" ); expect( getDatetime(thingWithNonDatetime, "https://some.vocab/predicate") ).toBeNull(); }); it("throws an error when passed something other than a Thing", () => { expect(() => getDatetime( (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(() => getDatetime( 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 { getDatetime( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url" ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("getDatetimeAll", () => { it("returns the datetime values for the given Predicate", () => { const thingWithDatetimes = getMockThingWithLiteralsFor( "https://some.vocab/predicate", "1955-06-08T13:37:42.000Z", "1990-11-12T13:37:42.000Z", "dateTime" ); const expectedDate1 = new Date(Date.UTC(1955, 5, 8, 13, 37, 42, 0)); const expectedDate2 = new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)); expect( getDatetimeAll(thingWithDatetimes, "https://some.vocab/predicate") ).toEqual([expectedDate1, expectedDate2]); }); it("accepts Properties as Named Nodes", () => { const thingWithDatetimes = getMockThingWithLiteralsFor( "https://some.vocab/predicate", "1955-06-08T13:37:42.000Z", "1990-11-12T13:37:42.000Z", "dateTime" ); const expectedDate1 = new Date(Date.UTC(1955, 5, 8, 13, 37, 42, 0)); const expectedDate2 = new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)); expect( getDatetimeAll( thingWithDatetimes, DataFactory.namedNode("https://some.vocab/predicate") ) ).toEqual([expectedDate1, expectedDate2]); }); it("returns an empty array if no datetime values were found", () => { const thingWithoutDatetimes = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect( getDatetimeAll(thingWithoutDatetimes, "https://some.vocab/predicate") ).toEqual([]); }); it("does not return non-datetime values", () => { const thingWithDifferentDatatypes = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Arbitrary value", "string" ); thingWithDifferentDatatypes.add( getMockQuadWithLiteralFor( "https://some.vocab/predicate", "1990-11-12T13:37:42.000Z", "dateTime" ) ); thingWithDifferentDatatypes.add( DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.namedNode("https://arbitrary.vocab/object") ) ); const expectedDate = new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)); expect( getDatetimeAll( thingWithDifferentDatatypes, "https://some.vocab/predicate" ) ).toEqual([expectedDate]); }); it("returns an empty array if no datetime values were found for the given Predicate", () => { const thingWithDatetime = getMockThingWithLiteralFor( "https://some.vocab/predicate", "1990-11-12T13:37:42.000Z", "dateTime" ); expect( getDatetimeAll(thingWithDatetime, "https://some-other.vocab/predicate") ).toEqual([]); }); it("does not return invalid values marked as datetime", () => { const thingWithNonDatetime = getMockThingWithLiteralsFor( "https://some.vocab/predicate", "Not a datetime", "1990-11-12T13:37:42.000Z", "dateTime" ); const expectedDate = new Date(Date.UTC(1990, 10, 12, 13, 37, 42, 0)); expect( getDatetimeAll(thingWithNonDatetime, "https://some.vocab/predicate") ).toEqual([expectedDate]); }); it("throws an error when passed something other than a Thing", () => { expect(() => getDatetimeAll( (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(() => getDatetimeAll( 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 { getDatetimeAll( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url" ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("getDecimal", () => { it("returns the decimal value for the given Predicate", () => { const thingWithDecimal = getMockThingWithLiteralFor( "https://some.vocab/predicate", "13.37", "decimal" ); expect(getDecimal(thingWithDecimal, "https://some.vocab/predicate")).toBe( 13.37 ); }); it("accepts Properties as Named Nodes", () => { const thingWithDecimal = getMockThingWithLiteralFor( "https://some.vocab/predicate", "13.37", "decimal" ); expect( getDecimal( thingWithDecimal, DataFactory.namedNode("https://some.vocab/predicate") ) ).toBe(13.37); }); it("returns null if no decimal value was found", () => { const thingWithoutDecimal = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect( getDecimal(thingWithoutDecimal, "https://some.vocab/predicate") ).toBeNull(); }); it("does not return non-decimal values", () => { const thingWithDifferentDatatypes = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Arbitrary value", "string" ); thingWithDifferentDatatypes.add( getMockQuadWithLiteralFor( "https://some.vocab/predicate", "13.37", "decimal" ) ); thingWithDifferentDatatypes.add( DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.namedNode("https://arbitrary.vocab/object") ) ); expect( getDecimal(thingWithDifferentDatatypes, "https://some.vocab/predicate") ).toBe(13.37); }); it("returns null if no decimal value was found for the given Predicate", () => { const thingWithDecimal = getMockThingWithLiteralFor( "https://some.vocab/predicate", "13.37", "decimal" ); expect( getDecimal(thingWithDecimal, "https://some-other.vocab/predicate") ).toBeNull(); }); it("throws an error when passed something other than a Thing", () => { expect(() => getDecimal( (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(() => getDecimal( 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 { getDecimal( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url" ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("getDecimalAll", () => { it("returns the decimal values for the given Predicate", () => { const thingWithDecimals = getMockThingWithLiteralsFor( "https://some.vocab/predicate", "13.37", "7.2", "decimal" ); expect( getDecimalAll(thingWithDecimals, "https://some.vocab/predicate") ).toEqual([13.37, 7.2]); }); it("accepts Properties as Named Nodes", () => { const thingWithDecimals = getMockThingWithLiteralsFor( "https://some.vocab/predicate", "13.37", "7.2", "decimal" ); expect( getDecimalAll( thingWithDecimals, DataFactory.namedNode("https://some.vocab/predicate") ) ).toEqual([13.37, 7.2]); }); it("returns an empty array if no decimal values were found", () => { const thingWithoutDecimals = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect( getDecimalAll(thingWithoutDecimals, "https://some.vocab/predicate") ).toEqual([]); }); it("does not return non-decimal values", () => { const thingWithDifferentDatatypes = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Arbitrary value", "string" ); thingWithDifferentDatatypes.add( getMockQuadWithLiteralFor( "https://some.vocab/predicate", "13.37", "decimal" ) ); thingWithDifferentDatatypes.add( DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.namedNode("https://arbitrary.vocab/object") ) ); expect( getDecimalAll(thingWithDifferentDatatypes, "https://some.vocab/predicate") ).toEqual([13.37]); }); it("returns an empty array if no decimal values were found for the given Predicate", () => { const thingWithDecimal = getMockThingWithLiteralFor( "https://some.vocab/predicate", "13.37", "decimal" ); expect( getDecimalAll(thingWithDecimal, "https://some-other.vocab/predicate") ).toEqual([]); }); it("throws an error when passed something other than a Thing", () => { expect(() => getDecimalAll( (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(() => getDecimalAll( 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 { getDecimalAll( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url" ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("getInteger", () => { it("returns the integer value for the given Predicate", () => { const thingWithInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect(getInteger(thingWithInteger, "https://some.vocab/predicate")).toBe( 42 ); }); it("accepts Properties as Named Nodes", () => { const thingWithInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect( getInteger( thingWithInteger, DataFactory.namedNode("https://some.vocab/predicate") ) ).toBe(42); }); it("returns null if no integer value was found", () => { const thingWithoutInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "13.37", "decimal" ); expect( getInteger(thingWithoutInteger, "https://some.vocab/predicate") ).toBeNull(); }); it("does not return non-integer values", () => { const thingWithDifferentDatatypes = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Arbitrary value", "string" ); thingWithDifferentDatatypes.add( getMockQuadWithLiteralFor("https://some.vocab/predicate", "42", "integer") ); thingWithDifferentDatatypes.add( DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.namedNode("https://arbitrary.vocab/object") ) ); expect( getInteger(thingWithDifferentDatatypes, "https://some.vocab/predicate") ).toBe(42); }); it("returns null if no integer value was found for the given Predicate", () => { const thingWithInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect( getInteger(thingWithInteger, "https://some-other.vocab/predicate") ).toBeNull(); }); it("throws an error when passed something other than a Thing", () => { expect(() => getInteger( (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(() => getInteger( 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 { getInteger( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url" ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("getIntegerAll", () => { it("returns the integer values for the given Predicate", () => { const thingWithIntegers = getMockThingWithLiteralsFor( "https://some.vocab/predicate", "42", "1337", "integer" ); expect( getIntegerAll(thingWithIntegers, "https://some.vocab/predicate") ).toEqual([42, 1337]); }); it("accepts Properties as Named Nodes", () => { const thingWithIntegers = getMockThingWithLiteralsFor( "https://some.vocab/predicate", "42", "1337", "integer" ); expect( getIntegerAll( thingWithIntegers, DataFactory.namedNode("https://some.vocab/predicate") ) ).toEqual([42, 1337]); }); it("returns an empty array if no integer values were found", () => { const thingWithoutIntegers = getMockThingWithLiteralFor( "https://some.vocab/predicate", "13.37", "decimal" ); expect( getIntegerAll(thingWithoutIntegers, "https://some.vocab/predicate") ).toEqual([]); }); it("does not return non-integer values", () => { const thingWithDifferentDatatypes = getMockThingWithLiteralFor( "https://some.vocab/predicate", "Arbitrary value", "string" ); thingWithDifferentDatatypes.add( getMockQuadWithLiteralFor("https://some.vocab/predicate", "42", "integer") ); thingWithDifferentDatatypes.add( DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.namedNode("https://arbitrary.vocab/object") ) ); expect( getIntegerAll(thingWithDifferentDatatypes, "https://some.vocab/predicate") ).toEqual([42]); }); it("returns an empty array if no integer values were found for the given Predicate", () => { const thingWithInteger = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect( getIntegerAll(thingWithInteger, "https://some-other.vocab/predicate") ).toEqual([]); }); it("throws an error when passed something other than a Thing", () => { expect(() => getIntegerAll( (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(() => getIntegerAll( 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 { getIntegerAll( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url" ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("getStringWithLocale", () => { it("returns the string value for the given Predicate in the given locale", () => { const literalWithLocale = DataFactory.literal("Some value", "nl-NL"); const quad = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), literalWithLocale ); const thing = dataset(); thing.add(quad); const thingWithLocaleString = Object.assign(thing, { internal_url: "https://arbitrary.vocab/subject", }); expect( getStringWithLocale( thingWithLocaleString, "https://some.vocab/predicate", "nl-NL" ) ).toBe("Some value"); }); it("accepts Properties as Named Nodes", () => { const literalWithLocale = DataFactory.literal("Some value", "nl-NL"); const quad = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), literalWithLocale ); const thing = dataset(); thing.add(quad); const thingWithLocaleString = Object.assign(thing, { internal_url: "https://arbitrary.vocab/subject", }); expect( getStringWithLocale( thingWithLocaleString, DataFactory.namedNode("https://some.vocab/predicate"), "nl-NL" ) ).toBe("Some value"); }); it("supports matching locales with different casing", () => { const literalWithLocale = DataFactory.literal("Some value", "nl-NL"); const quad = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), literalWithLocale ); const thing = dataset(); thing.add(quad); const thingWithLocaleString = Object.assign(thing, { internal_url: "https://arbitrary.vocab/subject", }); expect( getStringWithLocale( thingWithLocaleString, "https://some.vocab/predicate", "NL-nL" ) ).toBe("Some value"); }); it("returns null if no locale string value was found", () => { const thingWithoutStringNoLocale = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); expect( getStringWithLocale( thingWithoutStringNoLocale, "https://some.vocab/predicate", "nl-NL" ) ).toBeNull(); }); it("returns null if no locale string with the requested locale was found", () => { const literalWithLocale = DataFactory.literal("Some value", "nl-NL"); const quad = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), literalWithLocale ); const thing = dataset(); thing.add(quad); const thingWithDifferentLocaleString = Object.assign(thing, { internal_url: "https://arbitrary.vocab/subject", }); expect( getStringWithLocale( thingWithDifferentLocaleString, "https://some.vocab/predicate", "en-GB" ) ).toBeNull(); expect( getStringWithLocale( thingWithDifferentLocaleString, "https://some.vocab/predicate", "nl" ) ).toBeNull(); }); it("does not return non-locale-string values", () => { const literalWithLocale = DataFactory.literal("Some value", "nl-NL"); const quadWithLocaleString = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), literalWithLocale ); const thingWithDifferentDatatypes = getMockThingWithLiteralFor( "https://some.vocab/predicate", "42", "integer" ); thingWithDifferentDatatypes.add(quadWithLocaleString); thingWithDifferentDatatypes.add( DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), DataFactory.namedNode("https://arbitrary.vocab/object") ) ); expect( getStringWithLocale( thingWithDifferentDatatypes, "https://some.vocab/predicate", "nl-NL" ) ).toBe("Some value"); }); it("returns null if no locale string was found for the given Predicate", () => { const literalWithLocale = DataFactory.literal("Arbitrary value", "nl-NL"); const quad = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), literalWithLocale ); const thing = dataset(); thing.add(quad); const thingWithLocaleString = Object.assign(thing, { internal_url: "https://arbitrary.vocab/subject", }); expect( getStringWithLocale( thingWithLocaleString, "https://some-other.vocab/predicate", "nl-NL" ) ).toBeNull(); }); it("throws an error when passed something other than a Thing", () => { expect(() => getStringWithLocale( (null as unknown) as Thing, "https://arbitrary.vocab/predicate", "nl-NL" ) ).toThrow("Expected a Thing, but received: [null]."); }); it("throws an error when passed an invalid property URL", () => { expect(() => getStringWithLocale( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url", "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 { getStringWithLocale( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url", "nl-NL" ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("getStringByLocaleAll", () => { it("returns all the string values for the given Predicate keyed by their locale", () => { const thing = dataset() .add(makeQuad(SUBJECT, PREDICATE, "value 1", "en")) .add(makeQuad(SUBJECT, PREDICATE, "value 2", "en")) .add(makeQuad(SUBJECT, PREDICATE, "value 3", "fr")) .add(makeQuad(SUBJECT, PREDICATE, "value 4", "es")); const thingWithLocaleStrings = Object.assign(thing, { internal_url: SUBJECT, }); expect( Array.from(getStringByLocaleAll(thingWithLocaleStrings, PREDICATE)) ).toEqual([ ["en", ["value 1", "value 2"]], ["fr", ["value 3"]], ["es", ["value 4"]], ]); }); it("excludes non-language string values", () => { const thing = dataset() .add(makeQuad(SUBJECT, PREDICATE, "value 1", "fr")) .add(makeQuad(SUBJECT, PREDICATE, "value 2")) .add(makeQuad(SUBJECT, PREDICATE, "value 3", "es")) .add(makeQuad(SUBJECT, PREDICATE, "value 4")) .add(makeQuad(SUBJECT, PREDICATE, "value 5", "es")); const thingWithLocaleStrings = Object.assign(thing, { internal_url: SUBJECT, }); expect( Array.from(getStringByLocaleAll(thingWithLocaleStrings, PREDICATE)) ).toEqual([ ["fr", ["value 1"]], ["es", ["value 3", "value 5"]], ]); }); it("allows empty language tags - literal is still an rdf:langString", () => { const thing = dataset() .add(makeQuad(SUBJECT, PREDICATE, "chien", "fr")) .add(makeQuad(SUBJECT, PREDICATE, "dog", "")); const thingWithLocaleStrings = Object.assign(thing, { internal_url: SUBJECT, }); // An empty language tag is valid, and even in Turtle should be serialized // as an rdf:langString, e.g.: // <SUBJECT> <PREDICATE> "chien"@fr . // <SUBJECT> <PREDICATE> "dog"^^rdf:langString . expect( Array.from(getStringByLocaleAll(thingWithLocaleStrings, PREDICATE)) ).toEqual([ ["fr", ["chien"]], ["", ["dog"]], ]); }); it("ignores non-string literals...", () => { const thing = dataset() .add(makeQuad(SUBJECT, PREDICATE, "value 1", "fr")) .add(makeQuad(SUBJECT, PREDICATE, "value 2")) .add( makeQuad( SUBJECT, PREDICATE, "99.999", DataFactory.namedNode(xmlSchemaTypes.decimal) ) ); const thingWithLocaleStrings = Object.assign(thing, { internal_url: SUBJECT, }); expect( Array.from(getStringByLocaleAll(thingWithLocaleStrings, PREDICATE)) ).toEqual([["fr", ["value 1"]]]); }); it("throws an error when passed something other than a Thing", () => { expect(() => getStringByLocaleAll( (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(() => getStringByLocaleAll( 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 { getStringByLocaleAll( mockThingFrom("https://arbitrary.pod/resource#thing"), "not-a-url" ); } catch (e) { thrownError = e; } expect(thrownError).toBeInstanceOf(ValidPropertyUrlExpectedError); }); }); describe("getStringWithLocaleAll", () => { it("treats empty-string locale literally - i.e. only returns values added with an empty language tag", () => { const thing = dataset() .add(makeQuad(SUBJECT, PREDICATE, "value 1")) .add(makeQuad(SUBJECT, PREDICATE, "value 2", "fr")) .add(makeQuad(SUBJECT, PREDICATE, "value 3", "")) .add(makeQuad(SUBJECT, PREDICATE, "value 4", "es")); const thingWithLocaleStrings = Object.assign(thing, { internal_url: SUBJECT, }); // TODO: Not sure this RDF-spec compliant - need to double check, but // according to https://w3c.github.io/rdf-dir-literal/langString.html#literals // it seems language tags *must* be non-empty... expect( Array.from(getStringWithLocaleAll(thingWithLocaleStrings, PREDICATE, "")) ).toEqual(["value 3"]); }); it("returns the string values for the given Predicate in the given locale", () => { const literalWithLocale1 = DataFactory.literal("Some value 1", "nl-NL"); const quad1 = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), literalWithLocale1 ); const literalWithLocale2 = DataFactory.literal("Some value 2", "nl-NL"); const quad2 = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), literalWithLocale2 ); const thing = dataset(); thing.add(quad1); thing.add(quad2); const thingWithLocaleStrings = Object.assign(thing, { internal_url: "https://arbitrary.vocab/subject", }); expect( getStringWithLocaleAll( thingWithLocaleStrings, "https://some.vocab/predicate", "nl-NL" ) ).toEqual(["Some value 1", "Some value 2"]); }); it("accepts Properties as Named Nodes", () => { const literalWithLocale1 = DataFactory.literal("Some value 1", "nl-NL"); const quad1 = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), literalWithLocale1 ); const literalWithLocale2 = DataFactory.literal("Some value 2", "nl-NL"); const quad2 = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subject"), DataFactory.namedNode("https://some.vocab/predicate"), literalWithLocale2 ); const thing = dataset(); thing.add(quad1); thing.add(quad2); const thingWithLocaleStrings = Object.assign(thing, { internal_url: "https://arbitrary.vocab/subject", }); expect( getStringWithLocaleAll( thingWithLocaleStrings, DataFactory.namedNode("https://some.vocab/predicate"), "nl-NL" ) ).toEqual(["Some value 1", "Some value 2"]); }); it("supports matching locales with different casing", () => { const literalWithLocale = DataFactory.literal("Some value", "nl-NL"); const quad = DataFactory.quad( DataFactory.namedNode("https://arbitrary.vocab/subjec