Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions src/backend/services/resource_mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type ResourceRow = {
updatedAt?: DateTime | string | null;
};

export const extractResourceUrl = (
export const extractResourceContent = (
type: string,
bundle: ResourceBundle,
): string | undefined => {
Expand All @@ -29,22 +29,20 @@ export const extractResourceUrl = (
return 'video' in bundle ? bundle.video.url : undefined;
case 'url':
return 'url' in bundle ? bundle.url : undefined;
case 'text':
return 'content' in bundle ? bundle.content : undefined;
default:
return undefined;
}
};

const formatResourceDate = (
value: DateTime | string | undefined | null,
): string => {
const formatResourceDate = (value: DateTime | string | undefined | null): string => {
if (!value) return '';
if (typeof value === 'string') return value.split('T')[0];
return value.toISODate() ?? value.toISO()?.split('T')[0] ?? '';
};

const formatResourceDateTime = (
value: DateTime | string | undefined | null,
): string => {
const formatResourceDateTime = (value: DateTime | string | undefined | null): string => {
if (!value) return '';
if (typeof value === 'string') return value;
return value.toUTC().toISO() ?? value.toUTC().toString();
Expand All @@ -58,7 +56,7 @@ export const toResourceItem = (model: ResourceRow): ResourceItem => {
title: model.title,
type: model.type as ResourceType,
imageUrl: model.imageUrl,
url: extractResourceUrl(model.type, bundle),
content: extractResourceContent(model.type, bundle),
label: model.label,
visibility: model.visibility as VisibilityType,
description: model.description,
Expand Down
33 changes: 22 additions & 11 deletions src/frontend/test/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4356,6 +4356,7 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
title: 'Introduction to Biblical Archaeology',
type: 'video',
imageUrl: 'https://images.unsplash.com/photo-1574269909862-7e1d70bb8078?w=400',
content: 'https://example.com/intro-archaeology.mp4',
label: 'Supplementary Videos',
visibility: 'public',
description: 'A comprehensive overview of archaeological findings in the Holy Land',
Expand All @@ -4364,7 +4365,8 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
id: 'r2',
title: 'The Historical Jesus: Scholarly Perspectives',
type: 'text',
url: 'https://example.com/historical-jesus',
content:
'## Historical Evidence\n\nThis article surveys scholarly approaches to the historical Jesus, examining primary sources and archaeological context.',
label: 'Academic Reading',
visibility: 'public',
description:
Expand All @@ -4374,7 +4376,8 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
id: 'r3',
title: 'Understanding the Gospels',
type: 'text',
url: 'https://example.com/gospels.pdf',
content:
'## The Four Gospels\n\nMatthew, Mark, Luke, and John each offer distinct perspectives on the life and ministry of Jesus.',
label: 'Supplementary Reading',
visibility: 'public',
description: 'A detailed guide to the four Gospels and their contexts',
Expand All @@ -4383,7 +4386,7 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
id: 'r4',
title: 'Early Church History Timeline',
type: 'url',
url: 'https://example.com/timeline',
content: 'https://example.com/timeline',
label: 'Reference Materials',
visibility: 'public',
description: 'Interactive timeline of the first 500 years of Christianity',
Expand All @@ -4393,6 +4396,7 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
title: 'Hebrew Language Basics',
type: 'video',
imageUrl: 'https://images.unsplash.com/photo-1456513080510-7bf3a84b82f8?w=400',
content: 'https://example.com/hebrew-basics.mp4',
label: 'Language Resources',
visibility: 'public',
description: 'Learn the basics of Biblical Hebrew',
Expand All @@ -4402,6 +4406,7 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
title: 'Old Testament Survey',
type: 'video',
imageUrl: 'https://images.unsplash.com/photo-1516979187457-637abb4f9353?w=400',
content: 'https://example.com/ot-survey.mp4',
label: 'Supplementary Videos',
visibility: 'public',
description: 'Comprehensive overview of Old Testament books and themes',
Expand All @@ -4410,7 +4415,8 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
id: 'r7',
title: 'The Sermon on the Mount Study Guide',
type: 'text',
url: 'https://example.com/sermon-mount.pdf',
content:
'## Matthew 5–7\n\nStudy questions and reflection prompts for the Sermon on the Mount.',
label: 'Study Guides',
visibility: 'leaders',
description: 'In-depth study guide for Matthew 5-7',
Expand All @@ -4420,6 +4426,7 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
title: 'Church History Documentary Series',
type: 'video',
imageUrl: 'https://images.unsplash.com/photo-1519389950473-47ba0277781c?w=400',
content: 'https://example.com/church-history-series.mp4',
label: 'Supplementary Videos',
visibility: 'public',
description: '10-part series on the history of Christianity',
Expand All @@ -4428,7 +4435,7 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
id: 'r9',
title: 'Biblical Greek Resources',
type: 'url',
url: 'https://example.com/greek',
content: 'https://example.com/greek',
label: 'Language Resources',
visibility: 'public',
description: 'Collection of tools for learning Biblical Greek',
Expand All @@ -4437,7 +4444,7 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
id: 'r10',
title: 'Theological Dictionary',
type: 'url',
url: 'https://example.com/dictionary',
content: 'https://example.com/dictionary',
label: 'Reference Materials',
visibility: 'public',
description: 'Comprehensive theological terms and definitions',
Expand All @@ -4446,7 +4453,8 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
id: 'r11',
title: 'Leader Discussion Guide',
type: 'text',
url: 'https://example.com/leader-guide.pdf',
content:
'## Facilitator Notes\n\nDiscussion questions and leader prompts for small group sessions.',
label: 'Study Guides',
visibility: 'leaders',
description: 'Facilitator notes and discussion questions',
Expand All @@ -4455,7 +4463,8 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
id: 'r12',
title: 'Parables of Jesus Explained',
type: 'text',
url: 'https://example.com/parables',
content:
'## Major Parables\n\nAnalysis of the parables of the sower, the prodigal son, and the good Samaritan.',
label: 'Academic Reading',
visibility: 'public',
description: 'Analysis of major parables in the Gospels',
Expand All @@ -4466,6 +4475,7 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
'Archaeological Discoveries and Their Impact on New Testament Historical Studies: A Case study of the Gospel of Luke and Acts while referencing the Gospel of John and ignoring the Gospel of Matthew',
type: 'video',
imageUrl: 'https://images.unsplash.com/photo-1574269909862-7e1d70bb8078?w=400',
content: 'https://example.com/archaeology-nt-studies.mp4',
label: 'Supplementary Videos',
visibility: 'public',
description:
Expand All @@ -4475,7 +4485,8 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
id: 'r14',
title: 'A Critical Introduction to the Synoptic Problem and Gospel Source Criticism',
type: 'text',
url: 'https://example.com/synoptic-problem',
content:
'## Overview\n\nThis article introduces the Synoptic Problem and compares major scholarly hypotheses.\n\n- Two-Source hypothesis\n- Farrer hypothesis\n- Augustinian hypothesis',
label: 'Academic Reading',
visibility: 'guests',
description:
Expand All @@ -4486,7 +4497,7 @@ const availableResourceItems: Omit<ResourceItem, 'updatedAt'>[] = [
title:
'Patristic Writings on Scripture, Tradition, and the Formation of the Biblical Canon',
type: 'url',
url: 'https://example.com/patristic-canon',
content: 'https://example.com/patristic-canon',
label: 'Reference Materials',
visibility: 'leaders',
description:
Expand Down Expand Up @@ -4639,7 +4650,7 @@ export const mockEditUrlLinkResource: ResourceEditProps = toEditResource(
description: availableResources[3].description ?? '',
label: availableResources[3].label ?? '',
visibility: availableResources[3].visibility,
url: availableResources[3].url ?? 'https://example.com/timeline',
url: availableResources[3].content ?? 'https://example.com/timeline',
},
);

Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export interface ResourceItem {
title: string;
type: ResourceType;
imageUrl?: string | null;
url?: string;
content?: string;
label: string | null;
visibility: VisibilityType;
description?: string | null;
Expand Down
20 changes: 10 additions & 10 deletions tests/unit/resourceService.spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { test, expect } from '@playwright/test';
import { DateTime } from 'luxon';
import {
extractResourceUrl,
extractResourceContent,
toResourceItem,
toResourceIndexItem,
} from '../../src/backend/services/resource_mapper.js';
import { compareResourcesByRecentlyEdited } from '../../src/frontend/stories/components/resource-utils';
import type { LinkBundle, TextBundle, VideoBundle } from '../../src/types.js';

test.describe('Resource mapper', () => {
test('extractResourceUrl returns video url from bundle', () => {
test('extractResourceContent returns video url from bundle', () => {
const bundle: VideoBundle = { video: { url: 'https://example.com/v.mp4' } };
expect(extractResourceUrl('video', bundle)).toBe('https://example.com/v.mp4');
expect(extractResourceContent('video', bundle)).toBe('https://example.com/v.mp4');
});

test('extractResourceUrl returns url from bundle', () => {
test('extractResourceContent returns url from bundle', () => {
const bundle: LinkBundle = { url: 'https://example.com/info' };
expect(extractResourceUrl('url', bundle)).toBe('https://example.com/info');
expect(extractResourceContent('url', bundle)).toBe('https://example.com/info');
});

test('extractResourceUrl returns undefined for text type', () => {
test('extractResourceContent returns content from text bundle', () => {
const bundle: TextBundle = { content: 'Hello world' };
expect(extractResourceUrl('text', bundle)).toBeUndefined();
expect(extractResourceContent('text', bundle)).toBe('Hello world');
});

test('toResourceDto maps model fields to Resource DTO', () => {
Expand All @@ -42,7 +42,7 @@ test.describe('Resource mapper', () => {
title: 'Test Resource',
type: 'url',
imageUrl: 'https://example.com/img.jpg',
url: 'https://example.com/link',
content: 'https://example.com/link',
label: 'Reading',
visibility: 'guests',
description: 'A description',
Expand All @@ -65,7 +65,7 @@ test.describe('Resource mapper', () => {

expect(dto.label).toBeNull();
expect(dto.description).toBeNull();
expect(dto.url).toBeUndefined();
expect(dto.content).toBe('Body text');
expect(dto.type).toBe('text');
expect(dto.updatedAt).toBe('2024-02-12T08:00:00.000Z');
});
Expand All @@ -86,7 +86,7 @@ test.describe('Resource mapper', () => {

expect(item.createdAt).toBe('2024-01-15');
expect(item.updatedAt).toBe('2024-03-01T12:00:00.000Z');
expect(item.url).toBe('https://example.com/v.mp4');
expect(item.content).toBe('https://example.com/v.mp4');
});

test('toResourceIndexItem handles string timestamps', () => {
Expand Down
Loading