343 lines
11 KiB
TypeScript
343 lines
11 KiB
TypeScript
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import * as smartfeed from '../ts/index.js';
|
|
|
|
let testSmartFeed: smartfeed.Smartfeed;
|
|
|
|
tap.test('setup', async () => {
|
|
testSmartFeed = new smartfeed.Smartfeed();
|
|
});
|
|
|
|
tap.test('should create podcast with podcast:guid', async () => {
|
|
const podcast = testSmartFeed.createPodcastFeed({
|
|
domain: 'podcast2.example.com',
|
|
title: 'Podcast 2.0 Test',
|
|
description: 'Testing Podcast 2.0 features',
|
|
category: 'Technology',
|
|
company: 'Test Inc',
|
|
companyEmail: 'test@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'Technology',
|
|
itunesAuthor: 'Tech Author',
|
|
itunesOwner: { name: 'Tech Author', email: 'author@example.com' },
|
|
itunesImage: 'https://example.com/artwork.jpg',
|
|
itunesExplicit: false,
|
|
podcastGuid: '92f49cf0-db3e-5c17-8f11-9c5bd9e1f7ec',
|
|
});
|
|
|
|
expect(podcast).toBeInstanceOf(smartfeed.PodcastFeed);
|
|
expect(podcast.podcastOptions.podcastGuid).toEqual('92f49cf0-db3e-5c17-8f11-9c5bd9e1f7ec');
|
|
});
|
|
|
|
tap.test('should require podcast:guid', async () => {
|
|
let errorThrown = false;
|
|
try {
|
|
testSmartFeed.createPodcastFeed({
|
|
domain: 'test.com',
|
|
title: 'Test',
|
|
description: 'Test',
|
|
category: 'Test',
|
|
company: 'Test Inc',
|
|
companyEmail: 'test@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'Technology',
|
|
itunesAuthor: 'Author',
|
|
itunesOwner: { name: 'Owner', email: 'owner@example.com' },
|
|
itunesImage: 'https://example.com/image.jpg',
|
|
itunesExplicit: false,
|
|
// Missing podcastGuid
|
|
} as any);
|
|
} catch (error) {
|
|
errorThrown = true;
|
|
expect(error.message).toInclude('podcastGuid');
|
|
}
|
|
expect(errorThrown).toEqual(true);
|
|
});
|
|
|
|
tap.test('should create podcast with medium type', async () => {
|
|
const podcast = testSmartFeed.createPodcastFeed({
|
|
domain: 'music.example.com',
|
|
title: 'Music Podcast',
|
|
description: 'A music podcast',
|
|
category: 'Music',
|
|
company: 'Music Inc',
|
|
companyEmail: 'music@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'Music',
|
|
itunesAuthor: 'DJ',
|
|
itunesOwner: { name: 'DJ', email: 'dj@example.com' },
|
|
itunesImage: 'https://example.com/music.jpg',
|
|
itunesExplicit: false,
|
|
podcastGuid: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
|
|
podcastMedium: 'music',
|
|
});
|
|
|
|
expect(podcast.podcastOptions.podcastMedium).toEqual('music');
|
|
});
|
|
|
|
tap.test('should validate podcast medium type', async () => {
|
|
let errorThrown = false;
|
|
try {
|
|
testSmartFeed.createPodcastFeed({
|
|
domain: 'test.com',
|
|
title: 'Test',
|
|
description: 'Test',
|
|
category: 'Test',
|
|
company: 'Test Inc',
|
|
companyEmail: 'test@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'Technology',
|
|
itunesAuthor: 'Author',
|
|
itunesOwner: { name: 'Owner', email: 'owner@example.com' },
|
|
itunesImage: 'https://example.com/image.jpg',
|
|
itunesExplicit: false,
|
|
podcastGuid: 'test-guid-123',
|
|
podcastMedium: 'invalid' as any,
|
|
});
|
|
} catch (error) {
|
|
errorThrown = true;
|
|
expect(error.message).toInclude('medium must be one of');
|
|
}
|
|
expect(errorThrown).toEqual(true);
|
|
});
|
|
|
|
tap.test('should create locked podcast', async () => {
|
|
const podcast = testSmartFeed.createPodcastFeed({
|
|
domain: 'locked.example.com',
|
|
title: 'Locked Podcast',
|
|
description: 'A locked podcast',
|
|
category: 'News',
|
|
company: 'News Inc',
|
|
companyEmail: 'news@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'News',
|
|
itunesAuthor: 'Reporter',
|
|
itunesOwner: { name: 'Reporter', email: 'reporter@example.com' },
|
|
itunesImage: 'https://example.com/news.jpg',
|
|
itunesExplicit: false,
|
|
podcastGuid: 'locked-guid-456',
|
|
podcastLocked: true,
|
|
podcastLockOwner: 'owner@example.com',
|
|
});
|
|
|
|
expect(podcast.podcastOptions.podcastLocked).toEqual(true);
|
|
expect(podcast.podcastOptions.podcastLockOwner).toEqual('owner@example.com');
|
|
});
|
|
|
|
tap.test('should require lock owner when podcast is locked', async () => {
|
|
let errorThrown = false;
|
|
try {
|
|
testSmartFeed.createPodcastFeed({
|
|
domain: 'test.com',
|
|
title: 'Test',
|
|
description: 'Test',
|
|
category: 'Test',
|
|
company: 'Test Inc',
|
|
companyEmail: 'test@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'Technology',
|
|
itunesAuthor: 'Author',
|
|
itunesOwner: { name: 'Owner', email: 'owner@example.com' },
|
|
itunesImage: 'https://example.com/image.jpg',
|
|
itunesExplicit: false,
|
|
podcastGuid: 'test-guid-789',
|
|
podcastLocked: true,
|
|
// Missing podcastLockOwner
|
|
});
|
|
} catch (error) {
|
|
errorThrown = true;
|
|
expect(error.message).toInclude('lock owner');
|
|
expect(error.message).toInclude('required when podcast is locked');
|
|
}
|
|
expect(errorThrown).toEqual(true);
|
|
});
|
|
|
|
tap.test('should validate lock owner email format', async () => {
|
|
let errorThrown = false;
|
|
try {
|
|
testSmartFeed.createPodcastFeed({
|
|
domain: 'test.com',
|
|
title: 'Test',
|
|
description: 'Test',
|
|
category: 'Test',
|
|
company: 'Test Inc',
|
|
companyEmail: 'test@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'Technology',
|
|
itunesAuthor: 'Author',
|
|
itunesOwner: { name: 'Owner', email: 'owner@example.com' },
|
|
itunesImage: 'https://example.com/image.jpg',
|
|
itunesExplicit: false,
|
|
podcastGuid: 'test-guid-abc',
|
|
podcastLocked: true,
|
|
podcastLockOwner: 'not-an-email',
|
|
});
|
|
} catch (error) {
|
|
errorThrown = true;
|
|
expect(error.message).toInclude('email');
|
|
}
|
|
expect(errorThrown).toEqual(true);
|
|
});
|
|
|
|
tap.test('should include podcast:guid in RSS export', async () => {
|
|
const podcast = testSmartFeed.createPodcastFeed({
|
|
domain: 'export.example.com',
|
|
title: 'Export Test',
|
|
description: 'Testing RSS export',
|
|
category: 'Technology',
|
|
company: 'Test Inc',
|
|
companyEmail: 'test@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'Technology',
|
|
itunesAuthor: 'Author',
|
|
itunesOwner: { name: 'Author', email: 'author@example.com' },
|
|
itunesImage: 'https://example.com/artwork.jpg',
|
|
itunesExplicit: false,
|
|
podcastGuid: 'export-test-guid-123',
|
|
});
|
|
|
|
podcast.addEpisode({
|
|
title: 'Episode 1',
|
|
authorName: 'Author',
|
|
imageUrl: 'https://example.com/episode.jpg',
|
|
timestamp: Date.now(),
|
|
url: 'https://example.com/episode/1',
|
|
content: 'Test episode',
|
|
audioUrl: 'https://example.com/audio.mp3',
|
|
audioType: 'audio/mpeg',
|
|
audioLength: 1000000,
|
|
itunesDuration: 600,
|
|
});
|
|
|
|
const rss = podcast.exportPodcastRss();
|
|
|
|
expect(rss).toInclude('<podcast:guid>export-test-guid-123</podcast:guid>');
|
|
expect(rss).toInclude('xmlns:podcast="https://podcastindex.org/namespace/1.0"');
|
|
});
|
|
|
|
tap.test('should include podcast:medium in RSS export', async () => {
|
|
const podcast = testSmartFeed.createPodcastFeed({
|
|
domain: 'video.example.com',
|
|
title: 'Video Podcast',
|
|
description: 'A video podcast',
|
|
category: 'Video',
|
|
company: 'Video Inc',
|
|
companyEmail: 'video@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'Video',
|
|
itunesAuthor: 'Videographer',
|
|
itunesOwner: { name: 'Videographer', email: 'video@example.com' },
|
|
itunesImage: 'https://example.com/video.jpg',
|
|
itunesExplicit: false,
|
|
podcastGuid: 'video-guid-def',
|
|
podcastMedium: 'video',
|
|
});
|
|
|
|
const rss = podcast.exportPodcastRss();
|
|
|
|
expect(rss).toInclude('<podcast:medium>video</podcast:medium>');
|
|
});
|
|
|
|
tap.test('should default to podcast medium when not specified', async () => {
|
|
const podcast = testSmartFeed.createPodcastFeed({
|
|
domain: 'default.example.com',
|
|
title: 'Default Podcast',
|
|
description: 'Testing default medium',
|
|
category: 'Technology',
|
|
company: 'Test Inc',
|
|
companyEmail: 'test@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'Technology',
|
|
itunesAuthor: 'Author',
|
|
itunesOwner: { name: 'Author', email: 'author@example.com' },
|
|
itunesImage: 'https://example.com/artwork.jpg',
|
|
itunesExplicit: false,
|
|
podcastGuid: 'default-guid-ghi',
|
|
});
|
|
|
|
const rss = podcast.exportPodcastRss();
|
|
|
|
expect(rss).toInclude('<podcast:medium>podcast</podcast:medium>');
|
|
});
|
|
|
|
tap.test('should include podcast:locked in RSS export', async () => {
|
|
const podcast = testSmartFeed.createPodcastFeed({
|
|
domain: 'locked-export.example.com',
|
|
title: 'Locked Export Test',
|
|
description: 'Testing locked RSS export',
|
|
category: 'Technology',
|
|
company: 'Test Inc',
|
|
companyEmail: 'test@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'Technology',
|
|
itunesAuthor: 'Author',
|
|
itunesOwner: { name: 'Author', email: 'author@example.com' },
|
|
itunesImage: 'https://example.com/artwork.jpg',
|
|
itunesExplicit: false,
|
|
podcastGuid: 'locked-export-guid-jkl',
|
|
podcastLocked: true,
|
|
podcastLockOwner: 'lock@example.com',
|
|
});
|
|
|
|
const rss = podcast.exportPodcastRss();
|
|
|
|
expect(rss).toInclude('<podcast:locked owner="lock@example.com">yes</podcast:locked>');
|
|
});
|
|
|
|
tap.test('should include podcast:locked as "no" when not locked', async () => {
|
|
const podcast = testSmartFeed.createPodcastFeed({
|
|
domain: 'unlocked.example.com',
|
|
title: 'Unlocked Podcast',
|
|
description: 'Testing unlocked RSS export',
|
|
category: 'Technology',
|
|
company: 'Test Inc',
|
|
companyEmail: 'test@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'Technology',
|
|
itunesAuthor: 'Author',
|
|
itunesOwner: { name: 'Author', email: 'author@example.com' },
|
|
itunesImage: 'https://example.com/artwork.jpg',
|
|
itunesExplicit: false,
|
|
podcastGuid: 'unlocked-guid-mno',
|
|
podcastLocked: false,
|
|
});
|
|
|
|
const rss = podcast.exportPodcastRss();
|
|
|
|
expect(rss).toInclude('<podcast:locked>no</podcast:locked>');
|
|
});
|
|
|
|
tap.test('should support all medium types', async () => {
|
|
const mediums: Array<'podcast' | 'music' | 'video' | 'film' | 'audiobook' | 'newsletter' | 'blog'> = [
|
|
'podcast',
|
|
'music',
|
|
'video',
|
|
'film',
|
|
'audiobook',
|
|
'newsletter',
|
|
'blog',
|
|
];
|
|
|
|
for (const medium of mediums) {
|
|
const podcast = testSmartFeed.createPodcastFeed({
|
|
domain: `${medium}.example.com`,
|
|
title: `${medium} Test`,
|
|
description: `Testing ${medium} medium`,
|
|
category: 'Test',
|
|
company: 'Test Inc',
|
|
companyEmail: 'test@example.com',
|
|
companyDomain: 'https://example.com',
|
|
itunesCategory: 'Technology',
|
|
itunesAuthor: 'Author',
|
|
itunesOwner: { name: 'Author', email: 'author@example.com' },
|
|
itunesImage: 'https://example.com/artwork.jpg',
|
|
itunesExplicit: false,
|
|
podcastGuid: `${medium}-guid`,
|
|
podcastMedium: medium,
|
|
});
|
|
|
|
expect(podcast.podcastOptions.podcastMedium).toEqual(medium);
|
|
}
|
|
});
|
|
|
|
export default tap.start();
|