iiif-presentation 1.0.0 → 1.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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +35 -0
- data/.gitignore +1 -0
- data/Gemfile +2 -0
- data/README.md +22 -2
- data/VERSION +1 -1
- data/iiif-presentation.gemspec +1 -1
- data/lib/iiif/hash_behaviours.rb +1 -1
- data/lib/iiif/presentation/canvas.rb +4 -0
- data/lib/iiif/presentation/service.rb +12 -0
- data/lib/iiif/presentation.rb +5 -4
- data/lib/iiif/service.rb +40 -105
- data/lib/iiif/v3/abstract_resource.rb +491 -0
- data/lib/iiif/v3/presentation/annotation.rb +74 -0
- data/lib/iiif/v3/presentation/annotation_collection.rb +38 -0
- data/lib/iiif/v3/presentation/annotation_page.rb +53 -0
- data/lib/iiif/v3/presentation/canvas.rb +82 -0
- data/lib/iiif/v3/presentation/choice.rb +51 -0
- data/lib/iiif/v3/presentation/collection.rb +52 -0
- data/lib/iiif/v3/presentation/image_resource.rb +110 -0
- data/lib/iiif/v3/presentation/manifest.rb +82 -0
- data/lib/iiif/v3/presentation/range.rb +39 -0
- data/lib/iiif/v3/presentation/resource.rb +30 -0
- data/lib/iiif/v3/presentation/sequence.rb +66 -0
- data/lib/iiif/v3/presentation/service.rb +51 -0
- data/lib/iiif/v3/presentation.rb +36 -0
- data/spec/fixtures/v3/manifests/complete_from_spec.json +195 -0
- data/spec/fixtures/v3/manifests/minimal.json +49 -0
- data/spec/fixtures/v3/manifests/service_only.json +14 -0
- data/spec/fixtures/vcr_cassettes/pul_loris_cassette.json +1 -1
- data/spec/fixtures/vcr_cassettes/pul_loris_cassette_v3.json +1 -0
- data/spec/integration/iiif/presentation/image_resource_spec.rb +0 -1
- data/spec/integration/iiif/service_spec.rb +17 -32
- data/spec/integration/iiif/v3/abstract_resource_spec.rb +202 -0
- data/spec/integration/iiif/v3/presentation/image_resource_spec.rb +118 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/unit/iiif/presentation/canvas_spec.rb +0 -1
- data/spec/unit/iiif/presentation/manifest_spec.rb +1 -1
- data/spec/unit/iiif/v3/abstract_resource_define_methods_for_spec.rb +78 -0
- data/spec/unit/iiif/v3/abstract_resource_spec.rb +293 -0
- data/spec/unit/iiif/v3/presentation/annotation_collection_spec.rb +36 -0
- data/spec/unit/iiif/v3/presentation/annotation_page_spec.rb +131 -0
- data/spec/unit/iiif/v3/presentation/annotation_spec.rb +389 -0
- data/spec/unit/iiif/v3/presentation/canvas_spec.rb +337 -0
- data/spec/unit/iiif/v3/presentation/choice_spec.rb +120 -0
- data/spec/unit/iiif/v3/presentation/collection_spec.rb +55 -0
- data/spec/unit/iiif/v3/presentation/image_resource_spec.rb +189 -0
- data/spec/unit/iiif/v3/presentation/manifest_spec.rb +370 -0
- data/spec/unit/iiif/v3/presentation/range_spec.rb +54 -0
- data/spec/unit/iiif/v3/presentation/resource_spec.rb +174 -0
- data/spec/unit/iiif/v3/presentation/sequence_spec.rb +222 -0
- data/spec/unit/iiif/v3/presentation/service_spec.rb +220 -0
- data/spec/unit/iiif/v3/presentation/shared_examples/abstract_resource_only_keys.rb +41 -0
- data/spec/unit/iiif/v3/presentation/shared_examples/any_type_keys.rb +31 -0
- data/spec/unit/iiif/v3/presentation/shared_examples/array_only_keys.rb +40 -0
- data/spec/unit/iiif/v3/presentation/shared_examples/hash_only_keys.rb +40 -0
- data/spec/unit/iiif/v3/presentation/shared_examples/int_only_keys.rb +45 -0
- data/spec/unit/iiif/v3/presentation/shared_examples/numeric_only_keys.rb +45 -0
- data/spec/unit/iiif/v3/presentation/shared_examples/string_only_keys.rb +26 -0
- data/spec/unit/iiif/v3/presentation/shared_examples/uri_only_keys.rb +31 -0
- metadata +82 -11
- data/.travis.yml +0 -11
@@ -0,0 +1,337 @@
|
|
1
|
+
describe IIIF::V3::Presentation::Canvas do
|
2
|
+
|
3
|
+
describe '#required_keys' do
|
4
|
+
%w{ type id label }.each do |k|
|
5
|
+
it k do
|
6
|
+
expect(subject.required_keys).to include(k)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#prohibited_keys' do
|
12
|
+
it 'contains the expected key names' do
|
13
|
+
keys = described_class::PAGING_PROPERTIES +
|
14
|
+
%w{
|
15
|
+
viewing_direction
|
16
|
+
format
|
17
|
+
nav_date
|
18
|
+
start_canvas
|
19
|
+
content_annotations
|
20
|
+
}
|
21
|
+
expect(subject.prohibited_keys).to include(*keys)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#int_only_keys' do
|
26
|
+
it 'depth (for 3d objects)' do
|
27
|
+
expect(subject.int_only_keys).to include('depth')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#array_only_keys' do
|
32
|
+
it 'content' do
|
33
|
+
expect(subject.array_only_keys).to include('content')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#legal_viewing_hint_values' do
|
38
|
+
it 'contains the expected values' do
|
39
|
+
expect(subject.legal_viewing_hint_values).to contain_exactly('paged', 'continuous', 'non-paged', 'facing-pages', 'auto-advance')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#initialize' do
|
44
|
+
it 'sets type to Canvas by default' do
|
45
|
+
expect(subject['type']).to eq 'Canvas'
|
46
|
+
end
|
47
|
+
it 'allows subclasses to override type' do
|
48
|
+
subclass = Class.new(described_class) do
|
49
|
+
def initialize(hsh={})
|
50
|
+
hsh = { 'type' => 'a:SubClass' }
|
51
|
+
super(hsh)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
sub = subclass.new
|
55
|
+
expect(sub['type']).to eq 'a:SubClass'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#validate' do
|
60
|
+
let(:canvas_id) { 'http://example.org/iiif/book1/canvas/c1' }
|
61
|
+
let(:exp_id_err_msg) { "id must be an http(s) URI without a fragment for #{described_class}" }
|
62
|
+
before(:each) do
|
63
|
+
subject['id'] = canvas_id
|
64
|
+
subject['label'] = 'foo'
|
65
|
+
end
|
66
|
+
it 'raises IllegalValueError if id is not URI' do
|
67
|
+
subject['id'] = 'foo'
|
68
|
+
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_id_err_msg)
|
69
|
+
end
|
70
|
+
it 'raises IllegalValueError if id is not http(s)' do
|
71
|
+
subject['id'] = 'ftp://www.example.org'
|
72
|
+
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_id_err_msg)
|
73
|
+
end
|
74
|
+
it 'raises IllegalValueError if id has a fragment' do
|
75
|
+
subject['id'] = 'http://www.example.org#foo'
|
76
|
+
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_id_err_msg)
|
77
|
+
end
|
78
|
+
|
79
|
+
# let(:exp_extent_err_msg) { "#{described_class} must have (a height and a width) and/or a duration" }
|
80
|
+
# (see sul-dlss/purl/issues/169)
|
81
|
+
let(:exp_extent_err_msg) { "#{described_class} requires both height and width or neither" }
|
82
|
+
it 'raises IllegalValueError if height is a string' do
|
83
|
+
subject['height'] = 'foo'
|
84
|
+
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_extent_err_msg)
|
85
|
+
end
|
86
|
+
it 'raises IllegalValueError if height but no width' do
|
87
|
+
subject['height'] = 666
|
88
|
+
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_extent_err_msg)
|
89
|
+
end
|
90
|
+
it 'raises IllegalValueError if width but no height' do
|
91
|
+
subject['width'] = 666
|
92
|
+
subject['duration'] = 66.6
|
93
|
+
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_extent_err_msg)
|
94
|
+
end
|
95
|
+
it 'raises IllegalValueError if no width, height or duration' do
|
96
|
+
# (see sul-dlss/purl/issues/169)
|
97
|
+
skip('while this is in the current v3 spec, it does not make sense for some content (e.g. txt files)')
|
98
|
+
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_extent_err_msg)
|
99
|
+
end
|
100
|
+
it 'allows width, height and duration' do
|
101
|
+
subject['width'] = 666
|
102
|
+
subject['height'] = 666
|
103
|
+
subject['duration'] = 66.6
|
104
|
+
expect { subject.validate }.not_to raise_error
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'raises IllegalValueError for content entry that is not an AnnotationPage' do
|
108
|
+
subject['content'] = [IIIF::V3::Presentation::AnnotationPage.new, IIIF::V3::Presentation::Annotation.new]
|
109
|
+
exp_err_msg = "All entries in the content list must be a IIIF::V3::Presentation::AnnotationPage"
|
110
|
+
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_err_msg)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'IllegalValueError for content with annotation target not the canvas id' do
|
114
|
+
anno = IIIF::V3::Presentation::Annotation.new(
|
115
|
+
'id' => 'http://example.com/anno/666',
|
116
|
+
'target' => canvas_id)
|
117
|
+
anno_page = IIIF::V3::Presentation::AnnotationPage.new(
|
118
|
+
'id' => "http://example.org/iiif/book1/page/p1/1",
|
119
|
+
'items' => [anno])
|
120
|
+
subject['content'] = [anno_page]
|
121
|
+
|
122
|
+
expect { subject.validate }.not_to raise_error
|
123
|
+
|
124
|
+
anno['target'] = 'http://example.com/canvas/abc'
|
125
|
+
exp_err_msg = 'URI of the canvas must be repeated in the target field of included Annotations'
|
126
|
+
expect { subject.validate }.to raise_error(IIIF::V3::Presentation::IllegalValueError, exp_err_msg)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'realistic examples' do
|
131
|
+
let(:canvas_id) { 'http://example.org/iiif/book1/canvas/c1' }
|
132
|
+
let(:minimal_canvas_object) { described_class.new({
|
133
|
+
"id" => canvas_id,
|
134
|
+
'label' => {"en" => ["so minimal it's not here"]},
|
135
|
+
'height' => 1000,
|
136
|
+
'width' => 1000
|
137
|
+
})}
|
138
|
+
let(:anno_page) { IIIF::V3::Presentation::AnnotationPage.new(
|
139
|
+
"id" => "http://example.org/iiif/book1/page/p1/1",
|
140
|
+
'items' => []
|
141
|
+
)}
|
142
|
+
describe 'minimal canvas' do
|
143
|
+
it 'validates' do
|
144
|
+
expect{minimal_canvas_object.validate}.not_to raise_error
|
145
|
+
end
|
146
|
+
it 'has expected required values' do
|
147
|
+
expect(minimal_canvas_object.type).to eq described_class::TYPE
|
148
|
+
expect(minimal_canvas_object.id).to eq canvas_id
|
149
|
+
expect(minimal_canvas_object.label['en']).to include "so minimal it's not here"
|
150
|
+
expect(minimal_canvas_object.height).to eq 1000
|
151
|
+
expect(minimal_canvas_object.width).to eq 1000
|
152
|
+
end
|
153
|
+
end
|
154
|
+
describe 'minimal with empty content' do
|
155
|
+
let(:canvas_object) {
|
156
|
+
minimal_canvas_object['content'] = []
|
157
|
+
minimal_canvas_object
|
158
|
+
}
|
159
|
+
it 'validates' do
|
160
|
+
expect{canvas_object.validate}.not_to raise_error
|
161
|
+
end
|
162
|
+
it 'has empty array for content' do
|
163
|
+
expect(canvas_object.content).to eq []
|
164
|
+
end
|
165
|
+
end
|
166
|
+
describe 'minimal with content' do
|
167
|
+
let(:canvas_object) {
|
168
|
+
minimal_canvas_object['content'] = [anno_page, anno_page]
|
169
|
+
minimal_canvas_object
|
170
|
+
}
|
171
|
+
it 'validates' do
|
172
|
+
expect{canvas_object.validate}.not_to raise_error
|
173
|
+
end
|
174
|
+
it 'has content value' do
|
175
|
+
expect(canvas_object.content.size).to eq 2
|
176
|
+
expect(canvas_object.content).to eq [anno_page, anno_page]
|
177
|
+
end
|
178
|
+
|
179
|
+
describe 'stanford (purl code)' do
|
180
|
+
let(:canvas_object) {
|
181
|
+
c = described_class.new
|
182
|
+
c['id'] = canvas_id
|
183
|
+
c.label = {'en' => ['label']}
|
184
|
+
c.content << anno_page
|
185
|
+
c
|
186
|
+
}
|
187
|
+
describe 'non-image' do
|
188
|
+
it 'validates' do
|
189
|
+
expect{canvas_object.validate}.not_to raise_error
|
190
|
+
end
|
191
|
+
it 'has expected required values' do
|
192
|
+
expect(canvas_object.type).to eq described_class::TYPE
|
193
|
+
expect(canvas_object.id).to eq canvas_id
|
194
|
+
expect(canvas_object.label['en']).to include "label"
|
195
|
+
end
|
196
|
+
it 'has expected additional values' do
|
197
|
+
expect(canvas_object.content).to eq [anno_page]
|
198
|
+
end
|
199
|
+
end
|
200
|
+
describe 'image' do
|
201
|
+
let(:img_canvas) {
|
202
|
+
canvas_object.height = 666
|
203
|
+
canvas_object.width = 888
|
204
|
+
canvas_object
|
205
|
+
}
|
206
|
+
it 'validates' do
|
207
|
+
expect{img_canvas.validate}.not_to raise_error
|
208
|
+
end
|
209
|
+
it 'has expected required values' do
|
210
|
+
expect(img_canvas.type).to eq described_class::TYPE
|
211
|
+
expect(img_canvas.id).to eq canvas_id
|
212
|
+
expect(img_canvas.label['en']).to include "label"
|
213
|
+
expect(img_canvas.height).to eq 666
|
214
|
+
expect(img_canvas.width).to eq 888
|
215
|
+
end
|
216
|
+
it 'has expected additional values' do
|
217
|
+
expect(img_canvas.content).to eq [anno_page]
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
describe 'file object' do
|
223
|
+
describe 'without extent info' do
|
224
|
+
let(:file_object) { described_class.new({
|
225
|
+
"id" => "https://example.org/bd742gh0511/iiif3/canvas/bd742gh0511_1",
|
226
|
+
"label" => {"en" => ["File 1"]},
|
227
|
+
"content" => [anno_page]
|
228
|
+
})}
|
229
|
+
it 'validates' do
|
230
|
+
expect{file_object.validate}.not_to raise_error
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
describe 'image object' do
|
236
|
+
describe 'without extent info' do
|
237
|
+
let(:image_object) { described_class.new({
|
238
|
+
"id" => "https://example.org/yv090xk3108/iiif3/canvas/yv090xk3108_1",
|
239
|
+
"label" => {"en" => ["image"]},
|
240
|
+
"content" => [anno_page]
|
241
|
+
})}
|
242
|
+
it 'validates' do
|
243
|
+
expect{image_object.validate}.not_to raise_error
|
244
|
+
end
|
245
|
+
end
|
246
|
+
describe 'with extent given' do
|
247
|
+
let(:image_object) { described_class.new({
|
248
|
+
"id" => "https://example.org/yy816tv6021/iiif3/canvas/yy816tv6021_3",
|
249
|
+
"label" => {"en" => ["Image of media (1 of 2)"]},
|
250
|
+
"height" => 3456,
|
251
|
+
"width" => 5184,
|
252
|
+
"content" => [anno_page]
|
253
|
+
})}
|
254
|
+
it 'validates' do
|
255
|
+
expect{image_object.validate}.not_to raise_error
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe 'audio object' do
|
261
|
+
describe 'without duration' do
|
262
|
+
let(:canvas_for_audio) { described_class.new({
|
263
|
+
"id" => "https://example.org/xk681bt2506/iiif3/canvas/xk681bt2506_1",
|
264
|
+
"label" => {"en" => ["Audio file 1"]},
|
265
|
+
"content" => [anno_page]
|
266
|
+
})}
|
267
|
+
it 'validates' do
|
268
|
+
expect{canvas_for_audio.validate}.not_to raise_error
|
269
|
+
end
|
270
|
+
end
|
271
|
+
describe 'digerati example' do
|
272
|
+
let(:canvas_for_audio) { described_class.new({
|
273
|
+
"id" => "http://tomcrane.github.io/scratch/manifests/3/canvas/2",
|
274
|
+
"label" => "Track 2",
|
275
|
+
'summary' => {
|
276
|
+
'en' => ['foo']
|
277
|
+
},
|
278
|
+
"duration" => 45,
|
279
|
+
"content" => [anno_page]
|
280
|
+
})}
|
281
|
+
it 'validates' do
|
282
|
+
expect{canvas_for_audio.validate}.not_to raise_error
|
283
|
+
end
|
284
|
+
it 'duration' do
|
285
|
+
expect(canvas_for_audio.duration).to eq 45
|
286
|
+
end
|
287
|
+
it 'summary' do
|
288
|
+
expect(canvas_for_audio.summary['en'].first).to eq 'foo'
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
describe '3d object' do
|
294
|
+
let(:canvas_3d_object) { described_class.new({
|
295
|
+
"id" => "http://tomcrane.github.io/scratch/manifests/3/canvas/3d",
|
296
|
+
"thumbnail" => [{'id' => "http://files.universalviewer.io/manifests/nelis/animal-skull/thumb.jpg",
|
297
|
+
'type' => 'Image'}],
|
298
|
+
"width" => 10000,
|
299
|
+
"height" => 10000,
|
300
|
+
"depth" => 10000,
|
301
|
+
"label" => "A stage for an object",
|
302
|
+
"content" => [anno_page]
|
303
|
+
})}
|
304
|
+
it 'validates' do
|
305
|
+
expect{canvas_3d_object.validate}.not_to raise_error
|
306
|
+
end
|
307
|
+
it 'thumbnail' do
|
308
|
+
expect(canvas_3d_object.thumbnail).to eq [{'id' => "http://files.universalviewer.io/manifests/nelis/animal-skull/thumb.jpg", 'type' => 'Image'}]
|
309
|
+
end
|
310
|
+
it 'depth' do
|
311
|
+
expect(canvas_3d_object.depth).to eq 10000
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
describe 'video object' do
|
316
|
+
describe 'with extent info' do
|
317
|
+
let(:canvas_for_video) { described_class.new({
|
318
|
+
"id" => "http://tomcrane.github.io/scratch/manifests/3/canvas/1",
|
319
|
+
"label" => "Associate multiple Video representations as Choice",
|
320
|
+
"height" => 1000,
|
321
|
+
"width" => 1000,
|
322
|
+
"duration" => 100,
|
323
|
+
"content" => [anno_page]
|
324
|
+
}) }
|
325
|
+
it 'validates' do
|
326
|
+
expect{canvas_for_video.validate}.not_to raise_error
|
327
|
+
end
|
328
|
+
it 'height, width, duration' do
|
329
|
+
expect(canvas_for_video.height).to eq 1000
|
330
|
+
expect(canvas_for_video.width).to eq 1000
|
331
|
+
expect(canvas_for_video.duration).to eq 100
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
describe IIIF::V3::Presentation::Choice do
|
2
|
+
|
3
|
+
describe '#prohibited_keys' do
|
4
|
+
it 'contains the expected key names' do
|
5
|
+
keys = described_class::PAGING_PROPERTIES +
|
6
|
+
described_class::CONTENT_RESOURCE_PROPERTIES +
|
7
|
+
%w{
|
8
|
+
nav_date
|
9
|
+
viewing_direction
|
10
|
+
start_canvas
|
11
|
+
content_annotations
|
12
|
+
}
|
13
|
+
expect(subject.prohibited_keys).to include(*keys)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#any_type_keys' do
|
18
|
+
it 'default' do
|
19
|
+
expect(subject.any_type_keys).to include('default')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#string_only_keys' do
|
24
|
+
it 'choice_hint' do
|
25
|
+
expect(subject.string_only_keys).to include('choice_hint')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#array_only_keys' do
|
30
|
+
it 'items' do
|
31
|
+
expect(subject.array_only_keys).to include('items')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#legal_choice_hint_values' do
|
36
|
+
it 'contains the expected values' do
|
37
|
+
expect(subject.legal_choice_hint_values).to contain_exactly('client', 'user')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#legal_viewing_hint_values' do
|
42
|
+
it 'contains none' do
|
43
|
+
expect(subject.legal_viewing_hint_values).to contain_exactly('none')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#initialize' do
|
48
|
+
it 'sets type to Choice by default' do
|
49
|
+
expect(subject['type']).to eq 'Choice'
|
50
|
+
end
|
51
|
+
it 'allows subclasses to override type' do
|
52
|
+
subclass = Class.new(described_class) do
|
53
|
+
def initialize(hsh={})
|
54
|
+
hsh = { 'type' => 'a:SubClass' }
|
55
|
+
super(hsh)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
sub = subclass.new
|
59
|
+
expect(sub['type']).to eq 'a:SubClass'
|
60
|
+
end
|
61
|
+
it 'allows type to be passed in' do
|
62
|
+
my_choice = described_class.new('type' => 'bar')
|
63
|
+
expect(my_choice.type).to eq 'bar'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#validate' do
|
68
|
+
it 'raises an IllegalValueError if choice_hint isn\'t an allowable value' do
|
69
|
+
exp_err_msg = "choiceHint for #{described_class} must be one of [\"client\", \"user\"]."
|
70
|
+
subject['choice_hint'] = 'foo'
|
71
|
+
expect { subject.validate }.to raise_error IIIF::V3::Presentation::IllegalValueError, exp_err_msg
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'realistic examples' do
|
76
|
+
describe 'from digerati' do
|
77
|
+
let(:item_type) { 'Video' }
|
78
|
+
let(:item1_id) { 'http://example.org/foo.mp4f' }
|
79
|
+
let(:item1_mime) { 'video/mp4' }
|
80
|
+
let(:item1_res) { IIIF::V3::Presentation::Resource.new(
|
81
|
+
'id' => item1_id,
|
82
|
+
'type' => item_type,
|
83
|
+
'format' => item1_mime
|
84
|
+
)}
|
85
|
+
let(:item2_id) { 'http://example.org/foo.webm' }
|
86
|
+
let(:item2_mime) { 'video/webm' }
|
87
|
+
let(:item2_res) { IIIF::V3::Presentation::Resource.new(
|
88
|
+
'id' => item2_id,
|
89
|
+
'type' => item_type,
|
90
|
+
'format' => item2_mime
|
91
|
+
)}
|
92
|
+
let(:choice) { IIIF::V3::Presentation::Choice.new(
|
93
|
+
'choiceHint' => 'client',
|
94
|
+
'items' => [item1_res, item2_res]
|
95
|
+
)}
|
96
|
+
it 'validates' do
|
97
|
+
expect{choice.validate}.not_to raise_error
|
98
|
+
end
|
99
|
+
it 'has expected required values' do
|
100
|
+
expect(choice['type']).to eq 'Choice'
|
101
|
+
end
|
102
|
+
it 'has expected additional values' do
|
103
|
+
expect(choice.id).to be_nil
|
104
|
+
expect(choice['choice_hint']).to eq 'client'
|
105
|
+
expect(choice.choiceHint).to eq 'client'
|
106
|
+
expect(choice['items']).to eq [item1_res, item2_res]
|
107
|
+
first = choice['items'].first
|
108
|
+
expect(first.keys.size).to eq 3
|
109
|
+
expect(first['id']).to eq item1_id
|
110
|
+
expect(first['type']).to eq item_type
|
111
|
+
expect(first['format']).to eq item1_mime
|
112
|
+
second = choice['items'].last
|
113
|
+
expect(second.keys.size).to eq 3
|
114
|
+
expect(second['id']).to eq item2_id
|
115
|
+
expect(second['type']).to eq item_type
|
116
|
+
expect(second['format']).to eq item2_mime
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
describe IIIF::V3::Presentation::Collection do
|
2
|
+
|
3
|
+
let(:fixed_values) do
|
4
|
+
{
|
5
|
+
"@context" => [
|
6
|
+
"http://iiif.io/api/presentation/3/context.json",
|
7
|
+
"http://www.w3.org/ns/anno.jsonld"
|
8
|
+
],
|
9
|
+
'id' => 'http://example.org/iiif/collection/top',
|
10
|
+
'type' => 'Collection',
|
11
|
+
'label' => 'Top Level Collection for Example Organization',
|
12
|
+
'description' => 'Description of Collection',
|
13
|
+
'attribution' => 'Provided by Example Organization',
|
14
|
+
|
15
|
+
'collections' => [
|
16
|
+
{ 'id' => 'http://example.org/iiif/collection/part1',
|
17
|
+
'type' => 'Collection',
|
18
|
+
'label' => 'Sub Collection 1'
|
19
|
+
},
|
20
|
+
{ 'id' => 'http://example.org/iiif/collection/part2',
|
21
|
+
'type' => 'Collection',
|
22
|
+
'label' => 'Sub Collection 2'
|
23
|
+
}
|
24
|
+
],
|
25
|
+
'manifests' => [
|
26
|
+
{ 'id' => 'http://example.org/iiif/book1/manifest',
|
27
|
+
'type' => 'Manifest',
|
28
|
+
'label' => 'Book 1'
|
29
|
+
}
|
30
|
+
]
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#initialize' do
|
35
|
+
it 'sets type to Collection by default' do
|
36
|
+
expect(subject['type']).to eq 'Collection'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#{described_class}.define_methods_for_array_only_keys" do
|
41
|
+
it_behaves_like 'it has the appropriate methods for array-only keys v3'
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#{described_class}.define_methods_for_string_only_keys" do
|
45
|
+
it_behaves_like 'it has the appropriate methods for string-only keys v3'
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#{described_class}.define_methods_for_any_type_keys" do
|
49
|
+
it_behaves_like 'it has the appropriate methods for any-type keys v3'
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#validate' do
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|