Skip to content

Commit effaa2d

Browse files
committed
Fix ZeroClick ad display text
1 parent 8de1d15 commit effaa2d

2 files changed

Lines changed: 104 additions & 5 deletions

File tree

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { describe, expect, test } from 'bun:test'
2+
3+
import { createZeroClickProvider } from '../zeroclick'
4+
5+
import type { Logger } from '@codebuff/common/types/contracts/logger'
6+
7+
const logger: Logger = {
8+
debug: () => {},
9+
info: () => {},
10+
warn: () => {},
11+
error: () => {},
12+
}
13+
14+
describe('ZeroClick ad provider', () => {
15+
test('uses content as ad text and stores brand name as title', async () => {
16+
const provider = createZeroClickProvider({ apiKey: 'test-key' })
17+
const fetch = Object.assign(
18+
async () =>
19+
new Response(
20+
JSON.stringify([
21+
{
22+
id: 'offer-1',
23+
title:
24+
'Long product title that should not be used as the display label',
25+
subtitle: 'Subtitle that should not be included',
26+
content: 'Main offer description.',
27+
cta: 'Try it',
28+
clickUrl: 'https://zeroclick.example/click',
29+
brand: {
30+
name: 'Acme',
31+
url: null,
32+
iconUrl: 'https://example.com/icon.png',
33+
},
34+
},
35+
]),
36+
{
37+
status: 200,
38+
headers: { 'Content-Type': 'application/json' },
39+
},
40+
),
41+
{ preconnect: () => {} },
42+
) as typeof globalThis.fetch
43+
44+
const result = await provider.fetchAd({
45+
userId: 'user-1',
46+
userEmail: 'user@example.com',
47+
clientIp: '127.0.0.1',
48+
messages: [],
49+
testMode: true,
50+
logger,
51+
fetch,
52+
})
53+
54+
expect(result?.ads).toHaveLength(1)
55+
expect(result?.ads[0]).toMatchObject({
56+
adText: 'Main offer description.',
57+
title: 'Acme',
58+
cta: 'Try it',
59+
url: '',
60+
favicon: 'https://example.com/icon.png',
61+
clickUrl: 'https://zeroclick.example/click',
62+
impressionIds: ['offer-1'],
63+
})
64+
})
65+
66+
test('uses subtitle as ad text fallback when content is missing', async () => {
67+
const provider = createZeroClickProvider({ apiKey: 'test-key' })
68+
const fetch = Object.assign(
69+
async () =>
70+
new Response(
71+
JSON.stringify([
72+
{
73+
id: 'offer-1',
74+
title: 'Long product title',
75+
subtitle: 'Fallback subtitle description.',
76+
content: null,
77+
cta: 'Try it',
78+
clickUrl: 'https://zeroclick.example/click',
79+
brand: { name: 'Acme' },
80+
},
81+
]),
82+
{
83+
status: 200,
84+
headers: { 'Content-Type': 'application/json' },
85+
},
86+
),
87+
{ preconnect: () => {} },
88+
) as typeof globalThis.fetch
89+
90+
const result = await provider.fetchAd({
91+
userId: 'user-1',
92+
userEmail: 'user@example.com',
93+
clientIp: '127.0.0.1',
94+
messages: [],
95+
testMode: true,
96+
logger,
97+
fetch,
98+
})
99+
100+
expect(result?.ads[0]?.adText).toBe('Fallback subtitle description.')
101+
})
102+
})

web/src/lib/ad-providers/zeroclick.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,11 @@ function normalize(raw: ZeroClickOffer, servedId: string): NormalizedAd | null {
6666
if (!raw.id || !raw.clickUrl) return null
6767

6868
const title =
69+
raw.brand?.name?.trim() ||
6970
raw.title?.trim() ||
7071
raw.product?.title?.trim() ||
71-
raw.brand?.name?.trim() ||
7272
'Sponsored'
73-
const content = [raw.subtitle, raw.content]
74-
.map((part) => part?.trim())
75-
.filter(Boolean)
76-
.join(' ')
73+
const content = raw.content?.trim() || raw.subtitle?.trim() || ''
7774

7875
return {
7976
adText: content || title,

0 commit comments

Comments
 (0)