Skip to content

fix(tiktok-pixel): use TikTok's array snippet protocol#786

Merged
harlan-zw merged 2 commits into
mainfrom
fix/tiktok-pixel-array-protocol
May 21, 2026
Merged

fix(tiktok-pixel): use TikTok's array snippet protocol#786
harlan-zw merged 2 commits into
mainfrom
fix/tiktok-pixel-array-protocol

Conversation

@harlan-zw
Copy link
Copy Markdown
Collaborator

🔗 Linked issue

Resolves #785

❓ Type of change

  • 📖 Documentation
  • 🐞 Bug fix
  • 👌 Enhancement
  • ✨ New feature
  • 🧹 Chore
  • ⚠️ Breaking change

📚 Description

useScriptTikTokPixel's clientInit built window.ttq with the Facebook fbq callable protocol (ttq.callMethod / ttq.queue) and called a bogus ttq('init', id). TikTok's events.js consumes the official array protocol: ttq is an Array of [method, ...args] tuples with ttq.methods / setAndDefer and per-pixel _i/_t/_o scaffolding.

clientInit now builds the array protocol. To stay backwards compatible, use() returns a callable adapter so both proxy.ttq('page') (legacy) and proxy.ttq.page() work, forwarding to the live window.ttq array.

Adds an e2e suite + fixture (test/fixtures/tiktok-pixel) asserting the array shape (no callMethod/queue leftovers, _i._u scaffolding present) and that an explicitly-tracked event is delivered to /api/v2/pixel.

Note: against TikTok's current events.js, the old fbq stub still happened to deliver events (its events.js also drains ttq.queue). This change aligns with TikTok's documented base code and removes the invalid init call, which is the correct, future-proof fix regardless.

clientInit built window.ttq with the Facebook fbq callable protocol
(ttq.callMethod / ttq.queue) and called the bogus ttq('init', id).
TikTok's events.js consumes the official array protocol: ttq is an
Array of [method, ...args] tuples with ttq.methods / setAndDefer and
per-pixel _i/_t/_o scaffolding.

clientInit now builds the array protocol. use() returns a callable
adapter so both proxy.ttq('page') (legacy) and proxy.ttq.page() keep
working, forwarding to the live window.ttq array.

Adds an e2e suite + fixture asserting the array shape and that an
explicitly-tracked event reaches /api/v2/pixel.

Resolves #785
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
scripts-playground Error Error May 21, 2026 1:59am
Comment thread test/e2e/tiktok-pixel.test.ts Fixed
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 21, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@nuxt/scripts@786

commit: 3b7e306

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 27345e2b-f85a-438f-9a30-81ecee882137

📥 Commits

Reviewing files that changed from the base of the PR and between fe7737d and 3b7e306.

📒 Files selected for processing (1)
  • test/e2e/tiktok-pixel.test.ts

📝 Walkthrough

Walkthrough

This PR replaces the fbq-style callable ttq with TikTok's array-based snippet protocol: new Ttq types, a typed TtqArray global, createTtqAdapter and TTQ_METHODS dispatcher, and client scaffolding that initializes window.ttq as an array with methods, per-pixel _i/_t/_o, consent application, and queued page() calls. It also adds a Nuxt fixture, expanded unit/type checks, and a comprehensive E2E suite that verifies protocol shape, compatibility with both push and method forms, script URL params, and conditional delivery capture.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(tiktok-pixel): use TikTok's array snippet protocol' clearly and specifically summarizes the main change: replacing the Facebook fbq-style protocol with TikTok's official array-based snippet protocol.
Description check ✅ Passed The description is well-related to the changeset, explaining the rationale for switching from fbq callable protocol to TikTok's array protocol, backwards compatibility approach, and test coverage added.
Linked Issues check ✅ Passed The code changes comprehensively address all major requirements from issue #785: implements TikTok's array protocol with proper scaffolding (_i/_t/_o), deferred methods (methods/setAndDefer), removes fbq-style callMethod/queue, maintains backwards compatibility with callable adapter, and adds comprehensive e2e tests validating the array shape and event delivery to /api/v2/pixel.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the linked issue #785 objectives: registry type updates, runtime implementation of array protocol, test fixtures, e2e tests, and related unit test updates. No unrelated or out-of-scope changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/tiktok-pixel-array-protocol

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
test/types/types.test-d.ts (1)

