| 
 | 1 | +import { expect } from '@playwright/test';  | 
 | 2 | +import type { Event as SentryEvent, SpanEnvelope } from '@sentry/core';  | 
 | 3 | +import { sentryTest } from '../../../../utils/fixtures';  | 
 | 4 | +import {  | 
 | 5 | + getFirstSentryEnvelopeRequest,  | 
 | 6 | + getMultipleSentryEnvelopeRequests,  | 
 | 7 | + hidePage,  | 
 | 8 | + properFullEnvelopeRequestParser,  | 
 | 9 | + shouldSkipTracingTest,  | 
 | 10 | +} from '../../../../utils/helpers';  | 
 | 11 | + | 
 | 12 | +const supportedBrowsers = ['chromium'];  | 
 | 13 | + | 
 | 14 | +sentryTest(  | 
 | 15 | + 'should capture INP with correct target name when navigation keeps DOM element',  | 
 | 16 | + async ({ browserName, getLocalTestUrl, page }) => {  | 
 | 17 | + if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) {  | 
 | 18 | + sentryTest.skip();  | 
 | 19 | + }  | 
 | 20 | + | 
 | 21 | + const url = await getLocalTestUrl({ testDir: __dirname });  | 
 | 22 | + | 
 | 23 | + await page.goto(url);  | 
 | 24 | + await getFirstSentryEnvelopeRequest<SentryEvent>(page); // wait for page load  | 
 | 25 | + | 
 | 26 | + const spanEnvelopePromise = getMultipleSentryEnvelopeRequests<SpanEnvelope>(  | 
 | 27 | + page,  | 
 | 28 | + 1,  | 
 | 29 | + { envelopeType: 'span' },  | 
 | 30 | + properFullEnvelopeRequestParser,  | 
 | 31 | + );  | 
 | 32 | + | 
 | 33 | + // Simulating route change (keeping <nav> in DOM)  | 
 | 34 | + await page.locator('[data-test-id=nav-link-keepDOM]').click();  | 
 | 35 | + await page.locator('.navigated').isVisible();  | 
 | 36 | + | 
 | 37 | + await page.waitForTimeout(500);  | 
 | 38 | + | 
 | 39 | + // Page hide to trigger INP  | 
 | 40 | + await hidePage(page);  | 
 | 41 | + | 
 | 42 | + // Get the INP span envelope  | 
 | 43 | + const spanEnvelope = (await spanEnvelopePromise)[0];  | 
 | 44 | + | 
 | 45 | + const spanEnvelopeHeaders = spanEnvelope[0];  | 
 | 46 | + const spanEnvelopeItem = spanEnvelope[1][0][1];  | 
 | 47 | + | 
 | 48 | + const traceId = spanEnvelopeHeaders.trace!.trace_id;  | 
 | 49 | + expect(traceId).toMatch(/[a-f0-9]{32}/);  | 
 | 50 | + | 
 | 51 | + expect(spanEnvelopeHeaders).toEqual({  | 
 | 52 | + sent_at: expect.any(String),  | 
 | 53 | + trace: {  | 
 | 54 | + environment: 'production',  | 
 | 55 | + public_key: 'public',  | 
 | 56 | + sample_rate: '1',  | 
 | 57 | + sampled: 'true',  | 
 | 58 | + trace_id: traceId,  | 
 | 59 | + sample_rand: expect.any(String),  | 
 | 60 | + },  | 
 | 61 | + });  | 
 | 62 | + | 
 | 63 | + const inpValue = spanEnvelopeItem.measurements?.inp.value;  | 
 | 64 | + expect(inpValue).toBeGreaterThan(0);  | 
 | 65 | + | 
 | 66 | + expect(spanEnvelopeItem).toEqual({  | 
 | 67 | + data: {  | 
 | 68 | + 'sentry.exclusive_time': inpValue,  | 
 | 69 | + 'sentry.op': 'ui.interaction.click',  | 
 | 70 | + 'sentry.origin': 'auto.http.browser.inp',  | 
 | 71 | + 'sentry.source': 'custom',  | 
 | 72 | + transaction: 'test-url',  | 
 | 73 | + 'user_agent.original': expect.stringContaining('Chrome'),  | 
 | 74 | + },  | 
 | 75 | + measurements: {  | 
 | 76 | + inp: {  | 
 | 77 | + unit: 'millisecond',  | 
 | 78 | + value: inpValue,  | 
 | 79 | + },  | 
 | 80 | + },  | 
 | 81 | + description: 'body > nav#navigation > NavigationLink',  | 
 | 82 | + exclusive_time: inpValue,  | 
 | 83 | + op: 'ui.interaction.click',  | 
 | 84 | + origin: 'auto.http.browser.inp',  | 
 | 85 | + is_segment: true,  | 
 | 86 | + segment_id: spanEnvelopeItem.span_id,  | 
 | 87 | + span_id: expect.stringMatching(/[a-f0-9]{16}/),  | 
 | 88 | + start_timestamp: expect.any(Number),  | 
 | 89 | + timestamp: expect.any(Number),  | 
 | 90 | + trace_id: traceId,  | 
 | 91 | + });  | 
 | 92 | + },  | 
 | 93 | +);  | 
 | 94 | + | 
 | 95 | +sentryTest(  | 
 | 96 | + 'should capture INP with unknown target name when navigation removes element from DOM',  | 
 | 97 | + async ({ browserName, getLocalTestUrl, page }) => {  | 
 | 98 | + if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) {  | 
 | 99 | + sentryTest.skip();  | 
 | 100 | + }  | 
 | 101 | + | 
 | 102 | + const url = await getLocalTestUrl({ testDir: __dirname });  | 
 | 103 | + | 
 | 104 | + await page.goto(url);  | 
 | 105 | + await getFirstSentryEnvelopeRequest<SentryEvent>(page); // wait for page load  | 
 | 106 | + | 
 | 107 | + const spanEnvelopePromise = getMultipleSentryEnvelopeRequests<SpanEnvelope>(  | 
 | 108 | + page,  | 
 | 109 | + 1,  | 
 | 110 | + { envelopeType: 'span' },  | 
 | 111 | + properFullEnvelopeRequestParser,  | 
 | 112 | + );  | 
 | 113 | + | 
 | 114 | + // Simulating route change (also changing <nav> in DOM)  | 
 | 115 | + await page.locator('[data-test-id=nav-link-changeDOM]').click();  | 
 | 116 | + await page.locator('.navigated').isVisible();  | 
 | 117 | + | 
 | 118 | + await page.waitForTimeout(500);  | 
 | 119 | + | 
 | 120 | + // Page hide to trigger INP  | 
 | 121 | + await hidePage(page);  | 
 | 122 | + | 
 | 123 | + // Get the INP span envelope  | 
 | 124 | + const spanEnvelope = (await spanEnvelopePromise)[0];  | 
 | 125 | + | 
 | 126 | + const spanEnvelopeHeaders = spanEnvelope[0];  | 
 | 127 | + const spanEnvelopeItem = spanEnvelope[1][0][1];  | 
 | 128 | + | 
 | 129 | + const traceId = spanEnvelopeHeaders.trace!.trace_id;  | 
 | 130 | + expect(traceId).toMatch(/[a-f0-9]{32}/);  | 
 | 131 | + | 
 | 132 | + expect(spanEnvelopeHeaders).toEqual({  | 
 | 133 | + sent_at: expect.any(String),  | 
 | 134 | + trace: {  | 
 | 135 | + environment: 'production',  | 
 | 136 | + public_key: 'public',  | 
 | 137 | + sample_rate: '1',  | 
 | 138 | + sampled: 'true',  | 
 | 139 | + trace_id: traceId,  | 
 | 140 | + sample_rand: expect.any(String),  | 
 | 141 | + },  | 
 | 142 | + });  | 
 | 143 | + | 
 | 144 | + const inpValue = spanEnvelopeItem.measurements?.inp.value;  | 
 | 145 | + expect(inpValue).toBeGreaterThan(0);  | 
 | 146 | + | 
 | 147 | + expect(spanEnvelopeItem).toEqual({  | 
 | 148 | + data: {  | 
 | 149 | + 'sentry.exclusive_time': inpValue,  | 
 | 150 | + 'sentry.op': 'ui.interaction.click',  | 
 | 151 | + 'sentry.origin': 'auto.http.browser.inp',  | 
 | 152 | + 'sentry.source': 'custom',  | 
 | 153 | + transaction: 'test-url',  | 
 | 154 | + 'user_agent.original': expect.stringContaining('Chrome'),  | 
 | 155 | + },  | 
 | 156 | + measurements: {  | 
 | 157 | + inp: {  | 
 | 158 | + unit: 'millisecond',  | 
 | 159 | + value: inpValue,  | 
 | 160 | + },  | 
 | 161 | + },  | 
 | 162 | + description: '<unknown>', // FIXME: currently unable to get the target name when element is removed from DOM  | 
 | 163 | + exclusive_time: inpValue,  | 
 | 164 | + op: 'ui.interaction.click',  | 
 | 165 | + origin: 'auto.http.browser.inp',  | 
 | 166 | + is_segment: true,  | 
 | 167 | + segment_id: spanEnvelopeItem.span_id,  | 
 | 168 | + span_id: expect.stringMatching(/[a-f0-9]{16}/),  | 
 | 169 | + start_timestamp: expect.any(Number),  | 
 | 170 | + timestamp: expect.any(Number),  | 
 | 171 | + trace_id: traceId,  | 
 | 172 | + });  | 
 | 173 | + },  | 
 | 174 | +);  | 
0 commit comments