cozy-iiif 0.1.5 → 0.2.0
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/README.md +189 -9
- package/dist/Cozy.d.ts +1 -0
- package/dist/helpers/import-annotations.d.ts +3 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +93 -0
- package/dist/index.js +167 -164
- package/dist/types.d.ts +2 -1
- package/package.json +4 -2
- package/src/Cozy.ts +103 -102
- package/src/helpers/import-annotations.ts +115 -0
- package/src/helpers/index.ts +1 -0
- package/src/types.ts +3 -1
- package/test/Cozy.test.ts +1 -1
- package/test/annotations/fixtures.ts +135 -0
- package/test/annotations/import-annotations.test.ts +46 -0
- package/vite.config.ts +1 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "cozy-iiif",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.2.0",
|
4
4
|
"description": "A developer-friendly collection of abstractions and utilities built on top of @iiif/presentation-3 and @iiif/parser",
|
5
5
|
"license": "MIT",
|
6
6
|
"author": "Rainer Simon",
|
@@ -24,6 +24,7 @@
|
|
24
24
|
},
|
25
25
|
"exports": {
|
26
26
|
".": "./dist/index.js",
|
27
|
+
"./helpers": "./dist/helpers/index.js",
|
27
28
|
"./level-0": "./dist/level-0/index.js"
|
28
29
|
},
|
29
30
|
"devDependencies": {
|
@@ -34,6 +35,7 @@
|
|
34
35
|
"dependencies": {
|
35
36
|
"@iiif/parser": "^2.1.7",
|
36
37
|
"@iiif/presentation-3": "^2.2.3",
|
37
|
-
"p-throttle": "^7.0.0"
|
38
|
+
"p-throttle": "^7.0.0",
|
39
|
+
"uuid": "^11.1.0"
|
38
40
|
}
|
39
41
|
}
|
package/src/Cozy.ts
CHANGED
@@ -21,123 +21,121 @@ import type {
|
|
21
21
|
ImageServiceResource
|
22
22
|
} from './types';
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
};
|
35
|
-
}
|
24
|
+
const parseURL = async (input: string): Promise<CozyParseResult> => {
|
25
|
+
try {
|
26
|
+
new URL(input);
|
27
|
+
} catch {
|
28
|
+
return {
|
29
|
+
type: 'error',
|
30
|
+
code: 'INVALID_URL',
|
31
|
+
message: 'The provided input is not a valid URL'
|
32
|
+
};
|
33
|
+
}
|
36
34
|
|
37
|
-
|
35
|
+
let response: Response;
|
38
36
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
return {
|
43
|
-
type: 'error',
|
44
|
-
code: 'INVALID_HTTP_RESPONSE',
|
45
|
-
message: `Server responded: HTTP ${response.status} ${response.statusText ? `(${response.statusText})` : ''}`
|
46
|
-
}
|
47
|
-
}
|
48
|
-
} catch (error) {
|
37
|
+
try {
|
38
|
+
response = await fetch(input);
|
39
|
+
if (!response.ok) {
|
49
40
|
return {
|
50
41
|
type: 'error',
|
51
|
-
code: '
|
52
|
-
message:
|
53
|
-
}
|
42
|
+
code: 'INVALID_HTTP_RESPONSE',
|
43
|
+
message: `Server responded: HTTP ${response.status} ${response.statusText ? `(${response.statusText})` : ''}`
|
44
|
+
}
|
54
45
|
}
|
46
|
+
} catch (error) {
|
47
|
+
return {
|
48
|
+
type: 'error',
|
49
|
+
code: 'FETCH_ERROR',
|
50
|
+
message: error instanceof Error ? error.message : 'Failed to fetch resource'
|
51
|
+
};
|
52
|
+
}
|
55
53
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
54
|
+
const contentType = response.headers.get('content-type');
|
55
|
+
|
56
|
+
if (contentType?.startsWith('image/')) {
|
57
|
+
return {
|
58
|
+
type: 'plain-image',
|
59
|
+
url: input
|
60
|
+
};
|
61
|
+
}
|
64
62
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
63
|
+
if (contentType?.includes('text/html')) {
|
64
|
+
return {
|
65
|
+
type: 'webpage',
|
66
|
+
url: input
|
67
|
+
};
|
68
|
+
}
|
71
69
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
message: 'Missing @context'
|
84
|
-
}
|
85
|
-
};
|
86
|
-
|
87
|
-
const id = getPropertyValue<string>(json, 'id');
|
88
|
-
|
89
|
-
if (!id) {
|
90
|
-
return {
|
91
|
-
type: 'error',
|
92
|
-
code: 'INVALID_MANIFEST',
|
93
|
-
message: 'Missing id property'
|
94
|
-
}
|
95
|
-
}
|
70
|
+
try {
|
71
|
+
const json = await response.json();
|
72
|
+
return parse(json, input);
|
73
|
+
} catch {
|
74
|
+
return {
|
75
|
+
type: 'error',
|
76
|
+
code: 'UNSUPPORTED_FORMAT',
|
77
|
+
message: 'Could not parse resource'
|
78
|
+
};
|
79
|
+
}
|
80
|
+
}
|
96
81
|
|
97
|
-
|
98
|
-
|
82
|
+
const parse = (json: any, url?: string): CozyParseResult => {
|
83
|
+
const context = Array.isArray(json['@context'])
|
84
|
+
? json['@context'].find(str => str.includes('iiif.io/api/'))
|
85
|
+
: json['@context'];
|
99
86
|
|
100
|
-
|
87
|
+
if (!context) {
|
88
|
+
return {
|
89
|
+
type: 'error',
|
90
|
+
code: 'INVALID_MANIFEST',
|
91
|
+
message: 'Missing @context'
|
92
|
+
}
|
93
|
+
};
|
101
94
|
|
102
|
-
|
103
|
-
type: 'collection',
|
104
|
-
url: input,
|
105
|
-
resource: parseCollectionResource(json, majorVersion)
|
106
|
-
} : {
|
107
|
-
type: 'manifest',
|
108
|
-
url: input,
|
109
|
-
resource: parseManifestResource(json, majorVersion)
|
110
|
-
};
|
111
|
-
}
|
112
|
-
|
113
|
-
if (context.includes('image/2') || context.includes('image/3')) {
|
114
|
-
const resource = parseImageResource(json);
|
115
|
-
return resource ? {
|
116
|
-
type: 'iiif-image',
|
117
|
-
url: input,
|
118
|
-
resource
|
119
|
-
} : {
|
120
|
-
type: 'error',
|
121
|
-
code: 'INVALID_MANIFEST',
|
122
|
-
message: 'Invalid image service definition'
|
123
|
-
}
|
124
|
-
}
|
95
|
+
const id = getPropertyValue<string>(json, 'id');
|
125
96
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
} catch {
|
132
|
-
return {
|
133
|
-
type: 'error',
|
134
|
-
code: 'UNSUPPORTED_FORMAT',
|
135
|
-
message: 'Could not parse resource'
|
136
|
-
};
|
97
|
+
if (!id) {
|
98
|
+
return {
|
99
|
+
type: 'error',
|
100
|
+
code: 'INVALID_MANIFEST',
|
101
|
+
message: 'Missing id property'
|
137
102
|
}
|
103
|
+
}
|
104
|
+
|
105
|
+
if (context.includes('presentation/2') || context.includes('presentation/3')) {
|
106
|
+
const majorVersion = context.includes('presentation/2') ? 2 : 3;
|
107
|
+
|
108
|
+
const type = getPropertyValue(json, 'type');
|
138
109
|
|
110
|
+
return type.includes('Collection') ? {
|
111
|
+
type: 'collection',
|
112
|
+
url: url || id,
|
113
|
+
resource: parseCollectionResource(json, majorVersion)
|
114
|
+
} : {
|
115
|
+
type: 'manifest',
|
116
|
+
url: url || id,
|
117
|
+
resource: parseManifestResource(json, majorVersion)
|
118
|
+
};
|
119
|
+
}
|
120
|
+
|
121
|
+
if (context.includes('image/2') || context.includes('image/3')) {
|
122
|
+
const resource = parseImageResource(json);
|
123
|
+
return resource ? {
|
124
|
+
type: 'iiif-image',
|
125
|
+
url: url || id,
|
126
|
+
resource
|
127
|
+
} : {
|
128
|
+
type: 'error',
|
129
|
+
code: 'INVALID_MANIFEST',
|
130
|
+
message: 'Invalid image service definition'
|
131
|
+
}
|
139
132
|
}
|
140
133
|
|
134
|
+
return {
|
135
|
+
type: 'error',
|
136
|
+
code: 'INVALID_MANIFEST',
|
137
|
+
message: 'JSON resource is not a recognized IIIF format'
|
138
|
+
};
|
141
139
|
}
|
142
140
|
|
143
141
|
const parseCollectionResource = (resource: any, majorVersion: number): CozyCollection => {
|
@@ -194,6 +192,7 @@ const parseManifestResource = (resource: any, majorVersion: number): CozyManifes
|
|
194
192
|
width: c.width,
|
195
193
|
height: c.height,
|
196
194
|
images,
|
195
|
+
annotations: (c.annotations || []),
|
197
196
|
getLabel: getLabel(c),
|
198
197
|
getMetadata: getMetadata(c),
|
199
198
|
getThumbnailURL: getThumbnailURL(c, images)
|
@@ -259,4 +258,6 @@ const parseImageResource = (resource: any) => {
|
|
259
258
|
serviceUrl: normalizeServiceUrl(getPropertyValue<string>(resource, 'id'))
|
260
259
|
} as ImageServiceResource;
|
261
260
|
}
|
262
|
-
}
|
261
|
+
}
|
262
|
+
|
263
|
+
export const Cozy = { parse, parseURL };
|
@@ -0,0 +1,115 @@
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
2
|
+
import type { Annotation } from '@iiif/presentation-3';
|
3
|
+
import type { CozyCanvas, CozyManifest } from '../types';
|
4
|
+
|
5
|
+
// Helper to escape special characters in strings used in RegExp
|
6
|
+
const escapeRegExp = (str: string) =>
|
7
|
+
str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
8
|
+
|
9
|
+
const getAnnotationPageId = (canvas: CozyCanvas, namespace?: string) => {
|
10
|
+
if (namespace) {
|
11
|
+
// Use naming convention `{canvas.id}/namespace/page/p{idx}`. This means we need
|
12
|
+
// to find the highest index currently in use. (Index starts with 1.)
|
13
|
+
const pages = canvas.annotations;
|
14
|
+
if (pages.length > 0) {
|
15
|
+
const pattern = new RegExp(`${escapeRegExp(canvas.id)}/${escapeRegExp(namespace)}/page/p(\\d+)$`);
|
16
|
+
|
17
|
+
const highestIdx = pages.reduce<number>((highest, page) => {
|
18
|
+
const match = page.id.match(pattern);
|
19
|
+
if (match && match[1]) {
|
20
|
+
const thisIndex = parseInt(match[1]);
|
21
|
+
return Math.max(highest, thisIndex);
|
22
|
+
} else {
|
23
|
+
return highest;
|
24
|
+
}
|
25
|
+
}, 1);
|
26
|
+
|
27
|
+
return `${canvas.id}/${namespace}/annotations/page/p${highestIdx}`;
|
28
|
+
} else {
|
29
|
+
return `${canvas.id}/${namespace}/annotations/page/p1`;
|
30
|
+
}
|
31
|
+
} else {
|
32
|
+
// Use UUIDs as a fallback naming convention
|
33
|
+
return `${canvas.id}/annotations/page/${uuidv4()}`;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Will blindy attach the annotations to this canvas.
|
39
|
+
*/
|
40
|
+
const importAnnotationsToCanvas = (canvas: CozyCanvas, annotations: Annotation[], namespace?: string) => {
|
41
|
+
const page = {
|
42
|
+
id: getAnnotationPageId(canvas, namespace),
|
43
|
+
type: 'AnnotationPage',
|
44
|
+
items: annotations
|
45
|
+
}
|
46
|
+
|
47
|
+
return {
|
48
|
+
source: {
|
49
|
+
...canvas.source,
|
50
|
+
annotations: [...canvas.annotations, page]
|
51
|
+
},
|
52
|
+
id: canvas.id,
|
53
|
+
width: canvas.width,
|
54
|
+
height: canvas.height,
|
55
|
+
images: [...canvas.images],
|
56
|
+
annotations: [...canvas.annotations, page],
|
57
|
+
getLabel: canvas.getLabel,
|
58
|
+
getMetadata: canvas.getMetadata,
|
59
|
+
getThumbnailURL: canvas.getThumbnailURL
|
60
|
+
} as CozyCanvas;
|
61
|
+
}
|
62
|
+
|
63
|
+
/**
|
64
|
+
* Will use 'source' information from the annotation targets to associate annotations with the right
|
65
|
+
* canvases.
|
66
|
+
*/
|
67
|
+
const importAnnotationsToManifest = (manifest: CozyManifest, annotations: Annotation[], namespace?: string) => {
|
68
|
+
const getSource = (annotation: Annotation) => {
|
69
|
+
const target = annotation.target;
|
70
|
+
if (!target) return;
|
71
|
+
|
72
|
+
if (typeof target === 'string')
|
73
|
+
return target.substring(0, target.indexOf('#'));
|
74
|
+
else
|
75
|
+
return (target as any).source;
|
76
|
+
}
|
77
|
+
|
78
|
+
const bySource = annotations.reduce<Record<string, Annotation[]>>((acc, annotation) => {
|
79
|
+
const source = getSource(annotation);
|
80
|
+
if (!source) return acc;
|
81
|
+
|
82
|
+
if (!acc[source]) acc[source] = [];
|
83
|
+
acc[source].push(annotation);
|
84
|
+
|
85
|
+
return acc;
|
86
|
+
}, {});
|
87
|
+
|
88
|
+
const canvases = manifest.canvases.map(canvas => {
|
89
|
+
const toImport = bySource[canvas.id] || [];
|
90
|
+
return toImport.length > 0 ? importAnnotationsToCanvas(canvas, toImport, namespace) : canvas;
|
91
|
+
});
|
92
|
+
|
93
|
+
return {
|
94
|
+
source: {
|
95
|
+
...manifest.source,
|
96
|
+
items: canvases.map(c => c.source)
|
97
|
+
},
|
98
|
+
id: manifest.id,
|
99
|
+
majorVersion: manifest.majorVersion,
|
100
|
+
canvases,
|
101
|
+
structure: manifest.structure,
|
102
|
+
getLabel: manifest.getLabel,
|
103
|
+
getMetadata: manifest.getMetadata,
|
104
|
+
getTableOfContents: manifest.getTableOfContents
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
export const importAnnotations = <T extends CozyManifest | CozyCanvas>(
|
109
|
+
resource: T,
|
110
|
+
annotations: Annotation[],
|
111
|
+
namespace?: string
|
112
|
+
): T extends CozyCanvas ? CozyCanvas : CozyManifest =>
|
113
|
+
resource.source.type === 'Canvas'
|
114
|
+
? importAnnotationsToCanvas(resource as CozyCanvas, annotations, namespace) as T extends CozyCanvas ? CozyCanvas : CozyManifest
|
115
|
+
: importAnnotationsToManifest(resource as CozyManifest, annotations, namespace) as T extends CozyCanvas ? CozyCanvas : CozyManifest;
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './import-annotations';
|
package/src/types.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import type { Manifest, Canvas, ImageService2, ImageService3, IIIFExternalWebResource, Collection, Range } from '@iiif/presentation-3';
|
1
|
+
import type { Manifest, Canvas, ImageService2, ImageService3, IIIFExternalWebResource, Collection, Range, Annotation, AnnotationPage } from '@iiif/presentation-3';
|
2
2
|
|
3
3
|
export type CozyParseResult =
|
4
4
|
| { type: 'collection', url: string, resource: CozyCollection }
|
@@ -88,6 +88,8 @@ export interface CozyCanvas {
|
|
88
88
|
|
89
89
|
readonly images: CozyImageResource[];
|
90
90
|
|
91
|
+
readonly annotations: AnnotationPage[];
|
92
|
+
|
91
93
|
getLabel(locale?: string): string;
|
92
94
|
|
93
95
|
getMetadata(locale?: string): CozyMetadata[];
|
package/test/Cozy.test.ts
CHANGED
@@ -0,0 +1,135 @@
|
|
1
|
+
// Modified from https://iiif.io/api/cookbook/recipe/0021-tagging/
|
2
|
+
export const ANNOTATIONS = [{
|
3
|
+
id: 'https://iiif.io/api/cookbook/recipe/0021-tagging/annotation/p0001',
|
4
|
+
type: 'Annotation',
|
5
|
+
motivation: 'tagging',
|
6
|
+
body: {
|
7
|
+
type: 'TextualBody',
|
8
|
+
value: 'Test Annotation 1',
|
9
|
+
format: 'text/plain'
|
10
|
+
},
|
11
|
+
target: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/canvas/p1#xywh=265,661,1260,1239'
|
12
|
+
},{
|
13
|
+
id: 'https://iiif.io/api/cookbook/recipe/0021-tagging/annotation/p0002',
|
14
|
+
type: 'Annotation',
|
15
|
+
motivation: 'tagging',
|
16
|
+
body: {
|
17
|
+
type: 'TextualBody',
|
18
|
+
value: 'Test Annotation 2',
|
19
|
+
format: 'text/plain'
|
20
|
+
},
|
21
|
+
target: {
|
22
|
+
source: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/canvas/p2',
|
23
|
+
selector: {
|
24
|
+
type: 'FragmentSelector',
|
25
|
+
value: 'xywh=265,661,1260,1239'
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}]
|
29
|
+
|
30
|
+
// https://iiif.io/api/cookbook/recipe/0001-mvm-image/
|
31
|
+
export const SINGLE_CANVAS_NO_ANNOTATIONS = {
|
32
|
+
'@context': 'http://iiif.io/api/presentation/3/context.json',
|
33
|
+
id: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/manifest.json',
|
34
|
+
type: 'Manifest',
|
35
|
+
label: {
|
36
|
+
en: [
|
37
|
+
'Single Image Example'
|
38
|
+
]
|
39
|
+
},
|
40
|
+
items: [
|
41
|
+
{
|
42
|
+
id: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/canvas/p1',
|
43
|
+
type: 'Canvas',
|
44
|
+
height: 1800,
|
45
|
+
width: 1200,
|
46
|
+
items: [
|
47
|
+
{
|
48
|
+
id: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/page/p1/1',
|
49
|
+
type: 'AnnotationPage',
|
50
|
+
items: [
|
51
|
+
{
|
52
|
+
id: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/annotation/p0001-image',
|
53
|
+
type: 'Annotation',
|
54
|
+
motivation: 'painting',
|
55
|
+
body: {
|
56
|
+
id: 'http://iiif.io/api/presentation/2.1/example/fixtures/resources/page1-full.png',
|
57
|
+
type: 'Image',
|
58
|
+
format: 'image/png',
|
59
|
+
height: 1800,
|
60
|
+
width: 1200
|
61
|
+
},
|
62
|
+
target: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/canvas/p1'
|
63
|
+
}
|
64
|
+
]
|
65
|
+
}
|
66
|
+
]
|
67
|
+
}
|
68
|
+
]
|
69
|
+
}
|
70
|
+
|
71
|
+
export const TWO_CANVASES_NO_ANNOTATIONS = {
|
72
|
+
'@context': 'http://iiif.io/api/presentation/3/context.json',
|
73
|
+
id: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/manifest.json',
|
74
|
+
type: 'Manifest',
|
75
|
+
label: {
|
76
|
+
en: [
|
77
|
+
'Single Image Example'
|
78
|
+
]
|
79
|
+
},
|
80
|
+
items: [
|
81
|
+
{
|
82
|
+
id: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/canvas/p1',
|
83
|
+
type: 'Canvas',
|
84
|
+
height: 1800,
|
85
|
+
width: 1200,
|
86
|
+
items: [
|
87
|
+
{
|
88
|
+
id: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/page/p1/1',
|
89
|
+
type: 'AnnotationPage',
|
90
|
+
items: [
|
91
|
+
{
|
92
|
+
id: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/annotation/p0001-image',
|
93
|
+
type: 'Annotation',
|
94
|
+
motivation: 'painting',
|
95
|
+
body: {
|
96
|
+
id: 'http://iiif.io/api/presentation/2.1/example/fixtures/resources/page1-full.png',
|
97
|
+
type: 'Image',
|
98
|
+
format: 'image/png',
|
99
|
+
height: 1800,
|
100
|
+
width: 1200
|
101
|
+
},
|
102
|
+
target: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/canvas/p1'
|
103
|
+
}
|
104
|
+
]
|
105
|
+
}
|
106
|
+
]
|
107
|
+
}, {
|
108
|
+
id: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/canvas/p2',
|
109
|
+
type: 'Canvas',
|
110
|
+
height: 1800,
|
111
|
+
width: 1200,
|
112
|
+
items: [
|
113
|
+
{
|
114
|
+
id: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/page/p1/1',
|
115
|
+
type: 'AnnotationPage',
|
116
|
+
items: [
|
117
|
+
{
|
118
|
+
id: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/annotation/p0001-image',
|
119
|
+
type: 'Annotation',
|
120
|
+
motivation: 'painting',
|
121
|
+
body: {
|
122
|
+
id: 'http://iiif.io/api/presentation/2.1/example/fixtures/resources/page1-full.png',
|
123
|
+
type: 'Image',
|
124
|
+
format: 'image/png',
|
125
|
+
height: 1800,
|
126
|
+
width: 1200
|
127
|
+
},
|
128
|
+
target: 'https://iiif.io/api/cookbook/recipe/0001-mvm-image/canvas/p1'
|
129
|
+
}
|
130
|
+
]
|
131
|
+
}
|
132
|
+
]
|
133
|
+
}
|
134
|
+
]
|
135
|
+
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
2
|
+
import { Annotation } from '@iiif/presentation-3';
|
3
|
+
import { Cozy, CozyManifest } from '../../src';
|
4
|
+
|
5
|
+
import {
|
6
|
+
ANNOTATIONS,
|
7
|
+
SINGLE_CANVAS_NO_ANNOTATIONS,
|
8
|
+
TWO_CANVASES_NO_ANNOTATIONS
|
9
|
+
} from './fixtures';
|
10
|
+
|
11
|
+
describe('import-annotations', () => {
|
12
|
+
|
13
|
+
it('should insert a new page into a canvas with no annotatinos', () => {
|
14
|
+
const result = Cozy.parse(SINGLE_CANVAS_NO_ANNOTATIONS);
|
15
|
+
|
16
|
+
expect(result.type).toBe('manifest');
|
17
|
+
const manifest = (result as any).resource as CozyManifest;
|
18
|
+
|
19
|
+
expect(manifest.canvases.length).toBe(1);
|
20
|
+
const firstCanvas = manifest.canvases[0];
|
21
|
+
|
22
|
+
const annotations = ANNOTATIONS as Annotation[];
|
23
|
+
|
24
|
+
const modified = Cozy.Helpers.importAnnotations(firstCanvas, annotations)
|
25
|
+
expect(modified.annotations.length).toBe(1);
|
26
|
+
});
|
27
|
+
|
28
|
+
it('should correctly insert annotation pages into the test manifest', () => {
|
29
|
+
const result = Cozy.parse(TWO_CANVASES_NO_ANNOTATIONS);
|
30
|
+
|
31
|
+
expect(result.type).toBe('manifest');
|
32
|
+
const manifest = (result as any).resource as CozyManifest;
|
33
|
+
|
34
|
+
expect(manifest.canvases.length).toBe(2);
|
35
|
+
expect(manifest.canvases[0].annotations.length).toBe(0);
|
36
|
+
expect(manifest.canvases[1].annotations.length).toBe(0);
|
37
|
+
|
38
|
+
const annotations = ANNOTATIONS as Annotation[];
|
39
|
+
|
40
|
+
const modified = Cozy.Helpers.importAnnotations(manifest, annotations, 'cozy');
|
41
|
+
expect(modified.canvases[0].annotations.length).toBe(1);
|
42
|
+
expect(modified.canvases[1].annotations.length).toBe(1);
|
43
|
+
});
|
44
|
+
|
45
|
+
});
|
46
|
+
|
package/vite.config.ts
CHANGED