187-191: ⚡ Quick win

Add explicit event_id coverage for ProxyTtq['track'] method form.

This block currently proves proxy callable + method non-event_id, but not method-form event_id overload preservation.

Suggested test addition
   it('proxy.ttq preserves both call forms', () => {
     type ProxyTtq = ReturnType<typeof useScriptTikTokPixel>['proxy']['ttq']
     expectTypeOf<ProxyTtq>().toBeCallableWith('track', 'Purchase', { value: 10 }, { event_id: 'abc' })
+    expectTypeOf<ProxyTtq['track']>().toBeCallableWith('Purchase', { value: 10 }, { event_id: 'abc' })
     expectTypeOf<ProxyTtq['track']>().toBeCallableWith('StartTrial')
   })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/types/types.test-d.ts` around lines 187 - 191, Add a type-level
assertion that the method-form track on the ProxyTtq preserves the event_id
overload: inside the same test block that defines ProxyTtq (from
useScriptTikTokPixel), add an
expectTypeOf<ProxyTtq['track']>().toBeCallableWith('StartTrial', /* event data
or undefined */ undefined, { event_id: 'abc' }) (or equivalent call shape
matching the track signature) so the method-form event_id overload is explicitly
covered.
test/e2e/tiktok-pixel.test.ts (1)

118-133: ⚡ Quick win

Add a real callable-adapter assertion or rename this test.

Lines 131–132 validate method + push forms on window.ttq, but not the legacy callable adapter path (proxy.ttq('track', ...)) mentioned in the test title/comments. This leaves the stated backward-compat path unverified.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/tiktok-pixel.test.ts` around lines 118 - 133, Test title and
comments claim to verify the legacy callable adapter path (proxy.ttq('track',
...)) but the test only exercises ttq.track and ttq.push; update the test to
either (A) actually exercise the callable adapter by invoking proxy.ttq('track',
'ViewContent', { content_id: 'callable-adapter' }) inside the page.evaluate
block and assert window.ttq length/contents changed accordingly (use the
existing ttq, before/after and examine the appended tuple), or (B) if you don't
intend to test proxy.ttq, rename the test and adjust the comment to reflect that
it only verifies method and push forms (update the it(...) title and surrounding
comment). Ensure references to proxy.ttq, window.ttq, ttq.track and ttq.push are
used so the correct code paths are covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/e2e/tiktok-pixel.test.ts`:
- Around line 58-65: The current test grabs a script src string via
page.evaluate and asserts substrings; instead parse the found src into a URL
object and assert url.hostname === 'analytics.tiktok.com' and url.pathname ===
'/i18n/pixel/events.js' (or use startsWith for the pathname if query parts
vary), while keeping existing checks for query params like 'sdkid=TEST_PIXEL_ID'
and 'lib=ttq'; update the assertion block around the page.evaluate result (the
src variable in test/e2e/tiktok-pixel.test.ts) to construct new URL(src) and
perform hostname/pathname assertions rather than substring matching.

---

Nitpick comments:
In `@test/e2e/tiktok-pixel.test.ts`:
- Around line 118-133: Test title and comments claim to verify the legacy
callable adapter path (proxy.ttq('track', ...)) but the test only exercises
ttq.track and ttq.push; update the test to either (A) actually exercise the
callable adapter by invoking proxy.ttq('track', 'ViewContent', { content_id:
'callable-adapter' }) inside the page.evaluate block and assert window.ttq
length/contents changed accordingly (use the existing ttq, before/after and
examine the appended tuple), or (B) if you don't intend to test proxy.ttq,
rename the test and adjust the comment to reflect that it only verifies method
and push forms (update the it(...) title and surrounding comment). Ensure
references to proxy.ttq, window.ttq, ttq.track and ttq.push are used so the
correct code paths are covered.

In `@test/types/types.test-d.ts`:
- Around line 187-191: Add a type-level assertion that the method-form track on
the ProxyTtq preserves the event_id overload: inside the same test block that
defines ProxyTtq (from useScriptTikTokPixel), add an
expectTypeOf<ProxyTtq['track']>().toBeCallableWith('StartTrial', /* event data
or undefined */ undefined, { event_id: 'abc' }) (or equivalent call shape
matching the track signature) so the method-form event_id overload is explicitly
covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 59376210-4816-432e-b4f2-2a95620971a7

📥 Commits

Reviewing files that changed from the base of the PR and between 0829ee0 and fe7737d.

📒 Files selected for processing (12)
  • package.json
  • packages/script/src/registry-types.json
  • packages/script/src/runtime/registry/tiktok-pixel.ts
  • test/e2e/tiktok-pixel.test.ts
  • test/fixtures/tiktok-pixel/app.vue
  • test/fixtures/tiktok-pixel/nuxt.config.ts
  • test/fixtures/tiktok-pixel/package.json
  • test/fixtures/tiktok-pixel/pages/index.vue
  • test/fixtures/tiktok-pixel/pages/tiktok.vue
  • test/fixtures/tiktok-pixel/tsconfig.json
  • test/nuxt-runtime/consent-default.nuxt.test.ts
  • test/types/types.test-d.ts
Comment on lines +58 to +65
const src = await page.evaluate(() =>
Array.from(document.querySelectorAll<HTMLScriptElement>('script[src]'))
.map(s => s.src)
.find(s => s.includes('analytics.tiktok.com')),
)
expect(src).toMatch(/analytics\.tiktok\.com\/i18n\/pixel\/events\.js/)
expect(src).toMatch(/sdkid=TEST_PIXEL_ID/)
expect(src).toMatch(/lib=ttq/)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Strengthen URL assertions with parsed hostname/path checks.

Line 61 currently accepts any URL containing analytics.tiktok.com as a substring, which can pass with the wrong host. Parse the URL and assert hostname/pathname directly.

Proposed fix
       const src = await page.evaluate(() =>
         Array.from(document.querySelectorAll<HTMLScriptElement>('script[src]'))
           .map(s => s.src)
-          .find(s => s.includes('analytics.tiktok.com')),
+          .find(s => s.includes('/i18n/pixel/events.js')),
       )
-      expect(src).toMatch(/analytics\.tiktok\.com\/i18n\/pixel\/events\.js/)
-      expect(src).toMatch(/sdkid=TEST_PIXEL_ID/)
-      expect(src).toMatch(/lib=ttq/)
+      if (!src) {
+        throw new Error('TikTok events.js script not found')
+      }
+      const parsed = new URL(src)
+      expect(parsed.hostname).toBe('analytics.tiktok.com')
+      expect(parsed.pathname).toBe('/i18n/pixel/events.js')
+      expect(parsed.searchParams.get('sdkid')).toBe('TEST_PIXEL_ID')
+      expect(parsed.searchParams.get('lib')).toBe('ttq')
🧰 Tools
🪛 GitHub Check: CodeQL

[failure] 61-61: Incomplete URL substring sanitization
'analytics.tiktok.com' can be anywhere in the URL, and arbitrary hosts may come before or after it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/tiktok-pixel.test.ts` around lines 58 - 65, The current test grabs a
script src string via page.evaluate and asserts substrings; instead parse the
found src into a URL object and assert url.hostname === 'analytics.tiktok.com'
and url.pathname === '/i18n/pixel/events.js' (or use startsWith for the pathname
if query parts vary), while keeping existing checks for query params like
'sdkid=TEST_PIXEL_ID' and 'lib=ttq'; update the assertion block around the
page.evaluate result (the src variable in test/e2e/tiktok-pixel.test.ts) to
construct new URL(src) and perform hostname/pathname assertions rather than
substring matching.
CodeQL js/incomplete-url-substring-sanitization: parse the URL and
compare hostname instead of String.includes.
@harlan-zw harlan-zw merged commit f46ccb6 into main May 21, 2026
18 of 19 checks passed
@harlan-zw harlan-zw deleted the fix/tiktok-pixel-array-protocol branch May 21, 2026 02:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants