diff --git a/src/api/desktop/recommendations/response.spec.ts b/src/api/desktop/recommendations/response.spec.ts index 9c3b388aa..1047bd96a 100644 --- a/src/api/desktop/recommendations/response.spec.ts +++ b/src/api/desktop/recommendations/response.spec.ts @@ -2,7 +2,7 @@ import Ajv, { DefinedError } from 'ajv'; import OpenApiSpec from '../../OpenAPISpec'; import Recommendations from '../../../graphql-proxy/recommendations/recommendations'; -import { responseTransformer } from './response'; +import { appendUtmSource, responseTransformer } from './response'; import { WebAuth } from '../../../auth/types'; jest.mock('../../../graphql-proxy/recommendations/recommendations'); @@ -47,9 +47,38 @@ describe('response', () => { if (valid) { // any additional expectations can be defined here expect(res.data.length).toEqual(30); + expect(res.data[0].url.endsWith('utm_source=pocket-newtab-bff')); } else { throw validate.errors; } }); }); + + describe('appendUtmSource', () => { + it('should add a utm_source query parameter when input URL does not have any query parameters', () => { + const url = 'https://example.com'; + const expected = 'https://example.com/?utm_source=pocket-newtab-bff'; + expect(appendUtmSource(url)).toBe(expected); + }); + + it('should add a utm_source query parameter when the input URL already has a query parameter', () => { + const url = 'https://example.com?foo=bar'; + const expected = + 'https://example.com/?foo=bar&utm_source=pocket-newtab-bff'; + expect(appendUtmSource(url)).toBe(expected); + }); + + it('should add utm_source query parameter when the input URL ends with a fragment', () => { + const url = 'https://example.com#my-fragment'; + const expected = + 'https://example.com/?utm_source=pocket-newtab-bff#my-fragment'; + expect(appendUtmSource(url)).toBe(expected); + }); + + it('should override utm_source query parameter if the url already contains utm_source', () => { + const url = 'https://example.com/?utm_source=fgfeed'; + const expected = 'https://example.com/?utm_source=pocket-newtab-bff'; + expect(appendUtmSource(url)).toBe(expected); + }); + }); }); diff --git a/src/api/desktop/recommendations/response.ts b/src/api/desktop/recommendations/response.ts index c8487183b..faead8496 100644 --- a/src/api/desktop/recommendations/response.ts +++ b/src/api/desktop/recommendations/response.ts @@ -12,13 +12,26 @@ export type RecommendationsResponse = paths['/desktop/v1/recommendations']['get']['responses']['200']['content']['application/json']; type Recommendation = components['schemas']['Recommendation']; +export const appendUtmSource = (url: string): string => { + const urlObject = new URL(url); + const searchParams = new URLSearchParams(urlObject.search); + + // Set a static utm_source to attribute traffic to the new NewTab markets. + // TODO: [DIS-803] Make utm_source unique per ScheduledSurface, + // before migrating Firefox Release en-US to this API. + searchParams.set('utm_source', 'pocket-newtab-bff'); + urlObject.search = searchParams.toString(); + + return urlObject.toString(); +}; + export const mapRecommendation = ( recommendation: GraphRecommendation ): Recommendation => { return { __typename: 'Recommendation', tileId: recommendation.tileId, - url: recommendation.corpusItem.url, + url: appendUtmSource(recommendation.corpusItem.url), title: recommendation.corpusItem.title, excerpt: recommendation.corpusItem.excerpt, publisher: recommendation.corpusItem.publisher,