Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
152 changes: 152 additions & 0 deletions src/app/lib/uasApi/uasUtility.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import type { Article } from '#app/models/types/optimo';
import { sanitiseMetadataString, buildCurrentMetadata } from './uasUtility';

jest.mock('#app/lib/utilities/ichefURL', () =>
jest.fn(() => 'https://ichef.test.bbci.co.uk/ace/ws/320/image.jpg'),
);

const buildMockArticleWithAltText = (altText: string): Article =>
({
metadata: {
locators: {
canonicalUrl: 'https://www.bbc.com/hindi/articles/cwy0pz7qydzo',
},
},
content: {
model: {
blocks: [
{
type: 'headline',
model: {
blocks: [
{
model: {
blocks: [{ model: { text: 'Test headline' } }],
},
},
],
},
},
],
},
},
promo: {
images: {
defaultPromoImage: {
blocks: [
{
type: 'altText',
model: {
blocks: [
{
type: 'text',
model: {
blocks: [
{
type: 'paragraph',
model: { text: altText },
},
],
},
},
],
},
},
{
type: 'rawImage',
model: {
locator: '5bde/live/e4af80b0-6f4c-11f0-8dbd-f3d32ebd3327.jpg',
originCode: 'cpsprodpb',
width: 1024,
height: 576,
},
},
],
},
},
},
}) as unknown as Article;

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('removes embedded newlines', () => {
expect(sanitiseMetadataString('Line one\nLine two')).toBe(
'Line oneLine 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 article = buildMockArticleWithAltText('शाहरुख़ ख़ान\n');

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

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

it('strips embedded newlines from promoImageAltText', () => {
const article = buildMockArticleWithAltText('Line one\nLine two\n');

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

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

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

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

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

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

const metadata = buildCurrentMetadata(article, {
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',
});
});
});
6 changes: 5 additions & 1 deletion src/app/lib/uasApi/uasUtility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ const extractHeadlineFromBlocks = (
* @param service - BBC service name (e.g., 'arabic', 'portuguese')
* @returns Object with tracked metadata fields
*/
const sanitiseMetadataString = (value: string | undefined): string =>
value?.replace(/[\r\n]/g, '').trim() ?? '';
Comment thread
elvinasv marked this conversation as resolved.
Outdated

const buildCurrentMetadata = (
articlePageData: Article,
{ articleId, service }: { articleId: string; service: Services },
Expand All @@ -104,7 +107,7 @@ const buildCurrentMetadata = (
service,
title: headline,
Comment thread
LukasFrm marked this conversation as resolved.
Outdated
promoImage: buildPromoImageUrl(promoImage),
promoImageAltText: promoImage?.altText,
promoImageAltText: sanitiseMetadataString(promoImage?.altText),
locatorUrl: articlePageData?.metadata?.locators?.canonicalUrl ?? '',
};
};
Expand Down Expand Up @@ -156,6 +159,7 @@ export {
buildCurrentMetadata,
compareMetadataWithSaved,
extractHeadlineFromBlocks,
sanitiseMetadataString,
};

export type { MetadataComparisonResult };
Loading