mirror of
https://github.com/ChromeDevTools/chrome-devtools-mcp.git
synced 2026-11-04 15:00:11 +00:00
fix(lighthouse): explain unsupported URL schemes in navigation mode (#3144)
Closes #2969 In `navigation` mode, Lighthouse only accepts http:, https:, chrome: and chrome-extension: URLs. For anything else (file://, about:blank) it throws a bare `INVALID_URL`, which doesn't say what's wrong or that `mode: "snapshot"` works on the same page. This catches Lighthouse's `INVALID_URL` error in navigation mode and rethrows it with a message that names the URL, lists the supported schemes and suggests `mode: "snapshot"`. The original error is kept as `cause`. Other errors pass through unchanged. I catch the error rather than checking the scheme up front, so the message can't drift from Lighthouse's own `allowedProtocols` list. Tests (mocked): - navigation mode rejecting with INVALID_URL on a file:// URL → the new message, with the original error as cause (fails on main) - other navigation errors → passed through unchanged I left the `mode` parameter description as it is. Happy to add a note about file:// there too (and regenerate the docs) if you'd like.
This commit is contained in:
1 parent
91e112d3dd
commit
c6ad84a23b
2 files changed
+72
-6
No files matched your search
+20
-6
@@ -44,6 +44,13 @@ const isRecord = (value: unknown): value is Record<string, unknown> => {
|
||||
return typeof value === 'object' && value !== null;
|
||||
};
|
||||
|
||||
// Lighthouse's navigation mode only accepts the URL schemes in
|
||||
// `allowedProtocols` (core/lib/url-utils.js) and throws a bare `INVALID_URL`
|
||||
// for anything else, such as file:// or about:blank.
|
||||
const isInvalidUrlError = (error: unknown): boolean => {
|
||||
return isRecord(error) && error.code === 'INVALID_URL';
|
||||
};
|
||||
|
||||
const getString = (value: unknown): string | undefined => {
|
||||
return typeof value === 'string' ? value : undefined;
|
||||
};
|
||||
@@ -233,13 +240,20 @@ export const lighthouseAudit = definePageTool((args: ParsedArguments) => ({
|
||||
let result: RunnerResult | undefined;
|
||||
try {
|
||||
if (mode === 'navigation') {
|
||||
result = await lighthouseRunner.navigation(
|
||||
page.pptrPage,
|
||||
page.pptrPage.url(),
|
||||
{
|
||||
const url = page.pptrPage.url();
|
||||
try {
|
||||
result = await lighthouseRunner.navigation(page.pptrPage, url, {
|
||||
flags,
|
||||
},
|
||||
);
|
||||
});
|
||||
} catch (error) {
|
||||
if (isInvalidUrlError(error)) {
|
||||
throw new Error(
|
||||
`Lighthouse "navigation" mode cannot audit ${url}: it only supports http:, https:, chrome: and chrome-extension: URLs. Use mode "snapshot" to audit the page in its current state.`,
|
||||
{cause: error},
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
result = await lighthouseRunner.snapshot(page.pptrPage, {
|
||||
flags,
|
||||
|
||||
@@ -60,6 +60,58 @@ describe('lighthouse', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('explains which URLs navigation mode can audit', async () => {
|
||||
const {page, context, response, args} = createHandlerMocks();
|
||||
page.pptrPage.url.returns('file:///tmp/page.html');
|
||||
const invalidUrl = Object.assign(new Error('INVALID_URL'), {
|
||||
code: 'INVALID_URL',
|
||||
});
|
||||
sinon.stub(lighthouseRunner, 'navigation').rejects(invalidUrl);
|
||||
|
||||
await assert.rejects(
|
||||
() =>
|
||||
lighthouseAudit(args).handler(
|
||||
{
|
||||
params: {
|
||||
mode: 'navigation',
|
||||
device: 'desktop',
|
||||
},
|
||||
page,
|
||||
},
|
||||
response,
|
||||
context,
|
||||
),
|
||||
{
|
||||
message:
|
||||
'Lighthouse "navigation" mode cannot audit file:///tmp/page.html: it only supports http:, https:, chrome: and chrome-extension: URLs. Use mode "snapshot" to audit the page in its current state.',
|
||||
cause: invalidUrl,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('passes other navigation errors through unchanged', async () => {
|
||||
const {page, context, response, args} = createHandlerMocks();
|
||||
sinon
|
||||
.stub(lighthouseRunner, 'navigation')
|
||||
.rejects(new Error('PROTOCOL_TIMEOUT'));
|
||||
|
||||
await assert.rejects(
|
||||
() =>
|
||||
lighthouseAudit(args).handler(
|
||||
{
|
||||
params: {
|
||||
mode: 'navigation',
|
||||
device: 'desktop',
|
||||
},
|
||||
page,
|
||||
},
|
||||
response,
|
||||
context,
|
||||
),
|
||||
{message: 'PROTOCOL_TIMEOUT'},
|
||||
);
|
||||
});
|
||||
|
||||
it('restores emulation', async () => {
|
||||
const {page, context, response, args} = createHandlerMocks();
|
||||
context.saveTemporaryFile.resolves({filepath: 'report.json'});
|
||||
|
||||
Reference in new issue
Block a user