Skip to content

Commit 27e2479

Browse files
author
DavertMik
committed
fix(trace): use pathToFileURL for Windows-safe URLs; scan raw HTML in pageInfo
- Replace `file://${path}` template literals in lib/utils/trace.js fileToUrl() and bin/mcp-server.js with pathToFileURL().href so paths like `C:\foo\bar` are encoded correctly on Windows. - captureSnapshot now also returns out.htmlRaw (pre-cleanHtml source) so consumers can scan classes that the trash-class filter would strip. - pageInfo's error-class scan now reads htmlRaw, restoring detection of errorClasses containing digits/prefixes like `text-error` or `alert-1`.
1 parent e300b8d commit 27e2479

4 files changed

Lines changed: 43 additions & 17 deletions

File tree

bin/mcp-server.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
writeTraceMarkdown,
1515
} from '../lib/utils/trace.js'
1616
import event from '../lib/event.js'
17-
import { fileURLToPath } from 'url'
17+
import { fileURLToPath, pathToFileURL } from 'url'
1818
import { dirname, resolve as resolvePath } from 'path'
1919
import path from 'path'
2020
import { spawn } from 'child_process'
@@ -453,7 +453,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
453453
text: JSON.stringify({
454454
status: 'success',
455455
dir,
456-
traceFile: `file://${traceFile}`,
456+
traceFile: pathToFileURL(traceFile).href,
457457
artifacts: artifactsToFileUrls(captured, dir),
458458
}, null, 2),
459459
}],
@@ -551,7 +551,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
551551
error: result.error,
552552
})
553553
result.dir = traceDir
554-
result.traceFile = `file://${traceFile}`
554+
result.traceFile = pathToFileURL(traceFile).href
555555

556556
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }
557557
}
@@ -642,10 +642,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
642642
if (t.err) r.error = t.err.message
643643

644644
if (t.artifacts?.aiTrace) {
645-
r.traceFile = `file://${t.artifacts.aiTrace}`
645+
r.traceFile = pathToFileURL(t.artifacts.aiTrace).href
646646
}
647-
if (t.artifacts?.har) r.har = `file://${t.artifacts.har}`
648-
if (t.artifacts?.trace) r.trace = `file://${t.artifacts.trace}`
647+
if (t.artifacts?.har) r.har = pathToFileURL(t.artifacts.har).href
648+
if (t.artifacts?.trace) r.trace = pathToFileURL(t.artifacts.trace).href
649649

650650
if (!t.artifacts?.aiTrace) {
651651
try {
@@ -664,7 +664,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
664664
captured,
665665
error: r.error,
666666
})
667-
r.traceFile = `file://${tracePath}`
667+
r.traceFile = pathToFileURL(tracePath).href
668668
}
669669
} catch {}
670670
}

lib/plugin/pageInfo.js

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { output } from '../index.js'
99
import store from '../store.js'
1010
import { humanizeString, ucfirst } from '../utils.js'
1111
import { testToFileName } from '../mocha/test.js'
12+
1213
const defaultConfig = {
1314
errorClasses: ['error', 'warning', 'alert', 'danger'],
1415
browserLogs: ['error'],
@@ -59,15 +60,21 @@ export default function (config = {}) {
5960
if (captured.html) {
6061
const htmlPath = path.join(store.outputDir, captured.html)
6162
pageState.htmlSnapshot = htmlPath
62-
try {
63-
const html = fs.readFileSync(htmlPath, 'utf8')
64-
const errors = scanForErrorMessages(html, config.errorClasses)
65-
if (errors.length) {
66-
output.debug('Detected errors in HTML code')
67-
errors.forEach(error => output.debug(error))
68-
pageState.htmlErrors = errors
69-
}
70-
} catch {}
63+
// Scan raw HTML (pre-cleanHtml) so error classes containing digits
64+
// or trash-class prefixes aren't stripped before detection.
65+
const htmlForScan = captured.htmlRaw || (() => {
66+
try { return fs.readFileSync(htmlPath, 'utf8') } catch { return '' }
67+
})()
68+
if (htmlForScan) {
69+
try {
70+
const errors = scanForErrorMessages(htmlForScan, config.errorClasses)
71+
if (errors.length) {
72+
output.debug('Detected errors in HTML code')
73+
errors.forEach(error => output.debug(error))
74+
pageState.htmlErrors = errors
75+
}
76+
} catch {}
77+
}
7178
}
7279

7380
if (captured.aria) {

lib/utils/trace.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import crypto from 'crypto'
22
import fs from 'fs'
33
import path from 'path'
4+
import { pathToFileURL } from 'url'
45
import Container from '../container.js'
56
import { clearString } from '../utils.js'
67
import { formatHtml } from '../html.js'
@@ -63,7 +64,7 @@ export function artifactLinks(artifacts, { indent = ' ', consoleCount } = {}) {
6364
}
6465

6566
export function fileToUrl(dir, basename) {
66-
return `file://${path.join(dir, basename)}`
67+
return pathToFileURL(path.join(dir, basename)).href
6768
}
6869

6970
export function writeTraceMarkdown({ dir, title, file, durationMs, commands, captured, error }) {
@@ -193,6 +194,9 @@ export async function captureSnapshot(helper, {
193194
const file = `${prefix}_page.html`
194195
fs.writeFileSync(path.join(dir, file), formatted)
195196
out.html = file
197+
// Expose pre-cleanup HTML for consumers that need to inspect classes
198+
// stripped by cleanHtml (e.g. pageInfo's error-class scan).
199+
out.htmlRaw = html
196200
} catch {}
197201
}
198202

test/unit/utils/trace_test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ describe('lib/utils/trace.js', () => {
161161
it('joins dir + basename and prefixes with file://', () => {
162162
expect(fileToUrl('/output/run', 'p.html')).to.equal('file:///output/run/p.html')
163163
})
164+
165+
it('encodes spaces and special characters via pathToFileURL', () => {
166+
const url = fileToUrl('/output/run dir', 'p age.html')
167+
expect(url).to.equal('file:///output/run%20dir/p%20age.html')
168+
})
164169
})
165170

166171
describe('artifactsToFileUrls', () => {
@@ -339,6 +344,16 @@ describe('lib/utils/trace.js', () => {
339344
expect(html.split('\n').length).to.be.greaterThan(2)
340345
})
341346

347+
it('exposes pre-cleanup HTML in out.htmlRaw for class-scanning consumers', async () => {
348+
const raw = '<html><body><div class="text-error alert-1">boom</div></body></html>'
349+
const helper = fullHelper({ grabSource: sinon.stub().resolves(raw) })
350+
const out = await captureSnapshot(helper, { dir, prefix: 'p' })
351+
expect(out.htmlRaw).to.equal(raw)
352+
const formatted = fs.readFileSync(path.join(dir, 'p_page.html'), 'utf8')
353+
expect(formatted).to.not.include('text-error')
354+
expect(formatted).to.not.include('alert-1')
355+
})
356+
342357
it('normalizes Playwright ConsoleMessage objects to {type, text}', async () => {
343358
const playwrightLog = {
344359
type: () => 'error',

0 commit comments

Comments
 (0)