@natlibfi/ekirjasto-opds-feed-parser 1.0.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/CHANGELOG +17 -0
- package/LICENSE +201 -0
- package/README.md +57 -0
- package/package.json +45 -0
- package/test/availability_parser_test.ts +32 -0
- package/test/category_parser_test.ts +30 -0
- package/test/contributor_parser_test.ts +44 -0
- package/test/copies_parser_test.ts +30 -0
- package/test/entry_parser_test.ts +272 -0
- package/test/feed_parser_test.ts +244 -0
- package/test/files/acquisition.xml +416 -0
- package/test/files/entry.xml +33 -0
- package/test/files/navigation.xml +56 -0
- package/test/holds_parser_test.ts +30 -0
- package/test/indirect_acquisition_parser_test.ts +46 -0
- package/test/link_parser_test.ts +324 -0
- package/test/namespace_parser_test.ts +41 -0
- package/test/opds_parser_test.ts +147 -0
- package/test/price_parser_test.ts +30 -0
- package/test/series_parser_test.ts +30 -0
- package/tsconfig.json +15 -0
- package/tslint.json +54 -0
- package/typings.json +8 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import PrefixMap from "../src/prefix_map";
|
|
2
|
+
import LinkParser from "../src/link_parser";
|
|
3
|
+
import NamespaceParser from "../src/namespace_parser";
|
|
4
|
+
import OPDSCatalogRootLink from "../src/opds_catalog_root_link";
|
|
5
|
+
import OPDSFacetLink from "../src/opds_facet_link";
|
|
6
|
+
import SearchLink from "../src/search_link";
|
|
7
|
+
import AlternateLink from "../src/alternate_link";
|
|
8
|
+
import CompleteEntryLink from "../src/complete_entry_link";
|
|
9
|
+
import OPDSAcquisitionLink from "../src/opds_acquisition_link";
|
|
10
|
+
import OPDSArtworkLink from "../src/opds_artwork_link";
|
|
11
|
+
import OPDSCrawlableLink from "../src/opds_crawlable_link";
|
|
12
|
+
import OPDSCollectionLink from "../src/opds_collection_link";
|
|
13
|
+
import OPDSShelfLink from "../src/opds_shelf_link";
|
|
14
|
+
import AvailabilityParser from "../src/availability_parser";
|
|
15
|
+
import HoldsParser from "../src/holds_parser";
|
|
16
|
+
import CopiesParser from "../src/copies_parser";
|
|
17
|
+
import chai = require("chai");
|
|
18
|
+
let expect = chai.expect;
|
|
19
|
+
|
|
20
|
+
describe("LinkParser", () => {
|
|
21
|
+
let parser: LinkParser;
|
|
22
|
+
let prefixes: PrefixMap;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
prefixes = {
|
|
26
|
+
[NamespaceParser.OPDS_URI]: "opds:",
|
|
27
|
+
[NamespaceParser.THR_URI]: "thr:"
|
|
28
|
+
};
|
|
29
|
+
parser = new LinkParser(prefixes);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("#parse", () => {
|
|
33
|
+
it("extracts link attributes", () => {
|
|
34
|
+
let link = {
|
|
35
|
+
"$": {
|
|
36
|
+
"href": {"value": "test href"},
|
|
37
|
+
"rel": {"value": "test rel"},
|
|
38
|
+
"type": {"value": "test type"},
|
|
39
|
+
"title": {"value": "test title"},
|
|
40
|
+
"role": {"value": "test role"}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
let parsedLink = parser.parse(link);
|
|
44
|
+
expect(parsedLink.href).to.equals("test href");
|
|
45
|
+
expect(parsedLink.rel).to.equals("test rel");
|
|
46
|
+
expect(parsedLink.type).to.equals("test type");
|
|
47
|
+
expect(parsedLink.title).to.equals("test title");
|
|
48
|
+
expect(parsedLink.role).to.equals("test role");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("allows no type, title, and role", () => {
|
|
52
|
+
let link = {
|
|
53
|
+
"$": {
|
|
54
|
+
"href": {"value": "test href"},
|
|
55
|
+
"rel": {"value": "test rel"},
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
let parsedLink = parser.parse(link);
|
|
59
|
+
expect(parsedLink.href).to.equals("test href");
|
|
60
|
+
expect(parsedLink.rel).to.equals("test rel");
|
|
61
|
+
expect(parsedLink.type).to.be.undefined;
|
|
62
|
+
expect(parsedLink.title).to.be.undefined;
|
|
63
|
+
expect(parsedLink.role).to.be.undefined;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("extracts catalog root link", () => {
|
|
67
|
+
let link = {
|
|
68
|
+
"$": {
|
|
69
|
+
"href": {"value": "test href"},
|
|
70
|
+
"rel": {"value": OPDSCatalogRootLink.REL}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
let parsedLink = parser.parse(link);
|
|
74
|
+
expect(parsedLink).to.be.an.instanceof(OPDSCatalogRootLink);
|
|
75
|
+
expect(parsedLink.href).to.equals("test href");
|
|
76
|
+
expect(parsedLink.rel).to.equals(OPDSCatalogRootLink.REL);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("extracts facet link", () => {
|
|
80
|
+
let link = {
|
|
81
|
+
"$": {
|
|
82
|
+
"href": {"value": "test href"},
|
|
83
|
+
"rel": {"value": OPDSFacetLink.REL},
|
|
84
|
+
"opds:facetGroup": {"value": "test facet group"},
|
|
85
|
+
"opds:activeFacet": {"value": "false"},
|
|
86
|
+
"thr:count": {"value": "57"}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
let parsedLink = <OPDSFacetLink>parser.parse(link);
|
|
90
|
+
expect(parsedLink).to.be.an.instanceof(OPDSFacetLink);
|
|
91
|
+
expect(parsedLink.href).to.equals("test href");
|
|
92
|
+
expect(parsedLink.rel).to.equals(OPDSFacetLink.REL);
|
|
93
|
+
expect(parsedLink.facetGroup).to.equals("test facet group");
|
|
94
|
+
expect(parsedLink.activeFacet).to.be.false;
|
|
95
|
+
expect(parsedLink.count).to.equals(57);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("extracts search link", () => {
|
|
99
|
+
let link = {
|
|
100
|
+
"$": {
|
|
101
|
+
"href": {"value": "test href"},
|
|
102
|
+
"rel": {"value": SearchLink.REL},
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
let parsedLink = <SearchLink>parser.parse(link);
|
|
106
|
+
expect(parsedLink).to.be.an.instanceof(SearchLink);
|
|
107
|
+
expect(parsedLink.href).to.equals("test href");
|
|
108
|
+
expect(parsedLink.rel).to.equals(SearchLink.REL);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("extracts acquisition link", () => {
|
|
112
|
+
OPDSAcquisitionLink.RELS.forEach(rel => {
|
|
113
|
+
let link = {
|
|
114
|
+
"$": {
|
|
115
|
+
"href": {"value": "test href"},
|
|
116
|
+
"rel": {"value": rel}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
let parsedLink = parser.parse(link);
|
|
120
|
+
expect(parsedLink).to.be.an.instanceof(OPDSAcquisitionLink);
|
|
121
|
+
expect(parsedLink.href).to.equals("test href");
|
|
122
|
+
expect(parsedLink.rel).to.equals(rel);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("extracts prices for acquisition link", () => {
|
|
127
|
+
let value = "1000.00";
|
|
128
|
+
let currencyCode = "USD";
|
|
129
|
+
let link = {
|
|
130
|
+
"$": {
|
|
131
|
+
"href": {"value": "test href"},
|
|
132
|
+
"rel": {"value": OPDSAcquisitionLink.BUY_REL}
|
|
133
|
+
},
|
|
134
|
+
"opds:price": [
|
|
135
|
+
{
|
|
136
|
+
"$": {
|
|
137
|
+
"currencyCode": { "value": currencyCode }
|
|
138
|
+
},
|
|
139
|
+
"_": value
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
};
|
|
143
|
+
let parsedLink = parser.parse(link);
|
|
144
|
+
expect(parsedLink).to.be.an.instanceof(OPDSAcquisitionLink);
|
|
145
|
+
let castParsedLink = <OPDSAcquisitionLink>parsedLink;
|
|
146
|
+
expect(castParsedLink.prices.length).to.equals(1);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("extracts nested indirect acquisition for acquisition link", () => {
|
|
150
|
+
let type1 = "vnd.adobe/adept+xml";
|
|
151
|
+
let type2 = "application/epub+zip";
|
|
152
|
+
let type3 = "application/zip";
|
|
153
|
+
let type4 = "application/pdf";
|
|
154
|
+
let link = {
|
|
155
|
+
"$": {
|
|
156
|
+
"href": {"value": "test href"},
|
|
157
|
+
"rel": {"value": OPDSAcquisitionLink.BUY_REL}
|
|
158
|
+
},
|
|
159
|
+
"opds:indirectAcquisition": [
|
|
160
|
+
{
|
|
161
|
+
"$": {
|
|
162
|
+
"type": { "value": type1 }
|
|
163
|
+
},
|
|
164
|
+
"opds:indirectAcquisition": [
|
|
165
|
+
{
|
|
166
|
+
"$": {
|
|
167
|
+
"type": { "value": type2 }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"$": {
|
|
174
|
+
"type": { "value": type3 }
|
|
175
|
+
},
|
|
176
|
+
"opds:indirectAcquisition": [
|
|
177
|
+
{
|
|
178
|
+
"$": {
|
|
179
|
+
"type": { "value": type4 }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
};
|
|
186
|
+
let parsedLink = parser.parse(link);
|
|
187
|
+
expect(parsedLink).to.be.an.instanceof(OPDSAcquisitionLink);
|
|
188
|
+
let castParsedLink = <OPDSAcquisitionLink>parsedLink;
|
|
189
|
+
expect(castParsedLink.indirectAcquisitions[0].type).to.equals(type1);
|
|
190
|
+
expect(castParsedLink.indirectAcquisitions[0].indirectAcquisitions[0].type).to.equals(type2);
|
|
191
|
+
expect(castParsedLink.indirectAcquisitions[1].type).to.equals(type3);
|
|
192
|
+
expect(castParsedLink.indirectAcquisitions[1].indirectAcquisitions[0].type).to.equals(type4);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("extracts availability, holds, copies for acquisition link", () => {
|
|
196
|
+
let link = {
|
|
197
|
+
$: {
|
|
198
|
+
href: { value: "test href" },
|
|
199
|
+
rel: { value: OPDSAcquisitionLink.BORROW_REL }
|
|
200
|
+
},
|
|
201
|
+
"opds:availability": [
|
|
202
|
+
{
|
|
203
|
+
$: {
|
|
204
|
+
status: { value: "unavailable" },
|
|
205
|
+
until: { value: "2016-11-02T19:46:27Z" }
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
],
|
|
209
|
+
"opds:holds": [
|
|
210
|
+
{
|
|
211
|
+
$: {
|
|
212
|
+
total: { value: "29" },
|
|
213
|
+
position: { value: "11" }
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
],
|
|
217
|
+
"opds:copies": [
|
|
218
|
+
{
|
|
219
|
+
$: {
|
|
220
|
+
total: { value: "7" },
|
|
221
|
+
available: { value: "0" }
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
};
|
|
226
|
+
let parsedLink = parser.parse(link);
|
|
227
|
+
expect(parsedLink).to.be.an.instanceof(OPDSAcquisitionLink);
|
|
228
|
+
let castParsedLink = <OPDSAcquisitionLink>parsedLink;
|
|
229
|
+
expect(castParsedLink.availability).to.deep.equal(
|
|
230
|
+
new AvailabilityParser(prefixes).parse(link["opds:availability"][0])
|
|
231
|
+
);
|
|
232
|
+
expect(castParsedLink.holds).to.deep.equal(
|
|
233
|
+
new HoldsParser(prefixes).parse(link["opds:holds"][0])
|
|
234
|
+
);
|
|
235
|
+
expect(castParsedLink.copies).to.deep.equal(
|
|
236
|
+
new CopiesParser(prefixes).parse(link["opds:copies"][0])
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("extracts artwork link", () => {
|
|
241
|
+
OPDSArtworkLink.RELS.forEach(rel => {
|
|
242
|
+
let link = {
|
|
243
|
+
"$": {
|
|
244
|
+
"href": {"value": "test href"},
|
|
245
|
+
"rel": {"value": rel}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
let parsedLink = parser.parse(link);
|
|
249
|
+
expect(parsedLink).to.be.an.instanceof(OPDSArtworkLink);
|
|
250
|
+
expect(parsedLink.href).to.equals("test href");
|
|
251
|
+
expect(parsedLink.rel).to.equals(rel);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("extracts alternate link", () => {
|
|
256
|
+
let link = {
|
|
257
|
+
"$": {
|
|
258
|
+
"href": {"value": "test href"},
|
|
259
|
+
"rel": {"value": AlternateLink.REL}
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
let parsedLink = parser.parse(link);
|
|
263
|
+
expect(parsedLink).to.be.an.instanceof(AlternateLink);
|
|
264
|
+
expect(parsedLink.href).to.equals("test href");
|
|
265
|
+
expect(parsedLink.rel).to.equals(AlternateLink.REL);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("extracts complete entry link", () => {
|
|
269
|
+
let link = {
|
|
270
|
+
"$": {
|
|
271
|
+
"href": {"value": "test href"},
|
|
272
|
+
"rel": {"value": AlternateLink.REL},
|
|
273
|
+
"type": {"value": CompleteEntryLink.TYPE}
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
let parsedLink = parser.parse(link);
|
|
277
|
+
expect(parsedLink).to.be.an.instanceof(CompleteEntryLink);
|
|
278
|
+
expect(parsedLink.href).to.equals("test href");
|
|
279
|
+
expect(parsedLink.rel).to.equals(AlternateLink.REL);
|
|
280
|
+
expect(parsedLink.type).to.equals(CompleteEntryLink.TYPE);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it("extracts crawlable link", () => {
|
|
284
|
+
let link = {
|
|
285
|
+
"$": {
|
|
286
|
+
"href": {"value": "test href"},
|
|
287
|
+
"rel": {"value": OPDSCrawlableLink.REL}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
let parsedLink = parser.parse(link);
|
|
291
|
+
expect(parsedLink).to.be.an.instanceof(OPDSCrawlableLink);
|
|
292
|
+
expect(parsedLink.href).to.equals("test href");
|
|
293
|
+
expect(parsedLink.rel).to.equals(OPDSCrawlableLink.REL);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it("extracts collection link", () => {
|
|
297
|
+
let link = {
|
|
298
|
+
"$": {
|
|
299
|
+
"href": {"value": "test href"},
|
|
300
|
+
"rel": {"value": OPDSCollectionLink.REL},
|
|
301
|
+
"title": {"value": "test title"}
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
let parsedLink = parser.parse(link);
|
|
305
|
+
expect(parsedLink).to.be.an.instanceof(OPDSCollectionLink);
|
|
306
|
+
expect(parsedLink.href).to.equals("test href");
|
|
307
|
+
expect(parsedLink.title).to.equals("test title");
|
|
308
|
+
expect(parsedLink.rel).to.equals(OPDSCollectionLink.REL);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it("extracts shelf link", () => {
|
|
312
|
+
let link = {
|
|
313
|
+
"$": {
|
|
314
|
+
"href": {"value": "test href"},
|
|
315
|
+
"rel": {"value": OPDSShelfLink.REL}
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
let parsedLink = parser.parse(link);
|
|
319
|
+
expect(parsedLink).to.be.an.instanceof(OPDSShelfLink);
|
|
320
|
+
expect(parsedLink.href).to.equals("test href");
|
|
321
|
+
expect(parsedLink.rel).to.equals(OPDSShelfLink.REL);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import NamespaceParser from "../src/namespace_parser";
|
|
2
|
+
import chai = require("chai");
|
|
3
|
+
let expect = chai.expect;
|
|
4
|
+
|
|
5
|
+
describe("NamespaceParser", () => {
|
|
6
|
+
let parser: NamespaceParser;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
parser = new NamespaceParser();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe("#prefixes", () => {
|
|
13
|
+
it("returns empty string for no prefix", () => {
|
|
14
|
+
let namespaces = {
|
|
15
|
+
"$": {
|
|
16
|
+
"xmlns": {
|
|
17
|
+
"value": NamespaceParser.ATOM_URI,
|
|
18
|
+
"local": ""
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
let prefixes = parser.prefixes(namespaces);
|
|
23
|
+
let atomPrefix = prefixes[NamespaceParser.ATOM_URI];
|
|
24
|
+
expect(atomPrefix).to.equals("");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("returns prefix with colon", () => {
|
|
28
|
+
let namespaces = {
|
|
29
|
+
"$": {
|
|
30
|
+
"xmlns:atom": {
|
|
31
|
+
"value": NamespaceParser.ATOM_URI,
|
|
32
|
+
"local": "atom"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
let prefixes = parser.prefixes(namespaces);
|
|
37
|
+
let atomPrefix = prefixes[NamespaceParser.ATOM_URI];
|
|
38
|
+
expect(atomPrefix).to.equals("atom:");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import OPDSFeed from "../src/opds_feed";
|
|
2
|
+
import {
|
|
3
|
+
NavigationFeed,
|
|
4
|
+
AcquisitionFeed,
|
|
5
|
+
OPDSEntry,
|
|
6
|
+
OPDSCatalogRootLink,
|
|
7
|
+
SearchLink,
|
|
8
|
+
AlternateLink,
|
|
9
|
+
OPDSAcquisitionLink,
|
|
10
|
+
OPDSArtworkLink
|
|
11
|
+
} from "../src/index";
|
|
12
|
+
import OPDSParser from "../src/opds_parser";
|
|
13
|
+
import fs = require("fs");
|
|
14
|
+
import chai = require("chai");
|
|
15
|
+
let expect = chai.expect;
|
|
16
|
+
|
|
17
|
+
describe("OPDSParser", () => {
|
|
18
|
+
let parser: OPDSParser;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
parser = new OPDSParser();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("#parse", () => {
|
|
25
|
+
it("raises error when input has no feed or entry", (done) => {
|
|
26
|
+
let opds = "<test></test>";
|
|
27
|
+
parser.parse(opds).then(() => {
|
|
28
|
+
done("parser did not raise error for input with no feed");
|
|
29
|
+
}).catch((error) => {
|
|
30
|
+
done();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("allows foreign markup", (done) => {
|
|
35
|
+
// http://opds-spec.org/specs/opds-catalog-1-1-20110627/#Document_Extensibility
|
|
36
|
+
let opds = "<feed><test /></feed>";
|
|
37
|
+
parser.parse(opds).then((result) => {
|
|
38
|
+
if (result) {
|
|
39
|
+
done();
|
|
40
|
+
} else {
|
|
41
|
+
done("parse did not return an OPDSFeed");
|
|
42
|
+
}
|
|
43
|
+
}).catch((error) => {
|
|
44
|
+
done(error);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("parses navigation feed from OPDS test catalog", (done) => {
|
|
49
|
+
fs.readFile("test/files/navigation.xml", "utf8", (error, data) => {
|
|
50
|
+
if (error) {
|
|
51
|
+
done(error);
|
|
52
|
+
} else {
|
|
53
|
+
let promise: Promise<OPDSEntry | OPDSFeed> = parser.parse(data);
|
|
54
|
+
promise.then((result: OPDSFeed) => {
|
|
55
|
+
expect(result).to.be.an.instanceof(NavigationFeed);
|
|
56
|
+
expect(result).not.to.be.an.instanceof(AcquisitionFeed);
|
|
57
|
+
expect(result.id).to.equals("root.xml");
|
|
58
|
+
expect(result.title).to.equals("Test Catalog Root");
|
|
59
|
+
expect(result.updated).to.equals("2012-10-20T01:11:18Z");
|
|
60
|
+
|
|
61
|
+
expect(result.links.length).to.equals(5);
|
|
62
|
+
expect(result.links[0].href).to.equals("root.xml");
|
|
63
|
+
expect(result.links[1]).to.be.an.instanceof(OPDSCatalogRootLink);
|
|
64
|
+
expect(result.links[2]).to.be.an.instanceof(SearchLink);
|
|
65
|
+
|
|
66
|
+
expect(result.entries.length).to.equals(6);
|
|
67
|
+
expect(result.entries[0].title).to.equals("First Acquisition feed");
|
|
68
|
+
expect(result.entries[0].id).to.equals("main.xml");
|
|
69
|
+
expect(result.entries[0].updated).to.equals("2012-10-20T01:11:18Z");
|
|
70
|
+
expect(result.entries[0].summary.content).to.equals("Basic acquisition feed");
|
|
71
|
+
expect(result.entries[0].links.length).to.equals(1);
|
|
72
|
+
expect(result.entries[0].links[0].type).to.equals("application/atom+xml; profile=opds-catalog; kind=acquisition");
|
|
73
|
+
expect(result.entries[3].title).to.equals("Link: Recommended");
|
|
74
|
+
expect(result.entries[5].id).to.equals("popular.xml");
|
|
75
|
+
}).then(done, done);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("parses acquisition feed from OPDS test catalog", (done) => {
|
|
81
|
+
fs.readFile("test/files/acquisition.xml", "utf8", (error, data) => {
|
|
82
|
+
if (error) {
|
|
83
|
+
done(error);
|
|
84
|
+
} else {
|
|
85
|
+
let promise: Promise<OPDSEntry | OPDSFeed> = parser.parse(data);
|
|
86
|
+
promise.then((result: OPDSFeed) => {
|
|
87
|
+
expect(result).to.be.an.instanceof(AcquisitionFeed);
|
|
88
|
+
expect(result).not.to.be.an.instanceof(NavigationFeed);
|
|
89
|
+
expect(result.id).to.equals("main.xml");
|
|
90
|
+
expect(result.title).to.equals("First Acquisition Feed");
|
|
91
|
+
expect(result.updated).to.equals("2012-10-20T01:11:18Z");
|
|
92
|
+
expect(result.search.totalResults).to.equals(18);
|
|
93
|
+
|
|
94
|
+
expect(result.links.length).to.equals(4);
|
|
95
|
+
expect(result.links[1].href).to.equals("../root.xml");
|
|
96
|
+
expect(result.links[1]).to.be.an.instanceof(OPDSCatalogRootLink);
|
|
97
|
+
expect(result.links[2]).to.be.an.instanceof(SearchLink);
|
|
98
|
+
|
|
99
|
+
expect(result.entries.length).to.equals(17);
|
|
100
|
+
expect(result.entries[0].title).to.equals("Pride and Prejudice");
|
|
101
|
+
expect(result.entries[0].id).to.equals("http://feedbooks.github.io/test/1");
|
|
102
|
+
expect(result.entries[0].authors.length).to.equals(1);
|
|
103
|
+
expect(result.entries[0].authors[0].name).to.equals("Jane Austen");
|
|
104
|
+
expect(result.entries[0].updated).to.equals("2012-10-12T17:43:18Z");
|
|
105
|
+
expect(result.entries[0].summary.content).to.contain("the story of Mrs. Bennet's attempts to marry off her five daughters");
|
|
106
|
+
expect(result.entries[0].categories.length).to.equals(2);
|
|
107
|
+
expect(result.entries[0].categories[0].term).to.equals("FBFIC000000");
|
|
108
|
+
expect(result.entries[0].categories[0].label).to.equals("Fiction");
|
|
109
|
+
|
|
110
|
+
expect(result.entries[0].links.length).to.equals(4);
|
|
111
|
+
expect(result.entries[0].links[0]).to.be.an.instanceof(OPDSAcquisitionLink);
|
|
112
|
+
expect(result.entries[0].links[0].href).to.equals("http://www.feedbooks.com/book/52.epub");
|
|
113
|
+
expect(result.entries[0].links[1]).to.be.an.instanceof(OPDSArtworkLink);
|
|
114
|
+
}).then(done, done);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("parses example library simplified entry", (done) => {
|
|
120
|
+
fs.readFile("test/files/entry.xml", "utf8", (error, data) => {
|
|
121
|
+
if (error) {
|
|
122
|
+
done(error);
|
|
123
|
+
} else {
|
|
124
|
+
let promise: Promise<OPDSFeed | OPDSEntry> = parser.parse(data);
|
|
125
|
+
promise.then((result: OPDSEntry) => {
|
|
126
|
+
expect(result).to.be.an.instanceof(OPDSEntry);
|
|
127
|
+
expect(result).not.to.be.an.instanceof(OPDSFeed);
|
|
128
|
+
expect(result.id).to.equals("urn:librarysimplified.org/terms/id/Overdrive%20ID/135d0478-1ab9-4cc1-a1aa-2aa616b1218c");
|
|
129
|
+
expect(result.title).to.equals("The Frances Hodgson Burnett Megapack");
|
|
130
|
+
expect(result.updated).to.equals("2016-01-08T21:38:19Z");
|
|
131
|
+
|
|
132
|
+
expect(result.authors.length).to.equals(1);
|
|
133
|
+
expect(result.authors[0].name).to.equals("Frances Hodgson Burnett");
|
|
134
|
+
|
|
135
|
+
expect(result.links.length).to.equals(5);
|
|
136
|
+
expect(result.links[2]).to.be.an.instanceof(AlternateLink);
|
|
137
|
+
expect(result.links[4]).to.be.an.instanceof(OPDSAcquisitionLink);
|
|
138
|
+
expect((<OPDSAcquisitionLink>result.links[4]).indirectAcquisitions.length).to.equals(4);
|
|
139
|
+
|
|
140
|
+
expect(result.categories.length).to.equals(4);
|
|
141
|
+
expect(result.language).to.equals("en");
|
|
142
|
+
}).then(done, done);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import PrefixMap from "../src/prefix_map";
|
|
2
|
+
import OPDSPrice from "../src/opds_price";
|
|
3
|
+
import PriceParser from "../src/price_parser";
|
|
4
|
+
import chai = require("chai");
|
|
5
|
+
let expect = chai.expect;
|
|
6
|
+
|
|
7
|
+
describe("PriceParser", () => {
|
|
8
|
+
let parser: PriceParser;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
let prefixes: PrefixMap = {};
|
|
12
|
+
parser = new PriceParser(prefixes);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe("#parse", () => {
|
|
16
|
+
it("extracts attributes", () => {
|
|
17
|
+
let value = "1000.00";
|
|
18
|
+
let currencyCode = "USD";
|
|
19
|
+
let price = {
|
|
20
|
+
"$": {
|
|
21
|
+
"currencyCode": { "value": currencyCode }
|
|
22
|
+
},
|
|
23
|
+
"_": value
|
|
24
|
+
};
|
|
25
|
+
let parsedPrice = parser.parse(price);
|
|
26
|
+
expect(parsedPrice.value).to.equals(value);
|
|
27
|
+
expect(parsedPrice.currencyCode).to.equals(currencyCode);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import PrefixMap from "../src/prefix_map";
|
|
2
|
+
import Series from "../src/series";
|
|
3
|
+
import SeriesParser from "../src/series_parser";
|
|
4
|
+
import NamespaceParser from "../src/namespace_parser";
|
|
5
|
+
import chai = require("chai");
|
|
6
|
+
let expect = chai.expect;
|
|
7
|
+
|
|
8
|
+
describe("SeriesParser", () => {
|
|
9
|
+
let parser: SeriesParser;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
let prefixes: PrefixMap = {};
|
|
13
|
+
prefixes[NamespaceParser.SCHEMA_URI] = "schema:";
|
|
14
|
+
parser = new SeriesParser(prefixes);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("#parse", () => {
|
|
18
|
+
it("extracts attributes", () => {
|
|
19
|
+
let series = {
|
|
20
|
+
$: {
|
|
21
|
+
name: { value: "series name" },
|
|
22
|
+
"schema:position": { value: 5 }
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
let parsed = parser.parse(series);
|
|
26
|
+
expect(parsed.name).to.equals("series name");
|
|
27
|
+
expect(parsed.position).to.equals(5);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"target": "es5",
|
|
5
|
+
"noImplicitAny": false,
|
|
6
|
+
"outDir": "lib",
|
|
7
|
+
"sourceMap": false,
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"types": ["mocha", "node"],
|
|
10
|
+
"lib": ["dom", "es2015"]
|
|
11
|
+
},
|
|
12
|
+
"exclude": [
|
|
13
|
+
"node_modules"
|
|
14
|
+
]
|
|
15
|
+
}
|
package/tslint.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"rules": {
|
|
3
|
+
"class-name": true,
|
|
4
|
+
"comment-format": [
|
|
5
|
+
true,
|
|
6
|
+
"check-space"
|
|
7
|
+
],
|
|
8
|
+
"indent": [
|
|
9
|
+
true,
|
|
10
|
+
"spaces"
|
|
11
|
+
],
|
|
12
|
+
"no-duplicate-variable": true,
|
|
13
|
+
"no-eval": true,
|
|
14
|
+
"no-internal-module": true,
|
|
15
|
+
"no-trailing-whitespace": true,
|
|
16
|
+
"no-var-keyword": true,
|
|
17
|
+
"one-line": [
|
|
18
|
+
true,
|
|
19
|
+
"check-open-brace",
|
|
20
|
+
"check-whitespace"
|
|
21
|
+
],
|
|
22
|
+
"quotemark": [
|
|
23
|
+
true,
|
|
24
|
+
"double"
|
|
25
|
+
],
|
|
26
|
+
"semicolon": true,
|
|
27
|
+
"triple-equals": [
|
|
28
|
+
true,
|
|
29
|
+
"allow-null-check"
|
|
30
|
+
],
|
|
31
|
+
"typedef-whitespace": [
|
|
32
|
+
true,
|
|
33
|
+
{
|
|
34
|
+
"call-signature": "nospace",
|
|
35
|
+
"index-signature": "nospace",
|
|
36
|
+
"parameter": "nospace",
|
|
37
|
+
"property-declaration": "nospace",
|
|
38
|
+
"variable-declaration": "nospace"
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"variable-name": [
|
|
42
|
+
true,
|
|
43
|
+
"ban-keywords"
|
|
44
|
+
],
|
|
45
|
+
"whitespace": [
|
|
46
|
+
true,
|
|
47
|
+
"check-branch",
|
|
48
|
+
"check-decl",
|
|
49
|
+
"check-operator",
|
|
50
|
+
"check-separator",
|
|
51
|
+
"check-type"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
package/typings.json
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ambientDependencies": {
|
|
3
|
+
"chai": "github:DefinitelyTyped/DefinitelyTyped/chai/chai.d.ts#9c25433c84251bfe72bf0030a95edbbb2c81c9d5",
|
|
4
|
+
"mocha": "github:DefinitelyTyped/DefinitelyTyped/mocha/mocha.d.ts#d6dd320291705694ba8e1a79497a908e9f5e6617",
|
|
5
|
+
"node": "github:DefinitelyTyped/DefinitelyTyped/node/node.d.ts#1c56e368e17bb28ca57577250624ca5bd561aa81",
|
|
6
|
+
"xml2js": "github:DefinitelyTyped/DefinitelyTyped/xml2js/xml2js.d.ts#7304e0770d53762f89af7fcf14517d5f45a04cc2"
|
|
7
|
+
}
|
|
8
|
+
}
|