|
1 | 1 | import { testIds } from '../../playgrounds/shared/src'; |
2 | 2 | import { expect, test } from '../fixtures/fake-flare'; |
3 | 3 | import { logScenariosFor, runLogScenario } from './logShared'; |
| 4 | +import { attr, hasSpanType, spansOf } from './otlp'; |
4 | 5 | import { runScenario, scenariosFor } from './shared'; |
5 | 6 |
|
6 | 7 | test.describe('vue playground', () => { |
@@ -30,6 +31,57 @@ test.describe('vue playground', () => { |
30 | 31 | }); |
31 | 32 | }); |
32 | 33 |
|
| 34 | +test.describe('vue-router tracing', () => { |
| 35 | + test('pageload root carries the parameterized route and route source', async ({ page, fakeFlare }) => { |
| 36 | + await page.goto('/product/p01'); |
| 37 | + await page.waitForLoadState('networkidle'); |
| 38 | + |
| 39 | + const trace = await fakeFlare.waitForTrace({ |
| 40 | + timeout: 9000, |
| 41 | + predicate: (r) => { |
| 42 | + const pl = spansOf(r.bodyJson).find((s) => hasSpanType(s, 'browser_pageload')); |
| 43 | + return !!pl && JSON.stringify(attr(pl, 'flare.route.source') ?? '').includes('route'); |
| 44 | + }, |
| 45 | + }); |
| 46 | + const pageload = spansOf(trace.bodyJson).find((s) => hasSpanType(s, 'browser_pageload')); |
| 47 | + expect(pageload && attr(pageload, 'flare.entry_point.handler.identifier')).toEqual({ |
| 48 | + stringValue: '/product/:id', |
| 49 | + }); |
| 50 | + expect(pageload && attr(pageload, 'flare.route.source')).toEqual({ stringValue: 'route' }); |
| 51 | + }); |
| 52 | + |
| 53 | + test('client navigation opens a parameterized browser_navigation root (exactly one)', async ({ |
| 54 | + page, |
| 55 | + fakeFlare, |
| 56 | + }) => { |
| 57 | + await page.goto('/'); |
| 58 | + await page.waitForLoadState('networkidle'); |
| 59 | + |
| 60 | + await page.locator('a[href="/product/p01"]').first().click(); |
| 61 | + |
| 62 | + const trace = await fakeFlare.waitForTrace({ |
| 63 | + timeout: 9000, |
| 64 | + predicate: (r) => { |
| 65 | + const nav = spansOf(r.bodyJson).find((s) => hasSpanType(s, 'browser_navigation')); |
| 66 | + return ( |
| 67 | + !!nav && |
| 68 | + JSON.stringify(attr(nav, 'flare.entry_point.handler.identifier') ?? '').includes('/product/:id') |
| 69 | + ); |
| 70 | + }, |
| 71 | + }); |
| 72 | + const nav = spansOf(trace.bodyJson).find((s) => hasSpanType(s, 'browser_navigation')); |
| 73 | + expect(nav && attr(nav, 'flare.entry_point.handler.identifier')).toEqual({ stringValue: '/product/:id' }); |
| 74 | + expect(nav && attr(nav, 'flare.route.source')).toEqual({ stringValue: 'route' }); |
| 75 | + |
| 76 | + // No-double-roots invariant: registerNavigationSource suppresses the History-based root, so this |
| 77 | + // one click produces exactly ONE browser_navigation root across all traces. |
| 78 | + const navSpans = (await fakeFlare.traces()) |
| 79 | + .flatMap((t) => spansOf(t.bodyJson)) |
| 80 | + .filter((s) => hasSpanType(s, 'browser_navigation')); |
| 81 | + expect(navSpans).toHaveLength(1); |
| 82 | + }); |
| 83 | +}); |
| 84 | + |
33 | 85 | test.describe('vue logging', () => { |
34 | 86 | for (const scenario of logScenariosFor('vue').filter((s) => s.flushOnTrigger)) { |
35 | 87 | test(scenario.id, async ({ page, fakeFlare }) => { |
|
0 commit comments