chore: show download link on the status mismatch page (#928)

<img width="610" height="108" alt="image"
src="https://github.com/user-attachments/assets/bc1f7534-f282-44f0-bb58-e385b2bf82af"
/>
This commit is contained in:
Yury Semikhatsky
2025-08-22 09:10:53 -07:00
committed by GitHub
parent 94ca0763d5
commit fb65bc7559
3 changed files with 52 additions and 8 deletions

View File

@@ -193,3 +193,14 @@ body {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
/* Link-style button */
.link-button {
background: none;
border: none;
color: #0066cc;
text-decoration: underline;
cursor: pointer;
padding: 0;
font: inherit;
}

View File

@@ -23,7 +23,7 @@ type Status =
| { type: 'connecting'; message: string } | { type: 'connecting'; message: string }
| { type: 'connected'; message: string } | { type: 'connected'; message: string }
| { type: 'error'; message: string } | { type: 'error'; message: string }
| { type: 'error'; versionMismatch: { pwMcpVersion: string; extensionVersion: string } }; | { type: 'error'; versionMismatch: { pwMcpVersion: string; extensionVersion: string; downloadUrl: string } };
const ConnectApp: React.FC = () => { const ConnectApp: React.FC = () => {
const [tabs, setTabs] = useState<TabInfo[]>([]); const [tabs, setTabs] = useState<TabInfo[]>([]);
@@ -61,13 +61,15 @@ const ConnectApp: React.FC = () => {
const pwMcpVersion = params.get('pwMcpVersion'); const pwMcpVersion = params.get('pwMcpVersion');
const extensionVersion = chrome.runtime.getManifest().version; const extensionVersion = chrome.runtime.getManifest().version;
if (pwMcpVersion !== extensionVersion) { if (pwMcpVersion !== extensionVersion) {
const downloadUrl = params.get('downloadUrl') || `https://github.com/microsoft/playwright-mcp/releases/download/v${extensionVersion}/playwright-mcp-extension-v${extensionVersion}.zip`;
setShowButtons(false); setShowButtons(false);
setShowTabList(false); setShowTabList(false);
setStatus({ setStatus({
type: 'error', type: 'error',
versionMismatch: { versionMismatch: {
pwMcpVersion: pwMcpVersion || 'unknown', pwMcpVersion: pwMcpVersion || 'unknown',
extensionVersion extensionVersion,
downloadUrl
} }
}); });
return; return;
@@ -176,13 +178,34 @@ const ConnectApp: React.FC = () => {
); );
}; };
const VersionMismatchError: React.FC<{ pwMcpVersion: string; extensionVersion: string }> = ({ pwMcpVersion, extensionVersion }) => { const VersionMismatchError: React.FC<{ pwMcpVersion: string; extensionVersion: string; downloadUrl: string }> = ({ pwMcpVersion, extensionVersion, downloadUrl }) => {
const readmeUrl = 'https://github.com/microsoft/playwright-mcp/blob/main/extension/README.md'; const readmeUrl = 'https://github.com/microsoft/playwright-mcp/blob/main/extension/README.md';
const handleDownloadAndOpenExtensions = () => {
// Start download
const link = document.createElement('a');
link.href = downloadUrl;
link.download = `playwright-mcp-extension-v${extensionVersion}.zip`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
setTimeout(() => {
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
if (tabs[0]?.id)
chrome.tabs.update(tabs[0].id, { url: 'chrome://extensions/' });
});
}, 1000); // Wait 1 second for download to initiate
};
return ( return (
<div> <div>
Incompatible Playwright MCP version: {pwMcpVersion} (extension version: {extensionVersion}). Incompatible Playwright MCP version: {pwMcpVersion} (extension version: {extensionVersion}).{' '}
Please install the latest version of the extension.{' '} <button
See <a href={readmeUrl} target='_blank' rel='noopener noreferrer'>installation instructions</a>. onClick={handleDownloadAndOpenExtensions}
className='link-button'
>Click here</button> to download the matching extension, then drag and drop it into the Chrome Extensions page.{' '}
See <a href={readmeUrl} target='_blank' rel='noopener noreferrer'>installation instructions</a> for more details.
</div> </div>
); );
}; };
@@ -191,7 +214,11 @@ const StatusBanner: React.FC<{ status: Status }> = ({ status }) => {
return ( return (
<div className={`status-banner ${status.type}`}> <div className={`status-banner ${status.type}`}>
{'versionMismatch' in status ? ( {'versionMismatch' in status ? (
<VersionMismatchError pwMcpVersion={status.versionMismatch.pwMcpVersion} extensionVersion={status.versionMismatch.extensionVersion} /> <VersionMismatchError
pwMcpVersion={status.versionMismatch.pwMcpVersion}
extensionVersion={status.versionMismatch.extensionVersion}
downloadUrl={status.versionMismatch.downloadUrl}
/>
) : ( ) : (
status.message status.message
)} )}

View File

@@ -233,12 +233,18 @@ for (const [mode, startClientMethod] of [
}); });
const confirmationPage = await confirmationPagePromise; const confirmationPage = await confirmationPagePromise;
await expect(confirmationPage.locator('.status-banner')).toHaveText(`Incompatible Playwright MCP version: ${packageJSON.version} (extension version: 0.0.1). Please install the latest version of the extension. See installation instructions.`); await expect(confirmationPage.locator('.status-banner')).toHaveText(`Incompatible Playwright MCP version: ${packageJSON.version} (extension version: 0.0.1). Click here to download the matching extension, then drag and drop it into the Chrome Extensions page. See installation instructions for more details.`);
expect(await navigateResponse).toHaveResponse({ expect(await navigateResponse).toHaveResponse({
result: expect.stringContaining('Extension connection timeout.'), result: expect.stringContaining('Extension connection timeout.'),
isError: true, isError: true,
}); });
const downloadPromise = confirmationPage.waitForEvent('download');
await confirmationPage.locator('.status-banner').getByRole('button', { name: 'Click here' }).click();
const download = await downloadPromise;
expect(download.url()).toBe(`https://github.com/microsoft/playwright-mcp/releases/download/v0.0.1/playwright-mcp-extension-v0.0.1.zip`);
await download.cancel();
}); });
} }