Skip to content
Open
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
50 changes: 45 additions & 5 deletions src/app/lib/uasApi/getRecentActivity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ describe('getRecentActivity', () => {
expect(result.total).toBe(0);
});

it('should handle missing metaData gracefully', async () => {
it('should exclude items with no metaData field', async () => {
const responseWithMissingMetaData = {
total: 1,
pagination: { startIndex: 0, itemsPerPage: 10 },
Expand All @@ -149,9 +149,8 @@ describe('getRecentActivity', () => {
resourceId: 'article1',
resourceType: 'article',
resourceDomain: 'world-service-news',
created: '2026-02-15T18:30:05Z',
created: '2026-07-01T11:43:21Z',
action: 'favourited',
metaData: {},
'@id': 'urn:bbc:world-service-news:article:article1',
} as UasActivityItem,
],
Expand All @@ -163,8 +162,49 @@ describe('getRecentActivity', () => {

const result = await getRecentActivity({});

expect(result.savedArticles[0].title).toBe('Untitled');
expect(result.savedArticles[0].description).toBe('BBC');
expect(result.savedArticles).toHaveLength(0);
});

it('should exclude items with null metaData and keep items with valid metaData', async () => {
const mixedResponse = {
total: 2,
pagination: { startIndex: 0, itemsPerPage: 10 },
items: [
{
activityType: 'favourites',
resourceId: 'article-no-metadata',
resourceType: 'article',
resourceDomain: 'world-service-news',
created: '2026-07-01T11:43:21Z',
action: 'favourited',
'@id': 'urn:bbc:world-service-news:article:article-no-metadata',
} as UasActivityItem,
{
activityType: 'favourites',
resourceId: 'article-with-metadata',
resourceType: 'article',
resourceDomain: 'world-service-news',
created: '2026-06-30T12:18:08Z',
action: 'favourited',
metaData: {
service: 'hindi',
articleId: 'article-with-metadata',
title: 'Valid Article',
locatorUrl: '/hindi/articles/article-with-metadata',
},
'@id': 'urn:bbc:world-service-news:article:article-with-metadata',
} as UasActivityItem,
],
};

mockUasApiRequest.mockResolvedValueOnce({
json: jest.fn().mockResolvedValueOnce(mixedResponse),
} as unknown as Response);

const result = await getRecentActivity({});

expect(result.savedArticles).toHaveLength(1);
expect(result.savedArticles[0].id).toBe('article-with-metadata');
});

it('should pass signal for abort control', async () => {
Expand Down
6 changes: 4 additions & 2 deletions src/app/lib/uasApi/getRecentActivity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface UasActivityItem {
resourceDomain: string;
created: string;
action: string;
metaData: {
metaData?: {
service?: string;
articleId?: string;
title?: string;
Expand Down Expand Up @@ -89,7 +89,9 @@ const getRecentActivity = async ({

const { items: allItems } = data;

const savedArticles = allItems.map(transformActivityToSavedArticle);
const savedArticles = allItems
.filter(item => item.metaData != null)
.map(transformActivityToSavedArticle);

return {
savedArticles,
Expand Down
113 changes: 113 additions & 0 deletions src/app/lib/uasApi/uasUtility.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import type { SaveArticlePageData } from '#app/lib/utilities/extractSaveArticleProps';
import { sanitiseMetadataString, buildCurrentMetadata } from './uasUtility';

const buildMockSaveArticlePageData = (
promoImageAltText: string,
headline = 'Test headline',
): SaveArticlePageData => ({
canonicalUrl: 'https://www.bbc.com/hindi/articles/cwy0pz7qydzo',
promoImage: 'https://ichef.test.bbci.co.uk/ace/ws/320/image.jpg',
promoImageAltText,
headline,
});

describe('sanitiseMetadataString', () => {
it('removes a trailing newline', () => {
expect(sanitiseMetadataString('शाहरुख़ ख़ान\n')).toBe('शाहरुख़ ख़ान');
});

it('removes a trailing carriage return + newline', () => {
expect(sanitiseMetadataString('Some text\r\n')).toBe('Some text');
});

it('collapses embedded newlines into a single space', () => {
expect(sanitiseMetadataString('Line one\nLine two')).toBe(
'Line one Line two',
);
});

it('trims leading and trailing whitespace', () => {
expect(sanitiseMetadataString(' hello ')).toBe('hello');
});

it('returns an empty string for undefined input', () => {
expect(sanitiseMetadataString(undefined)).toBe('');
});

it('returns an empty string for an empty string input', () => {
expect(sanitiseMetadataString('')).toBe('');
});

it('leaves clean strings unchanged', () => {
expect(sanitiseMetadataString('Normal alt text')).toBe('Normal alt text');
});
});

describe('buildCurrentMetadata', () => {
it('strips trailing newline from promoImageAltText before sending to UAS', () => {
const saveArticlePageData = buildMockSaveArticlePageData('शाहरुख़ ख़ान\n');

const metadata = buildCurrentMetadata(saveArticlePageData, {
articleId: 'cwy0pz7qydzo',
service: 'hindi',
});

expect(metadata.promoImageAltText).toBe('शाहरुख़ ख़ान');
});

it('collapses embedded newlines in promoImageAltText into a single space', () => {
const saveArticlePageData = buildMockSaveArticlePageData(
'Line one\nLine two\n',
);

const metadata = buildCurrentMetadata(saveArticlePageData, {
articleId: 'cwy0pz7qydzo',
service: 'hindi',
});

expect(metadata.promoImageAltText).toBe('Line one Line two');
});

it('handles clean alt text without modification', () => {
const saveArticlePageData = buildMockSaveArticlePageData('Clean alt text');

const metadata = buildCurrentMetadata(saveArticlePageData, {
articleId: 'cwy0pz7qydzo',
service: 'hindi',
});

expect(metadata.promoImageAltText).toBe('Clean alt text');
});

it('collapses whitespace in the title before sending to UAS', () => {
const saveArticlePageData = buildMockSaveArticlePageData(
'Image description',
'Hindi\nArticle',
);

const metadata = buildCurrentMetadata(saveArticlePageData, {
articleId: 'cwy0pz7qydzo',
service: 'hindi',
});

expect(metadata.title).toBe('Hindi Article');
});

it('includes all expected metadata fields', () => {
const saveArticlePageData =
buildMockSaveArticlePageData('Image description');

const metadata = buildCurrentMetadata(saveArticlePageData, {
articleId: 'cwy0pz7qydzo',
service: 'hindi',
});

expect(metadata).toMatchObject({
articleId: 'cwy0pz7qydzo',
service: 'hindi',
title: 'Test headline',
locatorUrl: 'https://www.bbc.com/hindi/articles/cwy0pz7qydzo',
promoImageAltText: 'Image description',
});
});
});
8 changes: 6 additions & 2 deletions src/app/lib/uasApi/uasUtility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ interface MetadataComparisonResult {
* @param service - BBC service name (e.g., 'arabic', 'portuguese')
* @returns Object with tracked metadata fields
*/
const sanitiseMetadataString = (value: string | null | undefined): string =>
value?.replace(/\s+/g, ' ').trim() ?? '';

const buildCurrentMetadata = (
saveArticlePageData: SaveArticlePageData,
{ articleId, service }: { articleId: string; service: Services },
Expand All @@ -55,9 +58,9 @@ const buildCurrentMetadata = (
return {
articleId,
service,
title: headline,
title: sanitiseMetadataString(headline),
promoImage,
promoImageAltText,
promoImageAltText: sanitiseMetadataString(promoImageAltText),
locatorUrl: canonicalUrl,
};
};
Expand Down Expand Up @@ -106,6 +109,7 @@ export {
createFavouritesPayload,
buildCurrentMetadata,
compareMetadataWithSaved,
sanitiseMetadataString,
};

export type { MetadataComparisonResult };
Loading