@@ -135,6 +135,9 @@ func TestSourceTraceFetchNormalizesRequestInputs(t *testing.T) {
135135 `function traceInputURL(url)` ,
136136 `typeof Request !== 'undefined' && url instanceof Request` ,
137137 `return url.url || '';` ,
138+ `function traceURLPath(url)` ,
139+ `return new URL(value, window.location.href).pathname || '/';` ,
140+ `{ key: 'url.path', value: traceURLPath(url) }` ,
138141 `return new URL(traceInputURL(url), window.location.href).origin === window.location.origin;` ,
139142 `function traceInputHeaders(url, options)` ,
140143 `url && typeof url === 'object' && url.headers` ,
@@ -146,6 +149,20 @@ func TestSourceTraceFetchNormalizesRequestInputs(t *testing.T) {
146149 }
147150}
148151
152+ func TestTraceFetchExportsPathOnlyURL (t * testing.T ) {
153+ node , err := exec .LookPath ("node" )
154+ if err != nil {
155+ t .Skip ("node is not installed" )
156+ }
157+ script := filepath .Join (t .TempDir (), "gowdk-trace-url-test.js" )
158+ if err := os .WriteFile (script , []byte (traceURLHarnessScript (string (Source ()))), 0o600 ); err != nil {
159+ t .Fatal (err )
160+ }
161+ if output , err := exec .Command (node , script ).CombinedOutput (); err != nil {
162+ t .Fatalf ("trace URL harness failed: %v\n %s" , err , output )
163+ }
164+ }
165+
149166func TestFilename (t * testing.T ) {
150167 if Filename != "gowdk.js" {
151168 t .Fatalf ("unexpected runtime filename %q" , Filename )
@@ -184,6 +201,63 @@ func TestEmbeddedRuntimeSourceFilesParseWithNode(t *testing.T) {
184201 }
185202}
186203
204+ func traceURLHarnessScript (runtime string ) string {
205+ return `
206+ 'use strict';
207+
208+ const assert = require('node:assert/strict');
209+
210+ const requests = [];
211+ global.document = {
212+ documentElement: { hasAttribute() { return false; } },
213+ addEventListener() {},
214+ querySelector() { return null; },
215+ querySelectorAll() { return []; }
216+ };
217+ global.window = {
218+ location: {
219+ href: 'http://example.test/account?session=server-secret',
220+ origin: 'http://example.test'
221+ },
222+ addEventListener() {},
223+ __gowdkTraceEnabled: true
224+ };
225+ global.fetch = async function(url, options) {
226+ requests.push({ url: String(url), options: options || {} });
227+ return {
228+ ok: true,
229+ status: 204,
230+ headers: { get() { return ''; } },
231+ text: async () => ''
232+ };
233+ };
234+
235+ ` + runtime + `
236+
237+ (async function() {
238+ await window.__gowdkTrace.fetch('/api/patients?token=client-secret&code=123#frag', {}, {
239+ name: 'fetch patients',
240+ lane: 'api'
241+ });
242+ await new Promise(resolve => setImmediate(resolve));
243+
244+ assert.equal(requests[0].url, '/api/patients?token=client-secret&code=123#frag');
245+ const traceRequest = requests.find(request => request.url === '/_gowdk/traces/browser');
246+ assert.ok(traceRequest, 'trace span was not posted');
247+ const payload = String(traceRequest.options.body || '');
248+ assert.ok(!payload.includes('client-secret'), payload);
249+ assert.ok(!payload.includes('code=123'), payload);
250+ assert.ok(!payload.includes('#frag'), payload);
251+ const span = JSON.parse(payload);
252+ const urlPath = span.attributes.find(attr => attr.key === 'url.path');
253+ assert.deepEqual(urlPath, { key: 'url.path', value: '/api/patients' });
254+ })().catch(error => {
255+ console.error(error && error.stack || error);
256+ process.exit(1);
257+ });
258+ `
259+ }
260+
187261func TestRuntimeTemplatesReplacePlaceholders (t * testing.T ) {
188262 rendered := []string {
189263 IslandJSSource (IslandJSOptions {
0 commit comments