Skip to content
Merged
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
2 changes: 1 addition & 1 deletion apps/nextjs/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function RootLayout({
domainsConfig={{
refer: 'getacme.link',
site: 'getacme.link',
outbound: 'example.com,other.com,sub.example.com',
outbound: 'example.com,other.com,sub.example.com,*.wildcard.com',
}}
scriptProps={{
src: DUB_ANALYTICS_SCRIPT_URL,
Expand Down
9 changes: 9 additions & 0 deletions apps/nextjs/app/outbound/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ export default function Outbound() {
<a href="https://sub.example.com">Subdomain Link</a>
<a href="https://other.example.com">Other Subdomain Link</a>
<a href="https://www.sub.example.com">WWW Subdomain Link</a>

{/* Wildcard domain test links */}
<a href="https://api.wildcard.com">Wildcard API Link</a>
<a href="https://admin.wildcard.com">Wildcard Admin Link</a>
<a href="https://deep.nested.wildcard.com">Wildcard Nested Link</a>
<a href="https://wildcard.com">Wildcard Root Link</a>
<a href="https://notwildcard.com">Non-Wildcard Link</a>
<a href="https://www.api.wildcard.com">WWW Wildcard API Link</a>
<a href="https://www.admin.wildcard.com">WWW Wildcard Admin Link</a>
</div>
);
}
61 changes: 60 additions & 1 deletion apps/nextjs/tests/outbound-domains.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { DUB_ANALYTICS_SCRIPT_URL } from '@/app/constants';
import { test, expect } from '@playwright/test';

declare global {
Expand Down Expand Up @@ -27,6 +26,11 @@ test.describe('Outbound domains tracking', () => {
expect(exampleHref).toContain('dub_id=test-click-id');
expect(otherHref).toContain('dub_id=test-click-id');
expect(unrelatedHref).not.toContain('dub_id=test-click-id');

// Also verify wildcard domain functionality
const wildcardLink = await page.$('a[href*="api.wildcard.com"]');
const wildcardHref = await wildcardLink?.getAttribute('href');
expect(wildcardHref).toContain('dub_id=test-click-id');
});

test('should handle iframe src attributes', async ({ page }) => {
Expand Down Expand Up @@ -149,4 +153,59 @@ test.describe('Outbound domains tracking', () => {
const iframeSrc = await iframe?.getAttribute('src');
expect(iframeSrc).toContain('dub_id=test-click-id');
});

test('should handle wildcard domains correctly', async ({ page }) => {
await page.goto('/outbound?dub_id=test-click-id');
await page.waitForFunction(() => window._dubAnalytics !== undefined);

await page.waitForTimeout(1000);

// Test wildcard domain matching - *.wildcard.com should match all subdomains
const wildcardLink = await page.$('a[href*="api.wildcard.com"]');
const wildcardSubdomainLink = await page.$('a[href*="admin.wildcard.com"]');
const wildcardNestedLink = await page.$(
'a[href*="deep.nested.wildcard.com"]',
);
const wildcardRootLink = await page.$('a[href*="wildcard.com"]');

// Test non-wildcard domain that shouldn't match wildcard pattern
const nonWildcardLink = await page.$('a[href*="notwildcard.com"]');

const wildcardHref = await wildcardLink?.getAttribute('href');
const wildcardSubdomainHref =
await wildcardSubdomainLink?.getAttribute('href');
const wildcardNestedHref = await wildcardNestedLink?.getAttribute('href');
const wildcardRootHref = await wildcardRootLink?.getAttribute('href');
const nonWildcardHref = await nonWildcardLink?.getAttribute('href');

// All wildcard.com subdomains should have tracking
expect(wildcardHref).toContain('dub_id=test-click-id');
expect(wildcardSubdomainHref).toContain('dub_id=test-click-id');
expect(wildcardNestedHref).toContain('dub_id=test-click-id');
expect(wildcardRootHref).toContain('dub_id=test-click-id');

// Non-wildcard domain should not have tracking
expect(nonWildcardHref).not.toContain('dub_id=test-click-id');
});
Comment on lines +156 to +189
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Apex-domain selector is ambiguous; may match subdomains and cause flaky failures

'a[href*="wildcard.com"]' can resolve to any subdomain and even collide with non-targets depending on order. Also replace manual timeouts with auto-waits and assert using regex boundaries.

-    await page.waitForTimeout(1000);
+    // Rely on auto-waiting expectations instead of arbitrary sleeps.

-    // Test wildcard domain matching - *.wildcard.com should match all subdomains
-    const wildcardLink = await page.$('a[href*="api.wildcard.com"]');
-    const wildcardSubdomainLink = await page.$('a[href*="admin.wildcard.com"]');
-    const wildcardNestedLink = await page.$(
-      'a[href*="deep.nested.wildcard.com"]',
-    );
-    const wildcardRootLink = await page.$('a[href*="wildcard.com"]');
+    // Test wildcard domain matching - *.wildcard.com should match all subdomains
+    const api = page.locator('a[href*="://api.wildcard.com"]');
+    const admin = page.locator('a[href*="://admin.wildcard.com"]');
+    const nested = page.locator('a[href*="://deep.nested.wildcard.com"]');
+    // Apex only; avoid matching subdomains
+    const apex = page
+      .locator('a[href="https://wildcard.com"], a[href="https://wildcard.com/"]')
+      .first();

-    // Test non-wildcard domain that shouldn't match wildcard pattern
-    const nonWildcardLink = await page.$('a[href*="notwildcard.com"]');
+    // Test non-wildcard domain that shouldn't match wildcard pattern
+    const nonWildcard = page.locator('a[href*="://notwildcard.com"]');

-    const wildcardHref = await wildcardLink?.getAttribute('href');
-    const wildcardSubdomainHref =
-      await wildcardSubdomainLink?.getAttribute('href');
-    const wildcardNestedHref = await wildcardNestedLink?.getAttribute('href');
-    const wildcardRootHref = await wildcardRootLink?.getAttribute('href');
-    const nonWildcardHref = await nonWildcardLink?.getAttribute('href');
+    const hasDubId = /[?&]dub_id=test-click-id(&|$)/;

-    // All wildcard.com subdomains should have tracking
-    expect(wildcardHref).toContain('dub_id=test-click-id');
-    expect(wildcardSubdomainHref).toContain('dub_id=test-click-id');
-    expect(wildcardNestedHref).toContain('dub_id=test-click-id');
-    expect(wildcardRootHref).toContain('dub_id=test-click-id');
+    // All wildcard.com subdomains should have tracking
+    await expect(api).toHaveAttribute('href', hasDubId);
+    await expect(admin).toHaveAttribute('href', hasDubId);
+    await expect(nested).toHaveAttribute('href', hasDubId);
+    await expect(apex).toHaveAttribute('href', hasDubId);

-    // Non-wildcard domain should not have tracking
-    expect(nonWildcardHref).not.toContain('dub_id=test-click-id');
+    // Non-wildcard domain should not have tracking
+    await expect(nonWildcard).not.toHaveAttribute('href', hasDubId);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('should handle wildcard domains correctly', async ({ page }) => {
await page.goto('/outbound?dub_id=test-click-id');
await page.waitForFunction(() => window._dubAnalytics !== undefined);
await page.waitForTimeout(1000);
// Test wildcard domain matching - *.wildcard.com should match all subdomains
const wildcardLink = await page.$('a[href*="api.wildcard.com"]');
const wildcardSubdomainLink = await page.$('a[href*="admin.wildcard.com"]');
const wildcardNestedLink = await page.$(
'a[href*="deep.nested.wildcard.com"]',
);
const wildcardRootLink = await page.$('a[href*="wildcard.com"]');
// Test non-wildcard domain that shouldn't match wildcard pattern
const nonWildcardLink = await page.$('a[href*="notwildcard.com"]');
const wildcardHref = await wildcardLink?.getAttribute('href');
const wildcardSubdomainHref =
await wildcardSubdomainLink?.getAttribute('href');
const wildcardNestedHref = await wildcardNestedLink?.getAttribute('href');
const wildcardRootHref = await wildcardRootLink?.getAttribute('href');
const nonWildcardHref = await nonWildcardLink?.getAttribute('href');
// All wildcard.com subdomains should have tracking
expect(wildcardHref).toContain('dub_id=test-click-id');
expect(wildcardSubdomainHref).toContain('dub_id=test-click-id');
expect(wildcardNestedHref).toContain('dub_id=test-click-id');
expect(wildcardRootHref).toContain('dub_id=test-click-id');
// Non-wildcard domain should not have tracking
expect(nonWildcardHref).not.toContain('dub_id=test-click-id');
});
test('should handle wildcard domains correctly', async ({ page }) => {
await page.goto('/outbound?dub_id=test-click-id');
await page.waitForFunction(() => window._dubAnalytics !== undefined);
// Rely on auto-waiting expectations instead of arbitrary sleeps.
// Test wildcard domain matching - *.wildcard.com should match all subdomains
const api = page.locator('a[href*="://api.wildcard.com"]');
const admin = page.locator('a[href*="://admin.wildcard.com"]');
const nested = page.locator('a[href*="://deep.nested.wildcard.com"]');
// Apex only; avoid matching subdomains
const apex = page
.locator('a[href="https://wildcard.com"], a[href="https://wildcard.com/"]')
.first();
// Test non-wildcard domain that shouldn't match wildcard pattern
const nonWildcard = page.locator('a[href*="://notwildcard.com"]');
const hasDubId = /[?&]dub_id=test-click-id(&|$)/;
// All wildcard.com subdomains should have tracking
await expect(api).toHaveAttribute('href', hasDubId);
await expect(admin).toHaveAttribute('href', hasDubId);
await expect(nested).toHaveAttribute('href', hasDubId);
await expect(apex).toHaveAttribute('href', hasDubId);
// Non-wildcard domain should not have tracking
await expect(nonWildcard).not.toHaveAttribute('href', hasDubId);
});
🤖 Prompt for AI Agents
In apps/nextjs/tests/outbound-domains.spec.ts around lines 156 to 189, the
selector 'a[href*="wildcard.com"]' is ambiguous and can match subdomains or
unintended links, and the test uses a manual waitForTimeout; change selectors to
verify link hostnames explicitly (parse each anchor href with new URL(...) and
assert hostname === 'wildcard.com' or hostname.endsWith('.wildcard.com') for
wildcard cases), replace the generic 'a[href*="wildcard.com"]' with precise
queries (e.g., locate anchors then filter by parsed hostname), ensure the
non-wildcard case checks hostname does not match '*.wildcard.com', replace
waitForTimeout with deterministic waits like waitForSelector or waitForFunction
that _dubAnalytics and the specific anchors are present, and change the
assertions to use regex boundary checks (e.g., match dub_id=test-click-id as a
whole token) to avoid false positives.


test('should handle wildcard with www prefix correctly', async ({ page }) => {
await page.goto('/outbound?dub_id=test-click-id');
await page.waitForFunction(() => window._dubAnalytics !== undefined);

await page.waitForTimeout(1000);

// Test wildcard domain with www prefix
const wwwWildcardLink = await page.$('a[href*="www.api.wildcard.com"]');
const wwwWildcardSubdomainLink = await page.$(
'a[href*="www.admin.wildcard.com"]',
);

const wwwWildcardHref = await wwwWildcardLink?.getAttribute('href');
const wwwWildcardSubdomainHref =
await wwwWildcardSubdomainLink?.getAttribute('href');

// www. prefix should be ignored, so these should still match wildcard pattern
expect(wwwWildcardHref).toContain('dub_id=test-click-id');
expect(wwwWildcardSubdomainHref).toContain('dub_id=test-click-id');
});
});
8 changes: 7 additions & 1 deletion packages/script/src/extensions/outbound-domains.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ const initOutboundDomains = () => {
const normalizedUrlHostname = normalizeDomain(urlHostname);
const normalizedDomain = normalizeDomain(domain);

// Exact match after removing www.
// if wildcard domain, check if the url hostname ends with the apex domain
if (normalizedDomain.startsWith('*.')) {
const apexDomain = normalizedDomain.slice(2);
return normalizedUrlHostname.endsWith(`.${apexDomain}`);
}

// check for exact match after removing www.
return normalizedUrlHostname === normalizedDomain;
} catch (e) {
return false;
Expand Down