@noeldemartin/solid-utils 0.6.0-next.cccdc9c7e033588e2df9d1887ceae49788344d84 → 0.6.0-next.cfba421315c47e4aa5a72990714376d9dcd1ffff
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/io-CMHtz5bu.js +401 -0
- package/dist/io-CMHtz5bu.js.map +1 -0
- package/dist/noeldemartin-solid-utils.d.ts +51 -3
- package/dist/noeldemartin-solid-utils.js +166 -532
- package/dist/noeldemartin-solid-utils.js.map +1 -1
- package/dist/testing.d.ts +75 -0
- package/dist/testing.js +176 -0
- package/dist/testing.js.map +1 -0
- package/package.json +14 -9
- package/src/errors/UnauthorizedError.ts +1 -3
- package/src/errors/UnsuccessfulNetworkRequestError.ts +2 -2
- package/src/helpers/identifiers.ts +13 -16
- package/src/helpers/io.ts +10 -10
- package/src/helpers/jsonld.ts +5 -5
- package/src/helpers/vocabs.ts +3 -6
- package/src/index.ts +1 -0
- package/src/testing/chai/assertions.ts +35 -0
- package/src/testing/chai/index.ts +19 -0
- package/src/testing/helpers.ts +187 -0
- package/src/testing/hepers.test.ts +329 -0
- package/src/testing/index.ts +3 -0
- package/src/testing/types/index.ts +2 -0
- package/src/testing/vitest/index.ts +20 -0
- package/src/testing/vitest/matchers.ts +68 -0
- package/src/types/n3.d.ts +0 -2
|
@@ -9,11 +9,9 @@ export interface SubjectParts {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
function getContainerPath(parts: UrlParts): string | null {
|
|
12
|
-
if (!parts.path || !parts.path.startsWith('/'))
|
|
13
|
-
return null;
|
|
12
|
+
if (!parts.path || !parts.path.startsWith('/')) return null;
|
|
14
13
|
|
|
15
|
-
if (parts.path.match(/^\/[^/]*$/))
|
|
16
|
-
return '/';
|
|
14
|
+
if (parts.path.match(/^\/[^/]*$/)) return '/';
|
|
17
15
|
|
|
18
16
|
return `/${arr(parts.path.split('/')).filter().slice(0, -1).join('/')}/`.replace('//', '/');
|
|
19
17
|
}
|
|
@@ -27,30 +25,29 @@ function getContainerUrl(parts: UrlParts): string | null {
|
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
function __mintJsonLDIdentifiers(jsonld: JsonLD): void {
|
|
30
|
-
if (!('@type' in jsonld) || '@value' in jsonld)
|
|
31
|
-
return;
|
|
28
|
+
if (!('@type' in jsonld) || '@value' in jsonld) return;
|
|
32
29
|
|
|
33
30
|
jsonld['@id'] = jsonld['@id'] ?? uuid();
|
|
34
31
|
|
|
35
32
|
for (const propertyValue of Object.values(jsonld)) {
|
|
36
|
-
if (isObject(propertyValue))
|
|
37
|
-
__mintJsonLDIdentifiers(propertyValue);
|
|
33
|
+
if (isObject(propertyValue)) __mintJsonLDIdentifiers(propertyValue);
|
|
38
34
|
|
|
39
|
-
if (isArray(propertyValue))
|
|
40
|
-
propertyValue.forEach(value => isObject(value) && __mintJsonLDIdentifiers(value));
|
|
35
|
+
if (isArray(propertyValue)) propertyValue.forEach((value) => isObject(value) && __mintJsonLDIdentifiers(value));
|
|
41
36
|
}
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
export function mintJsonLDIdentifiers(jsonld: JsonLD): JsonLDResource {
|
|
45
|
-
return tap(objectDeepClone(jsonld) as JsonLDResource, clone => __mintJsonLDIdentifiers(clone));
|
|
40
|
+
return tap(objectDeepClone(jsonld) as JsonLDResource, (clone) => __mintJsonLDIdentifiers(clone));
|
|
46
41
|
}
|
|
47
42
|
|
|
48
43
|
export function parseResourceSubject(subject: string): SubjectParts {
|
|
49
44
|
const parts = urlParse(subject);
|
|
50
45
|
|
|
51
|
-
return !parts
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
return !parts
|
|
47
|
+
? {}
|
|
48
|
+
: objectWithoutEmpty({
|
|
49
|
+
containerUrl: getContainerUrl(parts),
|
|
50
|
+
documentName: parts.path ? parts.path.split('/').pop() : null,
|
|
51
|
+
resourceHash: parts.fragment,
|
|
52
|
+
});
|
|
56
53
|
}
|
package/src/helpers/io.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import jsonld from 'jsonld';
|
|
1
2
|
import md5 from 'md5';
|
|
2
3
|
import { arr, arrayFilter, arrayReplace, objectWithoutEmpty, stringMatchAll, tap } from '@noeldemartin/utils';
|
|
3
4
|
import { BlankNode as N3BlankNode, Quad as N3Quad, Parser, Writer } from 'n3';
|
|
4
|
-
import { fromRDF, toRDF } from 'jsonld';
|
|
5
5
|
import type { JsonLdDocument } from 'jsonld';
|
|
6
6
|
import type { Quad } from '@rdfjs/types';
|
|
7
7
|
import type { Term } from 'n3';
|
|
@@ -117,12 +117,12 @@ function normalizeQuads(quads: Quad[]): string {
|
|
|
117
117
|
.join('\n');
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
function preprocessSubjects(
|
|
121
|
-
if (!
|
|
120
|
+
function preprocessSubjects(json: JsonLD): void {
|
|
121
|
+
if (!json['@id']?.startsWith('#')) {
|
|
122
122
|
return;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
json['@id'] = ANONYMOUS_PREFIX + json['@id'];
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
function postprocessSubjects(quads: Quad[]): void {
|
|
@@ -186,16 +186,16 @@ export async function fetchSolidDocumentIfFound(
|
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
export async function jsonldToQuads(
|
|
190
|
-
if (isJsonLDGraph(
|
|
191
|
-
const graphQuads = await Promise.all(
|
|
189
|
+
export async function jsonldToQuads(json: JsonLD, baseIRI?: string): Promise<Quad[]> {
|
|
190
|
+
if (isJsonLDGraph(json)) {
|
|
191
|
+
const graphQuads = await Promise.all(json['@graph'].map((resource) => jsonldToQuads(resource, baseIRI)));
|
|
192
192
|
|
|
193
193
|
return graphQuads.flat();
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
preprocessSubjects(
|
|
196
|
+
preprocessSubjects(json);
|
|
197
197
|
|
|
198
|
-
const quads = await (toRDF(
|
|
198
|
+
const quads = await (jsonld.toRDF(json as JsonLdDocument, { base: baseIRI }) as Promise<Quad[]>);
|
|
199
199
|
|
|
200
200
|
postprocessSubjects(quads);
|
|
201
201
|
|
|
@@ -263,7 +263,7 @@ export function parseTurtle(turtle: string, options: Partial<ParsingOptions> = {
|
|
|
263
263
|
}
|
|
264
264
|
|
|
265
265
|
export async function quadsToJsonLD(quads: Quad[]): Promise<JsonLDGraph> {
|
|
266
|
-
const graph = await fromRDF(quads);
|
|
266
|
+
const graph = await jsonld.fromRDF(quads);
|
|
267
267
|
|
|
268
268
|
return {
|
|
269
269
|
'@graph': graph as JsonLDResource[],
|
package/src/helpers/jsonld.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import jsonld from 'jsonld';
|
|
2
2
|
import type { JsonLdDocument } from 'jsonld';
|
|
3
3
|
|
|
4
4
|
export type JsonLD = Partial<{
|
|
@@ -13,8 +13,8 @@ export type JsonLDGraph = {
|
|
|
13
13
|
'@graph': JsonLDResource[];
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export async function compactJsonLDGraph(
|
|
17
|
-
const compactedJsonLD = await compact(
|
|
16
|
+
export async function compactJsonLDGraph(json: JsonLDGraph): Promise<JsonLDGraph> {
|
|
17
|
+
const compactedJsonLD = await jsonld.compact(json as JsonLdDocument, {});
|
|
18
18
|
|
|
19
19
|
if ('@graph' in compactedJsonLD) {
|
|
20
20
|
return compactedJsonLD as JsonLDGraph;
|
|
@@ -27,6 +27,6 @@ export async function compactJsonLDGraph(jsonld: JsonLDGraph): Promise<JsonLDGra
|
|
|
27
27
|
return { '@graph': [] };
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export function isJsonLDGraph(
|
|
31
|
-
return '@graph' in
|
|
30
|
+
export function isJsonLDGraph(json: JsonLD): json is JsonLDGraph {
|
|
31
|
+
return '@graph' in json;
|
|
32
32
|
}
|
package/src/helpers/vocabs.ts
CHANGED
|
@@ -23,22 +23,19 @@ export function defineIRIPrefix(name: string, value: string): void {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export function expandIRI(iri: string, options: Partial<ExpandIRIOptions> = {}): string {
|
|
26
|
-
if (iri.startsWith('http'))
|
|
27
|
-
return iri;
|
|
26
|
+
if (iri.startsWith('http')) return iri;
|
|
28
27
|
|
|
29
28
|
const [prefix, name] = iri.split(':');
|
|
30
29
|
|
|
31
30
|
if (prefix && name) {
|
|
32
31
|
const expandedPrefix = knownPrefixes[prefix] ?? options.extraContext?.[prefix] ?? null;
|
|
33
32
|
|
|
34
|
-
if (!expandedPrefix)
|
|
35
|
-
throw new Error(`Can't expand IRI with unknown prefix: '${iri}'`);
|
|
33
|
+
if (!expandedPrefix) throw new Error(`Can't expand IRI with unknown prefix: '${iri}'`);
|
|
36
34
|
|
|
37
35
|
return expandedPrefix + name;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
if (!options.defaultPrefix)
|
|
41
|
-
throw new Error(`Can't expand IRI without a default prefix: '${iri}'`);
|
|
38
|
+
if (!options.defaultPrefix) throw new Error(`Can't expand IRI without a default prefix: '${iri}'`);
|
|
42
39
|
|
|
43
40
|
return options.defaultPrefix + prefix;
|
|
44
41
|
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { sparqlEquals, turtleEquals } from '@noeldemartin/solid-utils/testing/helpers';
|
|
2
|
+
import type { EqualityResult } from '@noeldemartin/solid-utils/testing/helpers';
|
|
3
|
+
|
|
4
|
+
export function defineChaiAssertions<T extends Record<string, (this: Chai.AssertionStatic, ...args: any[]) => void>>(
|
|
5
|
+
assertions: T): T {
|
|
6
|
+
return assertions;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default defineChaiAssertions({
|
|
10
|
+
turtle(graph: string): void {
|
|
11
|
+
const self = this as unknown as Chai.AssertionStatic;
|
|
12
|
+
const actual = self._obj as string;
|
|
13
|
+
const assert = self.assert.bind(this);
|
|
14
|
+
const expected = graph;
|
|
15
|
+
const result = turtleEquals(expected, actual);
|
|
16
|
+
|
|
17
|
+
assert(result.success, result.message, '', result.expected, result.actual);
|
|
18
|
+
},
|
|
19
|
+
sparql(query: string): void {
|
|
20
|
+
const self = this as unknown as Chai.AssertionStatic;
|
|
21
|
+
const actual = self._obj as string;
|
|
22
|
+
const assert = self.assert.bind(this);
|
|
23
|
+
const expected = query;
|
|
24
|
+
const result = sparqlEquals(expected, actual);
|
|
25
|
+
|
|
26
|
+
assert(result.success, result.message, '', result.expected, result.actual);
|
|
27
|
+
},
|
|
28
|
+
equalityResult(): void {
|
|
29
|
+
const self = this as unknown as Chai.AssertionStatic;
|
|
30
|
+
const result = self._obj as EqualityResult;
|
|
31
|
+
const assert = self.assert.bind(this);
|
|
32
|
+
|
|
33
|
+
assert(result.success, result.message, '', result.expected, result.actual);
|
|
34
|
+
},
|
|
35
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import assertions from './assertions';
|
|
2
|
+
|
|
3
|
+
export type ChaiSolidAssertions = {
|
|
4
|
+
[assertion in keyof typeof assertions]: (typeof assertions)[assertion];
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export function installChaiSolidAssertions(): void {
|
|
8
|
+
(globalThis as { chai?: Chai.ChaiStatic }).chai?.use((_chai) => {
|
|
9
|
+
return Object.entries(assertions).forEach(([name, method]) => _chai.Assertion.addMethod(name, method));
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare global {
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
15
|
+
namespace Chai {
|
|
16
|
+
interface Assertion extends ChaiSolidAssertions {}
|
|
17
|
+
interface Include extends ChaiSolidAssertions {}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { JSError, arrayRemove, pull, stringMatchAll } from '@noeldemartin/utils';
|
|
2
|
+
import type { Quad, Quad_Object } from '@rdfjs/types';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
jsonldToQuads,
|
|
6
|
+
quadToTurtle,
|
|
7
|
+
quadsToTurtle,
|
|
8
|
+
sparqlToQuadsSync,
|
|
9
|
+
turtleToQuadsSync,
|
|
10
|
+
} from '@noeldemartin/solid-utils/helpers/io';
|
|
11
|
+
import type { JsonLD } from '@noeldemartin/solid-utils/helpers/jsonld';
|
|
12
|
+
|
|
13
|
+
let patternsRegExpsIndex: Record<string, RegExp> = {};
|
|
14
|
+
const builtInPatterns: Record<string, string> = {
|
|
15
|
+
'%uuid%': '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
class ExpectedQuadAssertionError extends JSError {
|
|
19
|
+
|
|
20
|
+
constructor(public readonly expectedQuad: Quad) {
|
|
21
|
+
super(`Couldn't find the following triple: ${quadToTurtle(expectedQuad)}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function assertExpectedQuadsExist(expectedQuads: Quad[], actualQuads: Quad[]): void {
|
|
27
|
+
for (const expectedQuad of expectedQuads) {
|
|
28
|
+
const matchingQuad = actualQuads.find((actualQuad) => quadEquals(expectedQuad, actualQuad));
|
|
29
|
+
|
|
30
|
+
if (!matchingQuad) throw new ExpectedQuadAssertionError(expectedQuad);
|
|
31
|
+
|
|
32
|
+
arrayRemove(actualQuads, matchingQuad);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function containsPatterns(value: string): boolean {
|
|
37
|
+
return /\[\[(.*\]\[)?([^\]]+)\]\]/.test(value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function createPatternRegexp(expected: string): RegExp {
|
|
41
|
+
const patternAliases = [];
|
|
42
|
+
const patternMatches = stringMatchAll<4, 1 | 2>(expected, /\[\[((.*?)\]\[)?([^\]]+)\]\]/g);
|
|
43
|
+
const patterns: string[] = [];
|
|
44
|
+
let expectedRegExp = expected;
|
|
45
|
+
|
|
46
|
+
for (const patternMatch of patternMatches) {
|
|
47
|
+
patternMatch[2] && patternAliases.push(patternMatch[2]);
|
|
48
|
+
|
|
49
|
+
patterns.push(patternMatch[3]);
|
|
50
|
+
|
|
51
|
+
expectedRegExp = expectedRegExp.replace(patternMatch[0], `%PATTERN${patterns.length - 1}%`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
expectedRegExp = expectedRegExp.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
|
55
|
+
|
|
56
|
+
for (const [patternIndex, pattern] of Object.entries(patterns)) {
|
|
57
|
+
expectedRegExp = expectedRegExp.replace(`%PATTERN${patternIndex}%`, builtInPatterns[pattern] ?? pattern);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return new RegExp(expectedRegExp);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function quadValueEquals(expected: string, actual: string): boolean {
|
|
64
|
+
return containsPatterns(expected)
|
|
65
|
+
? (patternsRegExpsIndex[expected] ??= createPatternRegexp(expected)).test(actual)
|
|
66
|
+
: expected === actual;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function quadObjectEquals(expected: Quad_Object, actual: Quad_Object): boolean {
|
|
70
|
+
if (expected.termType !== actual.termType) return false;
|
|
71
|
+
|
|
72
|
+
if (expected.termType === 'Literal' && actual.termType === 'Literal') {
|
|
73
|
+
if (expected.datatype.value !== actual.datatype.value) return false;
|
|
74
|
+
|
|
75
|
+
if (!containsPatterns(expected.value))
|
|
76
|
+
return expected.datatype.value === 'http://www.w3.org/2001/XMLSchema#dateTime'
|
|
77
|
+
? new Date(expected.value).getTime() === new Date(actual.value).getTime()
|
|
78
|
+
: expected.value === actual.value;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return quadValueEquals(expected.value, actual.value);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function quadEquals(expected: Quad, actual: Quad): boolean {
|
|
85
|
+
return (
|
|
86
|
+
quadObjectEquals(expected.object, actual.object) &&
|
|
87
|
+
quadValueEquals(expected.subject.value, actual.subject.value) &&
|
|
88
|
+
quadValueEquals(expected.predicate.value, actual.predicate.value)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function resetPatterns(): void {
|
|
93
|
+
patternsRegExpsIndex = {};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface EqualityResult {
|
|
97
|
+
success: boolean;
|
|
98
|
+
message: string;
|
|
99
|
+
expected: string;
|
|
100
|
+
actual: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function jsonldEquals(expected: JsonLD, actual: JsonLD): Promise<EqualityResult> {
|
|
104
|
+
// TODO catch parsing errors and improve message.
|
|
105
|
+
resetPatterns();
|
|
106
|
+
|
|
107
|
+
const expectedQuads = await jsonldToQuads(expected);
|
|
108
|
+
const actualQuads = await jsonldToQuads(actual);
|
|
109
|
+
const expectedTurtle = quadsToTurtle(expectedQuads);
|
|
110
|
+
const actualTurtle = quadsToTurtle(actualQuads);
|
|
111
|
+
const result = (success: boolean, message: string) => ({
|
|
112
|
+
success,
|
|
113
|
+
message,
|
|
114
|
+
expected: expectedTurtle,
|
|
115
|
+
actual: actualTurtle,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (expectedQuads.length !== actualQuads.length)
|
|
119
|
+
return result(false, `Expected ${expectedQuads.length} triples, found ${actualQuads.length}.`);
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
assertExpectedQuadsExist(expectedQuads, actualQuads);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (!(error instanceof ExpectedQuadAssertionError)) throw error;
|
|
125
|
+
|
|
126
|
+
return result(false, error.message);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return result(true, 'jsonld matches');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function sparqlEquals(expected: string, actual: string): EqualityResult {
|
|
133
|
+
// TODO catch parsing errors and improve message.
|
|
134
|
+
resetPatterns();
|
|
135
|
+
|
|
136
|
+
const expectedOperations = sparqlToQuadsSync(expected, { normalizeBlankNodes: true });
|
|
137
|
+
const actualOperations = sparqlToQuadsSync(actual, { normalizeBlankNodes: true });
|
|
138
|
+
const result = (success: boolean, message: string) => ({ success, message, expected, actual });
|
|
139
|
+
|
|
140
|
+
for (const operation of Object.keys(expectedOperations)) {
|
|
141
|
+
if (!(operation in actualOperations)) return result(false, `Couldn't find expected ${operation} operation.`);
|
|
142
|
+
|
|
143
|
+
const expectedQuads = pull(expectedOperations, operation);
|
|
144
|
+
const actualQuads = pull(actualOperations, operation);
|
|
145
|
+
|
|
146
|
+
if (expectedQuads.length !== actualQuads.length)
|
|
147
|
+
return result(false, `Expected ${expectedQuads.length} ${operation} triples, found ${actualQuads.length}.`);
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
assertExpectedQuadsExist(expectedQuads, actualQuads);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
if (!(error instanceof ExpectedQuadAssertionError)) throw error;
|
|
153
|
+
|
|
154
|
+
return result(
|
|
155
|
+
false,
|
|
156
|
+
`Couldn't find the following ${operation} triple: ${quadToTurtle(error.expectedQuad)}`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const unexpectedOperation = Object.keys(actualOperations)[0] ?? null;
|
|
162
|
+
if (unexpectedOperation) return result(false, `Did not expect to find ${unexpectedOperation} triples.`);
|
|
163
|
+
|
|
164
|
+
return result(true, 'sparql matches');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function turtleEquals(expected: string, actual: string): EqualityResult {
|
|
168
|
+
// TODO catch parsing errors and improve message.
|
|
169
|
+
resetPatterns();
|
|
170
|
+
|
|
171
|
+
const expectedQuads = turtleToQuadsSync(expected, { normalizeBlankNodes: true });
|
|
172
|
+
const actualQuads = turtleToQuadsSync(actual, { normalizeBlankNodes: true });
|
|
173
|
+
const result = (success: boolean, message: string) => ({ success, message, expected, actual });
|
|
174
|
+
|
|
175
|
+
if (expectedQuads.length !== actualQuads.length)
|
|
176
|
+
return result(false, `Expected ${expectedQuads.length} triples, found ${actualQuads.length}.`);
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
assertExpectedQuadsExist(expectedQuads, actualQuads);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (!(error instanceof ExpectedQuadAssertionError)) throw error;
|
|
182
|
+
|
|
183
|
+
return result(false, error.message);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return result(true, 'turtle matches');
|
|
187
|
+
}
|