test(qa): e2e 装配修正 + 集成/门禁测试与工具链重构
- e2e:修死选择器与蜜罐误命中、补断言、GA4/security-headers 用例诚实标注 skip 边界;新增 assert-helpers/fixtures/hydrated/primary-nav/touch-targets 等复用桩。 - 集成层:config/test/jest.integration.config.js + tests-integration/ 真库 一次性 SQLite 用例,teardown 守卫开发库指纹。 - 门禁工具链:jest.setup 归位到根、scripts/accessibility 全站 axe 节点计数、 check-brand-text-token/check-motion-constraints 机械守卫、stryker/lighthouse 配置收敛。
This commit is contained in:
+224
-229
@@ -1,265 +1,260 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { test, expect, type Page } from './fixtures';
|
||||
|
||||
/**
|
||||
* GA4 事件追踪验证测试套件
|
||||
* GA4 事件追踪验证
|
||||
*
|
||||
* 通过 Playwright 网络拦截与页面评估,验证 Google Analytics 4 事件是否正确触发。
|
||||
* 由于测试环境中的 NEXT_PUBLIC_GA_MEASUREMENT_ID 为空,应用的 analytics 模块
|
||||
* 不会实际调用 gtag。本测试通过 addInitScript 注入 mock gtag 来记录调用,
|
||||
* 并模拟用户交互后应触发的 analytics 调用来验证事件参数的正确性。
|
||||
* 验收 A-6 重写:原实现先注入 mock window.gtag,再由测试自己调用 gtag 并断言
|
||||
* 刚写入的 __gtagCalls —— 断言的是 fixture,不是应用。且 .env.production 配置了
|
||||
* 真实 MEASUREMENT_ID 后,应用注入的 gtag.js 会覆盖 mock,导致全部用例失败。
|
||||
*
|
||||
* 现在只拦截 Google 的远端脚本(CI 不出网),gtag 函数与 dataLayer 队列由应用
|
||||
* 自己的内联片段创建(src/components/analytics/GoogleAnalytics.tsx),
|
||||
* 因此 window.dataLayer 中的每一条调用都必然由应用发出。
|
||||
*
|
||||
* 测试范围:
|
||||
* 1. TC-GA4-001: 页面浏览追踪 - 首页加载时触发 gtag config
|
||||
* 2. TC-GA4-002: 联系表单提交触发 form_submit 和 conversion 事件
|
||||
* 3. TC-GA4-003: CTA 按钮点击触发 button_click 事件
|
||||
* 4. TC-GA4-004: 页面浏览追踪 - 产品页加载时触发 gtag config
|
||||
* 1. TC-GA4-001: 首屏内联片段发出 js / consent default / 唯一一次 config
|
||||
* 2. TC-GA4-002: 联系表单提交触发 form_submit + 单次 conversion
|
||||
* 3. TC-GA4-003: 点击「接受所有」触发 button_click + consent update
|
||||
* 4. TC-GA4-004: 客户端路由跳转触发带 page_path 的 config
|
||||
*/
|
||||
|
||||
test.setTimeout(60000);
|
||||
|
||||
type GtagCall = unknown[];
|
||||
type GtagParams = Record<string, unknown>;
|
||||
|
||||
/** 内联片段把参数按 arguments 对象压入队列,这里统一展开成数组便于断言。 */
|
||||
async function gtagCalls(page: Page): Promise<GtagCall[]> {
|
||||
return page.evaluate(() => {
|
||||
const queue = (window as unknown as { dataLayer?: ArrayLike<unknown>[] }).dataLayer;
|
||||
if (!queue) {return [];}
|
||||
return Array.from(queue).map((entry) => Array.from(entry as ArrayLike<unknown>));
|
||||
});
|
||||
}
|
||||
|
||||
function callsWith(calls: GtagCall[], ...prefix: unknown[]): GtagCall[] {
|
||||
return calls.filter((call) => prefix.every((value, index) => call[index] === value));
|
||||
}
|
||||
|
||||
function params(call: GtagCall | undefined): GtagParams {
|
||||
return (call?.[2] ?? {}) as GtagParams;
|
||||
}
|
||||
|
||||
/** 拦截 Google 远端脚本,返回应用请求携带的 MEASUREMENT_ID;未配置 GA 时返回 null。 */
|
||||
async function gotoWithGtag(page: Page, path: string): Promise<string | null> {
|
||||
await page.route(/https?:\/\/www\.googletagmanager\.com\//, (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/javascript',
|
||||
body: '/* GA4 E2E stub: 保留应用自身的 dataLayer 队列 */',
|
||||
})
|
||||
);
|
||||
|
||||
const gtagRequest = page
|
||||
.waitForRequest(/www\.googletagmanager\.com\/gtag\/js/, { timeout: 15000 })
|
||||
.catch(() => null);
|
||||
|
||||
await page.goto(path, { timeout: 30000, waitUntil: 'domcontentloaded' });
|
||||
|
||||
const request = await gtagRequest;
|
||||
if (!request) {return null;}
|
||||
return new URL(request.url()).searchParams.get('id');
|
||||
}
|
||||
|
||||
/** 预置同意偏好:抑制横幅遮挡,同时让应用发出 consent update。 */
|
||||
async function seedConsent(page: Page): Promise<void> {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.setItem(
|
||||
'novalon-cookie-preferences',
|
||||
JSON.stringify({
|
||||
necessary: true,
|
||||
analytics: true,
|
||||
marketing: false,
|
||||
functionality: true,
|
||||
timestamp: Date.now(),
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* e2e/playwright.config.ts:29 的 storageState 全站预置了同意偏好(analytics:false),
|
||||
* 因此横幅默认不出现;需要真实横幅的用例必须先清除该键。
|
||||
*/
|
||||
async function clearSeededConsent(page: Page): Promise<void> {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.removeItem('novalon-cookie-preferences');
|
||||
});
|
||||
}
|
||||
|
||||
async function expectGtagReady(page: Page): Promise<void> {
|
||||
await expect
|
||||
.poll(async () => (await gtagCalls(page)).length, { timeout: 15000 })
|
||||
.toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
/** 等应用内联片段把 js / consent default / config 三条都写入队列后再断言,避免读到半成品。 */
|
||||
async function expectSnippetCalls(page: Page): Promise<GtagCall[]> {
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const calls = await gtagCalls(page);
|
||||
const present = [
|
||||
callsWith(calls, 'js').length,
|
||||
callsWith(calls, 'consent', 'default').length,
|
||||
callsWith(calls, 'config').length,
|
||||
];
|
||||
return present.filter((count) => count > 0).length;
|
||||
}, { timeout: 15000 })
|
||||
.toBe(3);
|
||||
|
||||
return gtagCalls(page);
|
||||
}
|
||||
|
||||
/** GoogleAnalytics 只在客户端路由变化时补发带 page_path 的 config。 */
|
||||
async function routedPageViews(page: Page, measurementId: string | null): Promise<GtagCall[]> {
|
||||
return callsWith(await gtagCalls(page), 'config', measurementId ?? '');
|
||||
}
|
||||
|
||||
test.describe('GA4 事件追踪验证', { tag: ['@analytics', '@regression', '@critical'] }, () => {
|
||||
test.describe.configure({ mode: 'parallel' });
|
||||
|
||||
// 在每个测试前设置 gtag mock
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
(window as any).gtag = (...args: any[]) => {
|
||||
if (!(window as any).__gtagCalls) {
|
||||
(window as any).__gtagCalls = [];
|
||||
}
|
||||
(window as any).__gtagCalls.push(args);
|
||||
};
|
||||
window.dataLayer = [];
|
||||
});
|
||||
});
|
||||
test('TC-GA4-001: 页面浏览追踪 - 首屏由应用发出唯一一次 config', async ({ page }) => {
|
||||
await seedConsent(page);
|
||||
const measurementId = await gotoWithGtag(page, '/');
|
||||
test.skip(measurementId === null, '构建未注入 GA 脚本(NEXT_PUBLIC_GA_MEASUREMENT_ID 为空)');
|
||||
|
||||
/**
|
||||
* Helper: 关闭 Cookie 同意横幅(如果存在)
|
||||
*/
|
||||
async function closeCookieConsent(page: any) {
|
||||
const acceptAllBtn = page.locator('button', { hasText: '接受所有' });
|
||||
if (await acceptAllBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
|
||||
await acceptAllBtn.click();
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
}
|
||||
const calls = await expectSnippetCalls(page);
|
||||
|
||||
test('TC-GA4-001: 页面浏览追踪 - 首页加载时触发 gtag config', async ({ page }) => {
|
||||
// 导航到首页
|
||||
await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
||||
await page.waitForTimeout(2000);
|
||||
expect(callsWith(calls, 'js')).toHaveLength(1);
|
||||
|
||||
// 关闭 Cookie 同意横幅
|
||||
await closeCookieConsent(page);
|
||||
const consentDefault = callsWith(calls, 'consent', 'default');
|
||||
expect(consentDefault).toHaveLength(1);
|
||||
expect(params(consentDefault[0]).analytics_storage).toBe('granted');
|
||||
expect(params(consentDefault[0]).ad_storage).toBe('denied');
|
||||
|
||||
// 模拟首页加载时应触发的 gtag config 调用
|
||||
await page.evaluate(() => {
|
||||
(window as any).gtag('config', 'G-TEST123', {
|
||||
page_path: '/',
|
||||
page_title: document.title,
|
||||
page_location: window.location.origin + '/',
|
||||
});
|
||||
});
|
||||
|
||||
// 验证 gtag config 调用
|
||||
const gtagCalls = await page.evaluate(() => (window as any).__gtagCalls || []);
|
||||
const configCalls = gtagCalls.filter((call: any[]) => call[0] === 'config');
|
||||
|
||||
expect(configCalls.length).toBeGreaterThan(0);
|
||||
expect(configCalls[0][1]).toBe('G-TEST123');
|
||||
expect(configCalls[0][2]).toBeDefined();
|
||||
expect(configCalls[0][2].page_path).toBe('/');
|
||||
expect(configCalls[0][2].page_title).toBeTruthy();
|
||||
const configs = callsWith(calls, 'config');
|
||||
expect(configs).toHaveLength(1);
|
||||
expect(configs[0]?.[1]).toBe(measurementId);
|
||||
expect(params(configs[0]).anonymize_ip).toBe(true);
|
||||
});
|
||||
|
||||
test('TC-GA4-002: 联系表单提交触发 form_submit 和 conversion 事件', async ({ page }) => {
|
||||
// 拦截 /api/contact 请求,模拟成功响应
|
||||
await page.route('**/api/contact', async (route) => {
|
||||
await route.fulfill({
|
||||
await seedConsent(page);
|
||||
await page.route('**/api/contact', (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ success: 'true' }),
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
await page.goto('/contact', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
||||
await page.waitForTimeout(2000);
|
||||
const measurementId = await gotoWithGtag(page, '/contact');
|
||||
test.skip(measurementId === null, '构建未注入 GA 脚本(NEXT_PUBLIC_GA_MEASUREMENT_ID 为空)');
|
||||
await expectGtagReady(page);
|
||||
|
||||
// 关闭 Cookie 同意横幅
|
||||
await closeCookieConsent(page);
|
||||
await page.locator('[data-testid="name-input"]').first().fill('测试用户');
|
||||
await page.locator('[data-testid="phone-input"]').first().fill('13800138000');
|
||||
await page.locator('[data-testid="email-input"]').first().fill('test@example.com');
|
||||
await page.locator('[data-testid="subject-input"]').first().fill('咨询产品方案');
|
||||
await page
|
||||
.locator('[data-testid="message-input"]')
|
||||
.first()
|
||||
.fill('您好,我想了解贵公司的ERP产品和CRM产品,请提供详细方案和报价。');
|
||||
|
||||
// 填写表单
|
||||
const nameInput = page.locator('[data-testid="name-input"]').first();
|
||||
await nameInput.fill('测试用户');
|
||||
|
||||
const phoneInput = page.locator('[data-testid="phone-input"]').first();
|
||||
await phoneInput.fill('13800138000');
|
||||
|
||||
const emailInput = page.locator('[data-testid="email-input"]').first();
|
||||
await emailInput.fill('test@example.com');
|
||||
|
||||
const subjectInput = page.locator('[data-testid="subject-input"]').first();
|
||||
await subjectInput.fill('咨询产品方案');
|
||||
|
||||
const messageInput = page.locator('[data-testid="message-input"]').first();
|
||||
await messageInput.fill('您好,我想了解贵公司的ERP产品和CRM产品,请提供详细方案和报价。');
|
||||
|
||||
// 提交表单
|
||||
const submitButton = page.locator('[data-testid="submit-button"]').first();
|
||||
await expect(submitButton).toBeEnabled({ timeout: 5000 });
|
||||
await expect(submitButton).toBeEnabled({ timeout: 10000 });
|
||||
await submitButton.click();
|
||||
|
||||
// 等待表单提交成功
|
||||
await page.waitForTimeout(2000);
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
const calls = await gtagCalls(page);
|
||||
return (
|
||||
callsWith(calls, 'event', 'form_submit').length +
|
||||
callsWith(calls, 'event', 'conversion').length
|
||||
);
|
||||
},
|
||||
{ timeout: 15000 }
|
||||
)
|
||||
.toBeGreaterThanOrEqual(2);
|
||||
|
||||
// 验证表单提交成功(显示成功消息)
|
||||
const successMessage = page.locator('text=提交成功').first();
|
||||
const isSuccessVisible = await successMessage.isVisible({ timeout: 5000 }).catch(() => false);
|
||||
if (isSuccessVisible) {
|
||||
await expect(successMessage).toBeVisible();
|
||||
const calls = await gtagCalls(page);
|
||||
const formSubmit = callsWith(calls, 'event', 'form_submit');
|
||||
expect(formSubmit).toHaveLength(1);
|
||||
expect(params(formSubmit[0]).event_category).toBe('contact');
|
||||
expect(params(formSubmit[0]).event_label).toBe('咨询产品方案');
|
||||
expect(params(formSubmit[0]).event_value).toBe(1);
|
||||
expect(params(formSubmit[0]).send_to).toBe(measurementId);
|
||||
|
||||
// contact-content-v3 只调用 trackContactForm(内部已发 conversion),
|
||||
// 计数为 1 是「一次提交 = 一次转化」的回归护栏。
|
||||
const conversions = callsWith(calls, 'event', 'conversion');
|
||||
expect(conversions).toHaveLength(1);
|
||||
expect(params(conversions[0]).conversion_label).toBe('contact_form_submit');
|
||||
expect(params(conversions[0]).value).toBe(1);
|
||||
expect(params(conversions[0]).currency).toBe('CNY');
|
||||
expect(typeof params(conversions[0]).transaction_id).toBe('string');
|
||||
});
|
||||
|
||||
test('TC-GA4-003: 同意横幅「接受所有」点击触发 button_click 事件', async ({ page }) => {
|
||||
await clearSeededConsent(page);
|
||||
const measurementId = await gotoWithGtag(page, '/');
|
||||
test.skip(measurementId === null, '构建未注入 GA 脚本(NEXT_PUBLIC_GA_MEASUREMENT_ID 为空)');
|
||||
|
||||
const acceptAll = page.locator('button', { hasText: '接受所有' });
|
||||
await expect(acceptAll).toBeVisible({ timeout: 15000 });
|
||||
await acceptAll.click();
|
||||
|
||||
await expect
|
||||
.poll(
|
||||
async () => callsWith(await gtagCalls(page), 'event', 'button_click').length,
|
||||
{ timeout: 10000 }
|
||||
)
|
||||
.toBe(2);
|
||||
|
||||
const calls = await gtagCalls(page);
|
||||
const buttonClick = callsWith(calls, 'event', 'button_click');
|
||||
// handleAcceptAll 先复用 handleSavePreferences(save_cookie_preferences),再补发自身事件。
|
||||
expect(buttonClick.map((call) => params(call).event_label)).toEqual([
|
||||
'save_cookie_preferences',
|
||||
'accept_all_cookies',
|
||||
]);
|
||||
for (const call of buttonClick) {
|
||||
expect(params(call).event_category).toBe('engagement');
|
||||
expect(params(call).send_to).toBe(measurementId);
|
||||
}
|
||||
|
||||
// 模拟表单提交成功后应触发的 analytics 事件
|
||||
// trackContactForm 内部会调用:
|
||||
// - trackEvent('form_submit', 'contact', '咨询产品方案', 1)
|
||||
// - trackConversion('contact_form_submit', 1)
|
||||
await page.evaluate(() => {
|
||||
// 模拟 form_submit 事件
|
||||
(window as any).gtag('event', 'form_submit', {
|
||||
event_category: 'contact',
|
||||
event_label: '咨询产品方案',
|
||||
event_value: 1,
|
||||
send_to: 'G-TEST123',
|
||||
});
|
||||
const consentUpdate = callsWith(calls, 'consent', 'update');
|
||||
expect(consentUpdate.length).toBeGreaterThan(0);
|
||||
expect(params(consentUpdate[consentUpdate.length - 1]).analytics_storage).toBe('granted');
|
||||
|
||||
// 模拟 conversion 事件
|
||||
(window as any).gtag('event', 'conversion', {
|
||||
send_to: 'G-TEST123',
|
||||
transaction_id: `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
||||
value: 1,
|
||||
currency: 'CNY',
|
||||
conversion_label: 'contact_form_submit',
|
||||
});
|
||||
});
|
||||
await expect(acceptAll).toBeHidden({ timeout: 10000 });
|
||||
});
|
||||
|
||||
const gtagCalls = await page.evaluate(() => (window as any).__gtagCalls || []);
|
||||
test('TC-GA4-004: 页面浏览追踪 - 客户端路由跳转触发带 page_path 的 config', async ({ page }) => {
|
||||
await seedConsent(page);
|
||||
const measurementId = await gotoWithGtag(page, '/');
|
||||
test.skip(measurementId === null, '构建未注入 GA 脚本(NEXT_PUBLIC_GA_MEASUREMENT_ID 为空)');
|
||||
await expectGtagReady(page);
|
||||
|
||||
// 验证 form_submit 事件
|
||||
const formSubmitCalls = gtagCalls.filter(
|
||||
(call: any[]) => call[0] === 'event' && call[1] === 'form_submit'
|
||||
const privacyLink = page.locator('footer a[href="/privacy"]').first();
|
||||
await expect(privacyLink).toBeVisible({ timeout: 15000 });
|
||||
await privacyLink.click();
|
||||
await expect(page).toHaveURL(/\/privacy$/);
|
||||
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const views = await routedPageViews(page, measurementId);
|
||||
return views.filter((call) => params(call).page_path === '/privacy').length;
|
||||
}, { timeout: 15000 })
|
||||
.toBe(1);
|
||||
|
||||
const routeConfig = (await routedPageViews(page, measurementId)).filter(
|
||||
(call) => params(call).page_path === '/privacy'
|
||||
);
|
||||
expect(formSubmitCalls.length).toBeGreaterThan(0);
|
||||
expect(formSubmitCalls[0][2].event_category).toBe('contact');
|
||||
expect(formSubmitCalls[0][2].event_label).toBeTruthy();
|
||||
|
||||
// 验证 conversion 事件
|
||||
const conversionCalls = gtagCalls.filter(
|
||||
(call: any[]) => call[0] === 'event' && call[1] === 'conversion'
|
||||
);
|
||||
expect(conversionCalls.length).toBeGreaterThan(0);
|
||||
expect(conversionCalls[0][2].conversion_label).toBe('contact_form_submit');
|
||||
expect(conversionCalls[0][2].value).toBe(1);
|
||||
expect(conversionCalls[0][2].currency).toBe('CNY');
|
||||
// A-17:SPA pageview 必须在下一帧读取标题,否则 page_title 会是空串
|
||||
// (由 GoogleAnalytics.test.tsx「read document.title after metadata paints」钉住)。
|
||||
expect(String(params(routeConfig[0]).page_title)).toContain('隐私政策');
|
||||
expect(params(routeConfig[0]).page_location).toContain('/privacy');
|
||||
});
|
||||
|
||||
test('TC-GA4-003: CTA 按钮点击触发 button_click 事件', async ({ page }) => {
|
||||
await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// 关闭 Cookie 同意横幅
|
||||
await closeCookieConsent(page);
|
||||
|
||||
// 滚动到页面底部以触发 CTA 区域
|
||||
await page.evaluate(() => {
|
||||
const ctaSection = document.querySelector('#cta');
|
||||
if (ctaSection) {
|
||||
ctaSection.scrollIntoView({ behavior: 'instant', block: 'center' });
|
||||
}
|
||||
});
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// 查找 CTA 按钮("预约免费咨询")
|
||||
const ctaButton = page.locator('#cta a', { hasText: '预约免费咨询' }).first();
|
||||
const isCtaVisible = await ctaButton.isVisible({ timeout: 5000 }).catch(() => false);
|
||||
|
||||
if (isCtaVisible) {
|
||||
// 模拟点击 CTA 按钮后应触发的 button_click 事件
|
||||
// 使用 button_click 事件格式: trackButtonClick 调用 trackEvent('button_click', 'engagement', buttonName)
|
||||
await page.evaluate(() => {
|
||||
(window as any).gtag('event', 'button_click', {
|
||||
event_category: 'engagement',
|
||||
event_label: '预约免费咨询',
|
||||
send_to: 'G-TEST123',
|
||||
});
|
||||
});
|
||||
|
||||
// 验证 button_click 事件
|
||||
const gtagCalls = await page.evaluate(() => (window as any).__gtagCalls || []);
|
||||
const buttonClickCalls = gtagCalls.filter(
|
||||
(call: any[]) => call[0] === 'event' && call[1] === 'button_click'
|
||||
);
|
||||
|
||||
expect(buttonClickCalls.length).toBeGreaterThan(0);
|
||||
expect(buttonClickCalls[0][2].event_category).toBe('engagement');
|
||||
expect(buttonClickCalls[0][2].event_label).toBe('预约免费咨询');
|
||||
} else {
|
||||
// CTA 按钮不可见时,验证其他导航按钮的点击事件
|
||||
const navButton = page.locator('nav a, header a', { hasText: '联系我们' }).first();
|
||||
const isNavVisible = await navButton.isVisible({ timeout: 3000 }).catch(() => false);
|
||||
|
||||
if (isNavVisible) {
|
||||
await page.evaluate(() => {
|
||||
(window as any).gtag('event', 'button_click', {
|
||||
event_category: 'navigation',
|
||||
event_label: '联系我们',
|
||||
send_to: 'G-TEST123',
|
||||
});
|
||||
});
|
||||
|
||||
const gtagCalls = await page.evaluate(() => (window as any).__gtagCalls || []);
|
||||
const buttonClickCalls = gtagCalls.filter(
|
||||
(call: any[]) => call[0] === 'event' && call[1] === 'button_click'
|
||||
);
|
||||
|
||||
expect(buttonClickCalls.length).toBeGreaterThan(0);
|
||||
expect(buttonClickCalls[0][2].event_category).toBe('navigation');
|
||||
expect(buttonClickCalls[0][2].event_label).toBe('联系我们');
|
||||
} else {
|
||||
// 降级验证:至少 verify gtag 函数可用并可记录事件
|
||||
const gtagFunctionExists = await page.evaluate(() => typeof (window as any).gtag === 'function');
|
||||
expect(gtagFunctionExists).toBe(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('TC-GA4-004: 页面浏览追踪 - 产品页加载时触发 gtag config', async ({ page }) => {
|
||||
await page.goto('/products', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// 关闭 Cookie 同意横幅
|
||||
await closeCookieConsent(page);
|
||||
|
||||
// 验证页面标题
|
||||
const pageTitle = page.locator('h1').first();
|
||||
await expect(pageTitle).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// 模拟产品页加载时应触发的 gtag config 调用
|
||||
await page.evaluate(() => {
|
||||
(window as any).gtag('config', 'G-TEST123', {
|
||||
page_path: '/products',
|
||||
page_title: document.title,
|
||||
page_location: window.location.origin + '/products',
|
||||
});
|
||||
});
|
||||
|
||||
// 验证 gtag config 调用
|
||||
const gtagCalls = await page.evaluate(() => (window as any).__gtagCalls || []);
|
||||
const configCalls = gtagCalls.filter((call: any[]) => call[0] === 'config');
|
||||
|
||||
expect(configCalls.length).toBeGreaterThan(0);
|
||||
expect(configCalls[0][1]).toBe('G-TEST123');
|
||||
expect(configCalls[0][2]).toBeDefined();
|
||||
expect(configCalls[0][2].page_path).toBe('/products');
|
||||
expect(configCalls[0][2].page_title).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user