@rapthi/podca-ts 1.0.3 → 1.0.10
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/index.cjs +172 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +120 -0
- package/dist/index.d.ts +120 -5
- package/dist/index.js +168 -17
- package/dist/index.js.map +1 -1
- package/package.json +11 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/itunes-search/iTunesSearch.d.ts +0 -6
- package/dist/itunes-search/iTunesSearch.d.ts.map +0 -1
- package/dist/itunes-search/iTunesSearch.js +0 -7
- package/dist/itunes-search/iTunesSearch.js.map +0 -1
- package/dist/itunes-search/itunes-search-options.d.ts +0 -15
- package/dist/itunes-search/itunes-search-options.d.ts.map +0 -1
- package/dist/itunes-search/itunes-search-options.js +0 -2
- package/dist/itunes-search/itunes-search-options.js.map +0 -1
- package/dist/itunes-search/itunes-search-result.d.ts +0 -41
- package/dist/itunes-search/itunes-search-result.d.ts.map +0 -1
- package/dist/itunes-search/itunes-search-result.js +0 -2
- package/dist/itunes-search/itunes-search.d.ts +0 -9
- package/dist/itunes-search/itunes-search.d.ts.map +0 -1
- package/dist/itunes-search/itunes-search.js +0 -34
- package/dist/itunes-search/itunes-search.spec.d.ts +0 -2
- package/dist/itunes-search/itunes-search.spec.d.ts.map +0 -1
- package/dist/itunes-search/itunes-search.spec.js +0 -46
- package/dist/podcast/podcast.d.ts +0 -55
- package/dist/podcast/podcast.d.ts.map +0 -1
- package/dist/podcast/podcast.js +0 -124
- package/dist/podcast/podcast.spec.d.ts +0 -2
- package/dist/podcast/podcast.spec.d.ts.map +0 -1
- package/dist/podcast/podcast.spec.js +0 -364
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { PodcastLoader } from './podcast';
|
|
3
|
-
vi.mock('@sesamy/podcast-parser', () => ({
|
|
4
|
-
parseFeedToJson: vi.fn(),
|
|
5
|
-
}));
|
|
6
|
-
import { parseFeedToJson } from '@sesamy/podcast-parser';
|
|
7
|
-
describe('PodcastLoader', () => {
|
|
8
|
-
let podcastLoader;
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
podcastLoader = new PodcastLoader();
|
|
11
|
-
vi.stubGlobal('fetch', vi.fn());
|
|
12
|
-
});
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
vi.clearAllMocks();
|
|
15
|
-
});
|
|
16
|
-
describe('getPodcastFromFeed', () => {
|
|
17
|
-
it('should successfully parse a valid podcast feed', async () => {
|
|
18
|
-
const mockXmlResponse = `<?xml version="1.0" encoding="UTF-8"?>
|
|
19
|
-
<rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://podcastindex.org/namespace/1.0">
|
|
20
|
-
<channel>
|
|
21
|
-
<title>Test Podcast</title>
|
|
22
|
-
<description>A test podcast</description>
|
|
23
|
-
<link>https://example.com</link>
|
|
24
|
-
<language>en</language>
|
|
25
|
-
<itunes:category text="Technology">
|
|
26
|
-
<itunes:category text="Software How-To"/>
|
|
27
|
-
</itunes:category>
|
|
28
|
-
<itunes:explicit>false</itunes:explicit>
|
|
29
|
-
<itunes:image href="https://example.com/image.jpg"/>
|
|
30
|
-
<itunes:author>John Doe</itunes:author>
|
|
31
|
-
<copyright>2024 Test Podcast</copyright>
|
|
32
|
-
<podcast:funding url="https://example.com/support">Support Us</podcast:funding>
|
|
33
|
-
<itunes:type>episodic</itunes:type>
|
|
34
|
-
<item>
|
|
35
|
-
<title>Episode 1</title>
|
|
36
|
-
<guid>#text</guid>
|
|
37
|
-
<link>https://example.com/episode1</link>
|
|
38
|
-
<pubDate>Mon, 01 Jan 2024 12:00:00 GMT</pubDate>
|
|
39
|
-
<description>First episode</description>
|
|
40
|
-
<itunes:duration>3600</itunes:duration>
|
|
41
|
-
<itunes:image href="https://example.com/ep1.jpg"/>
|
|
42
|
-
<itunes:explicit>no</itunes:explicit>
|
|
43
|
-
<itunes:episode>1</itunes:episode>
|
|
44
|
-
<itunes:season>1</itunes:season>
|
|
45
|
-
<itunes:episodeType>full</itunes:episodeType>
|
|
46
|
-
<enclosure url="https://example.com/ep1.mp3" type="audio/mpeg" length="123456"/>
|
|
47
|
-
</item>
|
|
48
|
-
</channel>
|
|
49
|
-
</rss>`;
|
|
50
|
-
const mockParsedData = {
|
|
51
|
-
rss: {
|
|
52
|
-
channel: {
|
|
53
|
-
title: 'Test Podcast',
|
|
54
|
-
description: 'A test podcast',
|
|
55
|
-
link: 'https://example.com',
|
|
56
|
-
language: 'en',
|
|
57
|
-
'itunes:category': [
|
|
58
|
-
{
|
|
59
|
-
'@_text': 'Technology',
|
|
60
|
-
'itunes:category': [{ '@_text': 'Software How-To' }],
|
|
61
|
-
},
|
|
62
|
-
],
|
|
63
|
-
'itunes:explicit': 'false',
|
|
64
|
-
'itunes:image': { '@_href': 'https://example.com/image.jpg' },
|
|
65
|
-
'itunes:author': 'John Doe',
|
|
66
|
-
copyright: '2024 Test Podcast',
|
|
67
|
-
'podcast:funding': { '@_url': 'https://example.com/support' },
|
|
68
|
-
'itunes:type': 'episodic',
|
|
69
|
-
item: [
|
|
70
|
-
{
|
|
71
|
-
title: 'Episode 1',
|
|
72
|
-
guid: { '#text': 'episode-1-guid' },
|
|
73
|
-
link: 'https://example.com/episode1',
|
|
74
|
-
pubDate: 'Mon, 01 Jan 2024 12:00:00 GMT',
|
|
75
|
-
description: 'First episode',
|
|
76
|
-
'itunes:duration': '3600',
|
|
77
|
-
'itunes:image': { '@_href': 'https://example.com/ep1.jpg' },
|
|
78
|
-
'itunes:explicit': 'no',
|
|
79
|
-
'itunes:episode': 1,
|
|
80
|
-
'itunes:season': 1,
|
|
81
|
-
'itunes:episodeType': 'full',
|
|
82
|
-
enclosure: [
|
|
83
|
-
{
|
|
84
|
-
'@_url': 'https://example.com/ep1.mp3',
|
|
85
|
-
'@_type': 'audio/mpeg',
|
|
86
|
-
'@_length': '123456',
|
|
87
|
-
},
|
|
88
|
-
],
|
|
89
|
-
},
|
|
90
|
-
],
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
};
|
|
94
|
-
vi.mocked(fetch).mockResolvedValueOnce({
|
|
95
|
-
ok: true,
|
|
96
|
-
text: async () => mockXmlResponse,
|
|
97
|
-
});
|
|
98
|
-
vi.mocked(parseFeedToJson).mockResolvedValueOnce(mockParsedData);
|
|
99
|
-
const result = await podcastLoader.getPodcastFromFeed('https://example.com/feed.xml');
|
|
100
|
-
expect(result.title).toBe('Test Podcast');
|
|
101
|
-
expect(result.description).toBe('A test podcast');
|
|
102
|
-
expect(result.link).toBe('https://example.com');
|
|
103
|
-
expect(result.language).toBe('en');
|
|
104
|
-
expect(result.explicit).toBe(false);
|
|
105
|
-
expect(result.imageUrl).toBe('https://example.com/image.jpg');
|
|
106
|
-
expect(result.author).toBe('John Doe');
|
|
107
|
-
expect(result.copyright).toBe('2024 Test Podcast');
|
|
108
|
-
expect(result.fundingUrl).toBe('https://example.com/support');
|
|
109
|
-
expect(result.type).toBe('episodic');
|
|
110
|
-
expect(result.categories).toHaveLength(2);
|
|
111
|
-
expect(result.episodes).toHaveLength(1);
|
|
112
|
-
});
|
|
113
|
-
it('should handle explicit content correctly', async () => {
|
|
114
|
-
const mockParsedData = {
|
|
115
|
-
rss: {
|
|
116
|
-
channel: {
|
|
117
|
-
title: 'Explicit Podcast',
|
|
118
|
-
description: 'Contains explicit content',
|
|
119
|
-
link: 'https://example.com',
|
|
120
|
-
language: 'en',
|
|
121
|
-
'itunes:explicit': 'true',
|
|
122
|
-
item: [],
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
vi.mocked(fetch).mockResolvedValueOnce({
|
|
127
|
-
ok: true,
|
|
128
|
-
text: async () => '<xml></xml>',
|
|
129
|
-
});
|
|
130
|
-
vi.mocked(parseFeedToJson).mockResolvedValueOnce(mockParsedData);
|
|
131
|
-
const result = await podcastLoader.getPodcastFromFeed('https://example.com/feed.xml');
|
|
132
|
-
expect(result.explicit).toBe(true);
|
|
133
|
-
});
|
|
134
|
-
it('should handle missing optional fields gracefully', async () => {
|
|
135
|
-
const mockParsedData = {
|
|
136
|
-
rss: {
|
|
137
|
-
channel: {
|
|
138
|
-
title: 'Minimal Podcast',
|
|
139
|
-
link: 'https://example.com',
|
|
140
|
-
item: [],
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
vi.mocked(fetch).mockResolvedValueOnce({
|
|
145
|
-
ok: true,
|
|
146
|
-
text: async () => '<xml></xml>',
|
|
147
|
-
});
|
|
148
|
-
vi.mocked(parseFeedToJson).mockResolvedValueOnce(mockParsedData);
|
|
149
|
-
const result = await podcastLoader.getPodcastFromFeed('https://example.com/feed.xml');
|
|
150
|
-
expect(result.title).toBe('Minimal Podcast');
|
|
151
|
-
expect(result.description).toBeUndefined();
|
|
152
|
-
expect(result.imageUrl).toBeUndefined();
|
|
153
|
-
expect(result.author).toBeUndefined();
|
|
154
|
-
expect(result.copyright).toBeUndefined();
|
|
155
|
-
expect(result.fundingUrl).toBeUndefined();
|
|
156
|
-
expect(result.type).toBeUndefined();
|
|
157
|
-
expect(result.categories).toEqual([]);
|
|
158
|
-
expect(result.episodes).toEqual([]);
|
|
159
|
-
});
|
|
160
|
-
it('should throw an error when fetch fails', async () => {
|
|
161
|
-
vi.mocked(fetch).mockRejectedValueOnce(new Error('Network error'));
|
|
162
|
-
await expect(podcastLoader.getPodcastFromFeed('https://invalid.com/feed.xml')).rejects.toThrow('Network error');
|
|
163
|
-
});
|
|
164
|
-
it('should throw an error when response is not ok', async () => {
|
|
165
|
-
vi.mocked(fetch).mockResolvedValueOnce({
|
|
166
|
-
ok: false,
|
|
167
|
-
status: 404,
|
|
168
|
-
});
|
|
169
|
-
await expect(podcastLoader.getPodcastFromFeed('https://example.com/feed.xml')).rejects.toThrow();
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
describe('mapCategories', () => {
|
|
173
|
-
it('should map single category without subcategories', () => {
|
|
174
|
-
const categories = [{ '@_text': 'Technology' }];
|
|
175
|
-
const result = podcastLoader['mapCategories'](categories);
|
|
176
|
-
expect(result).toEqual([{ name: 'Technology' }]);
|
|
177
|
-
});
|
|
178
|
-
it('should map categories with subcategories', () => {
|
|
179
|
-
const categories = [
|
|
180
|
-
{
|
|
181
|
-
'@_text': 'Technology',
|
|
182
|
-
'itunes:category': [
|
|
183
|
-
{ '@_text': 'Software How-To' },
|
|
184
|
-
{ '@_text': 'Gadgets' },
|
|
185
|
-
],
|
|
186
|
-
},
|
|
187
|
-
];
|
|
188
|
-
const result = podcastLoader['mapCategories'](categories);
|
|
189
|
-
expect(result).toEqual([
|
|
190
|
-
{ name: 'Technology' },
|
|
191
|
-
{ name: 'Software How-To' },
|
|
192
|
-
{ name: 'Gadgets' },
|
|
193
|
-
]);
|
|
194
|
-
});
|
|
195
|
-
it('should handle multiple parent categories with subcategories', () => {
|
|
196
|
-
const categories = [
|
|
197
|
-
{
|
|
198
|
-
'@_text': 'Technology',
|
|
199
|
-
'itunes:category': [{ '@_text': 'Software How-To' }],
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
'@_text': 'Business',
|
|
203
|
-
'itunes:category': [{ '@_text': 'Careers' }],
|
|
204
|
-
},
|
|
205
|
-
];
|
|
206
|
-
const result = podcastLoader['mapCategories'](categories);
|
|
207
|
-
expect(result).toEqual([
|
|
208
|
-
{ name: 'Technology' },
|
|
209
|
-
{ name: 'Software How-To' },
|
|
210
|
-
{ name: 'Business' },
|
|
211
|
-
{ name: 'Careers' },
|
|
212
|
-
]);
|
|
213
|
-
});
|
|
214
|
-
it('should return empty array when categories is undefined', () => {
|
|
215
|
-
const result = podcastLoader['mapCategories'](undefined);
|
|
216
|
-
expect(result).toEqual([]);
|
|
217
|
-
});
|
|
218
|
-
it('should handle categories without subcategories', () => {
|
|
219
|
-
const categories = [
|
|
220
|
-
{ '@_text': 'Technology' },
|
|
221
|
-
{ '@_text': 'Business' },
|
|
222
|
-
];
|
|
223
|
-
const result = podcastLoader['mapCategories'](categories);
|
|
224
|
-
expect(result).toEqual([
|
|
225
|
-
{ name: 'Technology' },
|
|
226
|
-
{ name: 'Business' },
|
|
227
|
-
]);
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
describe('mapEpisodes', () => {
|
|
231
|
-
it('should map episodes with all fields', () => {
|
|
232
|
-
const items = [
|
|
233
|
-
{
|
|
234
|
-
title: 'Episode 1',
|
|
235
|
-
guid: { '#text': 'ep1-guid' },
|
|
236
|
-
link: 'https://example.com/ep1',
|
|
237
|
-
pubDate: 'Mon, 01 Jan 2024 12:00:00 GMT',
|
|
238
|
-
description: 'First episode',
|
|
239
|
-
'itunes:duration': '3600',
|
|
240
|
-
'itunes:image': { '@_href': 'https://example.com/ep1.jpg' },
|
|
241
|
-
'itunes:explicit': 'yes',
|
|
242
|
-
'itunes:episode': 1,
|
|
243
|
-
'itunes:season': 1,
|
|
244
|
-
'itunes:episodeType': 'full',
|
|
245
|
-
enclosure: [
|
|
246
|
-
{
|
|
247
|
-
'@_url': 'https://example.com/ep1.mp3',
|
|
248
|
-
'@_type': 'audio/mpeg',
|
|
249
|
-
'@_length': '123456',
|
|
250
|
-
},
|
|
251
|
-
],
|
|
252
|
-
},
|
|
253
|
-
];
|
|
254
|
-
const result = podcastLoader['mapEpisodes'](items);
|
|
255
|
-
expect(result).toHaveLength(1);
|
|
256
|
-
expect(result[0]).toEqual({
|
|
257
|
-
title: 'Episode 1',
|
|
258
|
-
guid: 'ep1-guid',
|
|
259
|
-
linkUrl: 'https://example.com/ep1',
|
|
260
|
-
pubDate: 'Mon, 01 Jan 2024 12:00:00 GMT',
|
|
261
|
-
description: 'First episode',
|
|
262
|
-
durationInSeconds: '3600',
|
|
263
|
-
imageUrl: 'https://example.com/ep1.jpg',
|
|
264
|
-
explicit: true,
|
|
265
|
-
number: 1,
|
|
266
|
-
season: 1,
|
|
267
|
-
type: 'full',
|
|
268
|
-
enclosure: {
|
|
269
|
-
url: 'https://example.com/ep1.mp3',
|
|
270
|
-
type: 'audio/mpeg',
|
|
271
|
-
length: '123456',
|
|
272
|
-
},
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
it('should handle episodes with minimal fields', () => {
|
|
276
|
-
const items = [
|
|
277
|
-
{
|
|
278
|
-
title: 'Episode 1',
|
|
279
|
-
guid: { '#text': 'ep1-guid' },
|
|
280
|
-
},
|
|
281
|
-
];
|
|
282
|
-
const result = podcastLoader['mapEpisodes'](items);
|
|
283
|
-
expect(result).toHaveLength(1);
|
|
284
|
-
expect(result[0]).toEqual({
|
|
285
|
-
title: 'Episode 1',
|
|
286
|
-
guid: 'ep1-guid',
|
|
287
|
-
enclosure: undefined,
|
|
288
|
-
linkUrl: undefined,
|
|
289
|
-
pubDate: undefined,
|
|
290
|
-
description: undefined,
|
|
291
|
-
durationInSeconds: undefined,
|
|
292
|
-
imageUrl: undefined,
|
|
293
|
-
explicit: false,
|
|
294
|
-
number: undefined,
|
|
295
|
-
season: undefined,
|
|
296
|
-
type: undefined,
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
it('should handle explicit field as "no"', () => {
|
|
300
|
-
const items = [
|
|
301
|
-
{
|
|
302
|
-
title: 'Clean Episode',
|
|
303
|
-
guid: { '#text': 'ep-guid' },
|
|
304
|
-
'itunes:explicit': 'no',
|
|
305
|
-
},
|
|
306
|
-
];
|
|
307
|
-
const result = podcastLoader['mapEpisodes'](items);
|
|
308
|
-
expect(result[0].explicit).toBe(false);
|
|
309
|
-
});
|
|
310
|
-
it('should handle multiple episodes', () => {
|
|
311
|
-
const items = [
|
|
312
|
-
{
|
|
313
|
-
title: 'Episode 1',
|
|
314
|
-
guid: { '#text': 'ep1-guid' },
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
title: 'Episode 2',
|
|
318
|
-
guid: { '#text': 'ep2-guid' },
|
|
319
|
-
},
|
|
320
|
-
];
|
|
321
|
-
const result = podcastLoader['mapEpisodes'](items);
|
|
322
|
-
expect(result).toHaveLength(2);
|
|
323
|
-
expect(result[0].title).toBe('Episode 1');
|
|
324
|
-
expect(result[1].title).toBe('Episode 2');
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
describe('mapEnclosure', () => {
|
|
328
|
-
it('should map enclosure with all fields', () => {
|
|
329
|
-
const enclosure = [
|
|
330
|
-
{
|
|
331
|
-
'@_url': 'https://example.com/ep1.mp3',
|
|
332
|
-
'@_type': 'audio/mpeg',
|
|
333
|
-
'@_length': '123456',
|
|
334
|
-
},
|
|
335
|
-
];
|
|
336
|
-
const result = podcastLoader['mapEnclosure'](enclosure);
|
|
337
|
-
expect(result).toEqual({
|
|
338
|
-
url: 'https://example.com/ep1.mp3',
|
|
339
|
-
type: 'audio/mpeg',
|
|
340
|
-
length: '123456',
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
it('should return undefined when enclosure is undefined', () => {
|
|
344
|
-
const result = podcastLoader['mapEnclosure'](undefined);
|
|
345
|
-
expect(result).toBeUndefined();
|
|
346
|
-
});
|
|
347
|
-
it('should return undefined when enclosure array is empty', () => {
|
|
348
|
-
const result = podcastLoader['mapEnclosure']([]);
|
|
349
|
-
expect(result).toBeUndefined();
|
|
350
|
-
});
|
|
351
|
-
it('should handle enclosure with different media types', () => {
|
|
352
|
-
const enclosures = [
|
|
353
|
-
{
|
|
354
|
-
'@_url': 'https://example.com/ep1.m4a',
|
|
355
|
-
'@_type': 'audio/mp4',
|
|
356
|
-
'@_length': '654321',
|
|
357
|
-
},
|
|
358
|
-
];
|
|
359
|
-
const result = podcastLoader['mapEnclosure'](enclosures);
|
|
360
|
-
expect(result?.type).toBe('audio/mp4');
|
|
361
|
-
expect(result?.url).toBe('https://example.com/ep1.m4a');
|
|
362
|
-
});
|
|
363
|
-
});
|
|
364
|
-
});
|