From bb36b3bb62cf6457832469134c8814448d003ead Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Fri, 7 Mar 2025 10:59:36 +0000 Subject: [PATCH] feat: "copy on pick" checkbox for locator picker --- l10n/bundle.l10n.de.json | 4 +++- l10n/bundle.l10n.fr.json | 2 ++ l10n/bundle.l10n.it.json | 2 ++ l10n/bundle.l10n.json | 2 ++ l10n/bundle.l10n.zh-CN.json | 2 ++ media/common.css | 10 +++++++--- package.json | 10 ++++++++++ package.nls.de.json | 2 ++ package.nls.fr.json | 2 ++ package.nls.it.json | 2 ++ package.nls.json | 2 ++ package.nls.zh-CN.json | 2 ++ src/extension.ts | 2 +- src/locatorsView.script.ts | 8 ++++++++ src/locatorsView.ts | 20 +++++++++++++++++++- src/reusedBrowser.ts | 2 ++ src/settingsModel.ts | 2 ++ tests/mock/vscode.ts | 6 ++++++ tests/pick-selector.spec.ts | 20 ++++++++++++++++++++ 19 files changed, 96 insertions(+), 6 deletions(-) diff --git a/l10n/bundle.l10n.de.json b/l10n/bundle.l10n.de.json index 54d23d7b9..0d100b2ed 100644 --- a/l10n/bundle.l10n.de.json +++ b/l10n/bundle.l10n.de.json @@ -1,9 +1,11 @@ { "Accept to copy locator into clipboard": "Akzeptieren, um Locator in die Zwischenablage zu kopieren", + "Automatically copy picked locator to clipboard": "Wählen Sie den Locator automatisch in die Zwischenablage", "Can't close browsers while running tests": "Browser können nicht geschlossen werden, während Tests ausgeführt werden", "Can't record while running tests": "Aufzeichnung während der Ausführung von Tests nicht möglich", "Clear cache": "Cache leeren", "Close all browsers": "Alle Browser schließen", + "Copy on pick": "Beim Auswählen kopieren", "Locator": "Locator", "No Playwright tests found.": "Keine Playwright-Tests gefunden.", "Pick locator": "Locator auswählen", @@ -16,7 +18,7 @@ "Run global setup": "Global setup ausführen", "Run global teardown": "Global teardown ausführen", "Select Playwright Config": "Playwright-Konfiguration auswählen", - "Show browser mode does not work in remote vscode": "Der Browser kann nicht mit Remote VSCode angezeigt werden", + "Show browser mode does not work in remote vscode": "Der Browser kann nicht mit Remote VSCode angezeigt werden", "Show browser": "Browser anzeigen", "Run global setup on each run": "Globales Setup jedesmal ausführen", "Show trace viewer": "Trace Viewer anzeigen", diff --git a/l10n/bundle.l10n.fr.json b/l10n/bundle.l10n.fr.json index 182f47379..bb8e424d6 100644 --- a/l10n/bundle.l10n.fr.json +++ b/l10n/bundle.l10n.fr.json @@ -1,9 +1,11 @@ { "Accept to copy locator into clipboard": "Accepter pour copier le locator dans le presse-papiers", + "Automatically copy picked locator to clipboard": "Copier automatiquement le localisateur sélectionné dans le presse-papiers", "Can't close browsers while running tests": "Impossible de fermer les navigateurs pendant l'exécution des tests", "Can't record while running tests": "Impossible d'enregistrer pendant l'exécution des tests", "Clear cache": "Vider le cache", "Close all browsers": "Fermer tous les navigateurs", + "Copy on pick": "Copier lors de la sélection", "Locator": "Locator", "No Playwright tests found.": "Aucun test Playwright trouvé.", "Pick locator": "Trouver locator", diff --git a/l10n/bundle.l10n.it.json b/l10n/bundle.l10n.it.json index 491fc5cc3..928d3e73e 100644 --- a/l10n/bundle.l10n.it.json +++ b/l10n/bundle.l10n.it.json @@ -1,9 +1,11 @@ { "Accept to copy locator into clipboard": "Accetta di copiare il locator nella clipboard", + "Automatically copy picked locator to clipboard": "Copia automaticamente il localizzatore selezionato negli appunti", "Can't close browsers while running tests": "Non e' possibile chiudere il browser mentre si eseguono i test", "Can't record while running tests": "Non e' possibile registrare mentre si eseguono i test", "Clear cache": "Pulire la cache", "Close all browsers": "Chiudere tutti i browser", + "Copy on pick": "Copia al momento della selezione", "Locator": "Locator", "No Playwright tests found.": "0 Playwright test trovati.", "Pick locator": "Seleziona il locator", diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 7fc5a44ee..fb5427b64 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -1,9 +1,11 @@ { "Accept to copy locator into clipboard": "Accept to copy locator into clipboard", + "Automatically copy picked locator to clipboard": "Automatically copy picked locator to clipboard", "Can't close browsers while running tests": "Can't close browsers while running tests", "Can't record while running tests": "Can't record while running tests", "Clear cache": "Clear cache", "Close all browsers": "Close all browsers", + "Copy on pick": "Copy on pick", "Locator": "Locator", "No Playwright tests found.": "No Playwright tests found.", "Pick locator": "Pick locator", diff --git a/l10n/bundle.l10n.zh-CN.json b/l10n/bundle.l10n.zh-CN.json index 9d9e85207..5ec89e188 100644 --- a/l10n/bundle.l10n.zh-CN.json +++ b/l10n/bundle.l10n.zh-CN.json @@ -1,9 +1,11 @@ { "Accept to copy locator into clipboard": "确认保存到剪贴板", + "Automatically copy picked locator to clipboard": "自动将选中的定位器复制到剪贴板", "Can't close browsers while running tests": "运行测试时不能关闭浏览器", "Can't record while running tests": "运行测试时不能录制", "Clear cache": "清除缓存", "Close all browsers": "关闭全部 Playwright 浏览器", + "Copy on pick": "选择时复制", "Locator": "定位器", "No Playwright tests found.": "没有找到 Playwright Test", "Pick locator": "拾取选择器", diff --git a/media/common.css b/media/common.css index ac0e9ded8..e576e391d 100644 --- a/media/common.css +++ b/media/common.css @@ -60,7 +60,7 @@ input { font-family: system-ui, Ubuntu, "Droid Sans", sans-serif, "Droid Sans Mono", "monospace", monospace; font-size: 13px; line-height: 20px; - letter-spacing: 0px; + letter-spacing: 0px; } .combobox select { @@ -85,7 +85,7 @@ textarea { margin: 10px 8px 3px 8px; font-weight: 700; color: var(--vscode-editor-inlineValuesForeground); - + display: flex; justify-content: space-between; align-items: center; @@ -168,13 +168,17 @@ body[data-vscode-theme-kind=vscode-dark] .combobox:hover { .locators-view .section { margin: 0 10px 10px; display: flex; - flex-direction: column; + flex-direction: column; } .locators-view .section > label { margin-bottom: 5px; } +.locators-view label { + flex: none; +} + .locators-view p.error { color: var(--vscode-errorForeground); } diff --git a/package.json b/package.json index 838786be7..521635f8e 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,11 @@ "command": "pw.extension.toggle.runGlobalSetupOnEachRun", "title": "%contributes.command.pw.extension.toggle.runGlobalSetupOnEachRun%" }, + { + "category": "Test", + "command": "pw.extension.toggle.pickLocatorCopyToClipboard", + "title": "%contributes.command.pw.extension.toggle.pickLocatorCopyToClipboard%" + }, { "category": "Test", "command": "pw.extension.command.runGlobalSetup", @@ -124,6 +129,11 @@ "default": false, "description": "%configuration.playwright.runGlobalSetupOnEachRun%" }, + "playwright.pickLocatorCopyToClipboard": { + "type": "boolean", + "default": false, + "description": "%configuration.playwright.pickLocatorCopyToClipboard%" + }, "playwright.updateSnapshots": { "type": "string", "enum": [ diff --git a/package.nls.de.json b/package.nls.de.json index 9566ad507..26982b428 100644 --- a/package.nls.de.json +++ b/package.nls.de.json @@ -15,10 +15,12 @@ "contributes.command.pw.extension.toggle.reuseBrowser": "Wiederverwendung des Playwright Browsers umschalten", "contributes.command.pw.extension.toggle.showTrace": "Playwright Trace Viewer umschalten", "contributes.command.pw.extension.toggle.runGlobalSetupOnEachRun": "Wiederverwendung des globalen Setups umschalten", + "contributes.command.pw.extension.toggle.pickLocatorCopyToClipboard": "Schalter für das automatische Kopieren des Playwright Locator-Pickers in die Zwischenablage umschalten", "configuration.playwright.env": "Umgebungsvariablen, die an Playwright Test übergeben werden.", "configuration.playwright.reuseBrowser": "Browser zwischen Tests anzeigen & wiederverwenden.", "configuration.playwright.showTrace": "Trace Viewer anzeigen.", "configuration.playwright.runGlobalSetupOnEachRun": "Globales Setup nicht wiederverwenden.", + "configuration.playwright.pickLocatorCopyToClipboard": "Markierten Locator automatisch in die Zwischenablage kopieren.", "configuration.playwright.updateSnapshots": "Snapshots aktualisieren", "configuration.playwright.updateSourceMethod": "Quellmethode aktualisieren", "views.test.pw.extension.locatorsView": "Locator", diff --git a/package.nls.fr.json b/package.nls.fr.json index 66c1d0db9..1a5f0e0cb 100644 --- a/package.nls.fr.json +++ b/package.nls.fr.json @@ -15,10 +15,12 @@ "contributes.command.pw.extension.toggle.reuseBrowser": "Activer/désactiver la réutilisation du navigateur", "contributes.command.pw.extension.toggle.showTrace": "Activer/désactiver Playwright Trace Viewer", "contributes.command.pw.extension.toggle.runGlobalSetupOnEachRun": "Activer/désactiver la réutilisation de la setup globale", + "contributes.command.pw.extension.toggle.pickLocatorCopyToClipboard": "Basculer la copie automatique du sélecteur Playwright dans le presse-papiers", "configuration.playwright.env": "Variables d'environnement à transmettre à Playwright Test.", "configuration.playwright.reuseBrowser": "Afficher et réutiliser le navigateur entre les tests.", "configuration.playwright.showTrace": "Afficher les traces avec Trace Viewer.", "configuration.playwright.runGlobalSetupOnEachRun": "Ne pas réutiliser la setup globale.", + "configuration.playwright.pickLocatorCopyToClipboard": "Copier automatiquement le locator sélectionné dans le presse-papiers.", "configuration.playwright.updateSnapshots": "Mettre à jour les captures d'écran", "configuration.playwright.updateSourceMethod": "Mettre à jour la méthode source", "views.test.pw.extension.locatorsView": "Localisateurs", diff --git a/package.nls.it.json b/package.nls.it.json index 337884e1d..a8c6ad948 100644 --- a/package.nls.it.json +++ b/package.nls.it.json @@ -15,10 +15,12 @@ "contributes.command.pw.extension.toggle.reuseBrowser": "Attiva/disattiva il riuso del browser Playwright", "contributes.command.pw.extension.toggle.showTrace": "Attiva/disattiva il Trace Viewer di Playwright", "contributes.command.pw.extension.toggle.runGlobalSetupOnEachRun": "Attiva/disattiva il riuso del Playwright global setup", + "contributes.command.pw.extension.toggle.pickLocatorCopyToClipboard": "Attiva/disattiva la copia automatica del selettore Playwright negli appunti", "configuration.playwright.env": "Variabili d'ambiente da passare a Playwright Test.", "configuration.playwright.reuseBrowser": "Mostra & riusa il browser tra i test", "configuration.playwright.showTrace": "Mostra il Trace Viewer.", "configuration.playwright.runGlobalSetupOnEachRun": "Esegui il global setup ad ogni run.", + "configuration.playwright.pickLocatorCopyToClipboard": "Copia automaticamente il locator selezionato negli appunti.", "configuration.playwright.updateSnapshots": "Aggiorna gli snapshot", "configuration.playwright.updateSourceMethod": "Aggiorna il metodo sorgente", "views.test.pw.extension.locatorsView": "Localizzatori", diff --git a/package.nls.json b/package.nls.json index 840c77f51..22e7be081 100644 --- a/package.nls.json +++ b/package.nls.json @@ -15,10 +15,12 @@ "contributes.command.pw.extension.toggle.reuseBrowser": "Toggle Playwright browser reuse", "contributes.command.pw.extension.toggle.showTrace": "Toggle Playwright Trace Viewer", "contributes.command.pw.extension.toggle.runGlobalSetupOnEachRun": "Toggle Playwright global setup reuse", + "contributes.command.pw.extension.toggle.pickLocatorCopyToClipboard": "Toggle Playwright locator picker automatically copying to clipboard", "configuration.playwright.env": "Environment variables to pass to Playwright Test.", "configuration.playwright.reuseBrowser": "Show & reuse browser between tests.", "configuration.playwright.showTrace": "Show Trace Viewer.", "configuration.playwright.runGlobalSetupOnEachRun": "Run global setup on each run.", + "configuration.playwright.pickLocatorCopyToClipboard": "Automatically copy picked locator to clipboard.", "configuration.playwright.updateSnapshots": "Update snapshots", "configuration.playwright.updateSourceMethod": "Update source method", "views.test.pw.extension.locatorsView": "Locators", diff --git a/package.nls.zh-CN.json b/package.nls.zh-CN.json index 957905250..8b238133e 100644 --- a/package.nls.zh-CN.json +++ b/package.nls.zh-CN.json @@ -15,10 +15,12 @@ "contributes.command.pw.extension.toggle.reuseBrowser": "是否复用 Playwright 浏览器", "contributes.command.pw.extension.toggle.showTrace": "是否显示 Playwright 浏览器的跟踪", "contributes.command.pw.extension.toggle.runGlobalSetupOnEachRun": "切换 Playwright 全局设置的重用", + "contributes.command.pw.extension.toggle.pickLocatorCopyToClipboard": "切换 Playwright 定位器选择器自动复制到剪贴板", "configuration.playwright.env": "Playwright Test 运行环境变量", "configuration.playwright.reuseBrowser": "在测试用例间显示并复用 Playwright 浏览器", "configuration.playwright.showTrace": "显示 Playwright 浏览器的跟踪", "configuration.playwright.runGlobalSetupOnEachRun": "不要重复使用全局设置", + "configuration.playwright.pickLocatorCopyToClipboard": "自动将选中的定位器复制到剪贴板", "configuration.playwright.updateSnapshots": "更新快照", "configuration.playwright.updateSourceMethod": "更新源方法", "views.test.pw.extension.locatorsView": "定位器", diff --git a/src/extension.ts b/src/extension.ts index be26d71a4..046d765fb 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -151,7 +151,7 @@ export class Extension implements RunHooks { async activate() { const vscode = this._vscode; this._settingsView = new SettingsView(vscode, this._settingsModel, this._models, this._reusedBrowser, this._context.extensionUri); - this._locatorsView = new LocatorsView(vscode, this._reusedBrowser, this._context.extensionUri); + this._locatorsView = new LocatorsView(vscode, this._settingsModel, this._reusedBrowser, this._context.extensionUri); const messageNoPlaywrightTestsFound = this._vscode.l10n.t('No Playwright tests found.'); this._disposables = [ this._debugHighlight, diff --git a/src/locatorsView.script.ts b/src/locatorsView.script.ts index f9a85fb32..96c691864 100644 --- a/src/locatorsView.script.ts +++ b/src/locatorsView.script.ts @@ -19,6 +19,7 @@ import { createAction, vscode } from './common'; // @ts-check const locatorInput = document.getElementById('locator') as HTMLInputElement; const ariaTextArea = document.getElementById('ariaSnapshot') as HTMLTextAreaElement; +const copyToClipboardCheckbox = document.getElementById('copyToClipboardCheckbox') as HTMLInputElement; locatorInput.addEventListener('input', () => { vscode.postMessage({ method: 'locatorChanged', params: { locator: locatorInput.value } }); @@ -28,6 +29,10 @@ ariaTextArea.addEventListener('input', () => { vscode.postMessage({ method: 'ariaSnapshotChanged', params: { ariaSnapshot: ariaTextArea.value } }); }); +copyToClipboardCheckbox.addEventListener('change', () => { + vscode.postMessage({ method: 'toggle', params: { setting: 'pickLocatorCopyToClipboard' } }); +}); + window.addEventListener('message', event => { const locatorError = document.getElementById('locatorError')!; const ariaSnapshotError = document.getElementById('ariaSnapshotError')!; @@ -52,5 +57,8 @@ window.addEventListener('message', event => { if (actionElement) (action.location === 'actions-2' ? actions2Element : actionsElement).appendChild(actionElement); } + } else if (method === 'settings') { + if ('pickLocatorCopyToClipboard' in params.settings) + copyToClipboardCheckbox.checked = params.settings.pickLocatorCopyToClipboard; } }); diff --git a/src/locatorsView.ts b/src/locatorsView.ts index cc22e0d65..4c1fa3d48 100644 --- a/src/locatorsView.ts +++ b/src/locatorsView.ts @@ -18,6 +18,7 @@ import { DisposableBase } from './disposableBase'; import { ReusedBrowser } from './reusedBrowser'; import { pickElementAction } from './settingsView'; import { getNonce, html } from './utils'; +import type { SettingsModel } from './settingsModel'; import * as vscodeTypes from './vscodeTypes'; export class LocatorsView extends DisposableBase implements vscodeTypes.WebviewViewProvider { @@ -26,13 +27,15 @@ export class LocatorsView extends DisposableBase implements vscodeTypes.WebviewV private _extensionUri: vscodeTypes.Uri; private _locator: { locator: string, error?: string } = { locator: '' }; private _ariaSnapshot: { yaml: string, error?: string } = { yaml: '' }; + private _settingsModel: SettingsModel; private _reusedBrowser: ReusedBrowser; private _backendVersion = 0; - constructor(vscode: vscodeTypes.VSCode, reusedBrowser: ReusedBrowser, extensionUri: vscodeTypes.Uri) { + constructor(vscode: vscodeTypes.VSCode, settingsModel: SettingsModel, reusedBrowser: ReusedBrowser, extensionUri: vscodeTypes.Uri) { super(); this._vscode = vscode; this._extensionUri = extensionUri; + this._settingsModel = settingsModel; this._reusedBrowser = reusedBrowser; this._disposables = [ vscode.window.registerWebviewViewProvider('pw.extension.locatorsView', this), @@ -45,6 +48,7 @@ export class LocatorsView extends DisposableBase implements vscodeTypes.WebviewV }), reusedBrowser.onRunningTestsChanged(() => this._updateActions()), reusedBrowser.onPageCountChanged(() => this._updateActions()), + settingsModel.onChange(() => this._updateSettings()), ]; } @@ -78,6 +82,8 @@ export class LocatorsView extends DisposableBase implements vscodeTypes.WebviewV this._ariaSnapshot.error = e.message; this._updateValues(); }); + } else if (data.method === 'toggle') { + this._vscode.commands.executeCommand(`pw.extension.toggle.${data.params.setting}`); } })); @@ -86,9 +92,11 @@ export class LocatorsView extends DisposableBase implements vscodeTypes.WebviewV return; this._updateActions(); this._updateValues(); + this._updateSettings(); })); this._updateActions(); this._updateValues(); + this._updateSettings(); } private _updateActions() { @@ -113,6 +121,11 @@ export class LocatorsView extends DisposableBase implements vscodeTypes.WebviewV } }); } + + private _updateSettings() { + if (this._view) + this._view.webview.postMessage({ method: 'settings', params: { settings: this._settingsModel.json() } }); + } } function htmlForWebview(vscode: vscodeTypes.VSCode, extensionUri: vscodeTypes.Uri, webview: vscodeTypes.Webview) { @@ -135,6 +148,11 @@ function htmlForWebview(vscode: vscodeTypes.VSCode, extensionUri: vscodeTypes.Ur
+
+

diff --git a/src/reusedBrowser.ts b/src/reusedBrowser.ts index 508cdf3e9..e2b29ccb3 100644 --- a/src/reusedBrowser.ts +++ b/src/reusedBrowser.ts @@ -123,6 +123,8 @@ export class ReusedBrowser implements vscodeTypes.Disposable { this._backend = backend; this._backend.on('inspectRequested', params => { + if (this._settingsModel.pickLocatorCopyToClipboard.get() && params.locator) + this._vscode.env.clipboard.writeText(params.locator); this._onInspectRequestedEvent.fire({ backendVersion: config.version, ...params }); }); diff --git a/src/settingsModel.ts b/src/settingsModel.ts index 0a99479b7..61755140b 100644 --- a/src/settingsModel.ts +++ b/src/settingsModel.ts @@ -46,6 +46,7 @@ export class SettingsModel extends DisposableBase { runGlobalSetupOnEachRun: Setting; updateSnapshots: Setting<'all' | 'changed' | 'missing' | 'none' | 'no-override'>; updateSourceMethod: Setting<'overwrite' | 'patch' | '3way' | 'no-override'>; + pickLocatorCopyToClipboard: Setting; constructor(vscode: vscodeTypes.VSCode, context: vscodeTypes.ExtensionContext) { super(); @@ -59,6 +60,7 @@ export class SettingsModel extends DisposableBase { this.runGlobalSetupOnEachRun = this._createSetting('runGlobalSetupOnEachRun'); this.updateSnapshots = this._createSetting('updateSnapshots'); this.updateSourceMethod = this._createSetting('updateSourceMethod'); + this.pickLocatorCopyToClipboard = this._createSetting('pickLocatorCopyToClipboard'); this._disposables.push( this._onChange, diff --git a/tests/mock/vscode.ts b/tests/mock/vscode.ts index f1b46e8fe..dd83bb936 100644 --- a/tests/mock/vscode.ts +++ b/tests/mock/vscode.ts @@ -971,6 +971,7 @@ export class VSCode { readonly connectionLog: any[] = []; readonly openExternalUrls: string[] = []; readonly diagnosticsCollections: DiagnosticsCollection[] = []; + private _clipboardText = ''; constructor(readonly versionNumber: number, baseDir: string, browser: Browser) { this.version = String(versionNumber); @@ -1186,6 +1187,11 @@ export class VSCode { }, }; }; + + this.env.clipboard = { + writeText: async (text: string) => this._clipboardText = text, + readText: async () => this._clipboardText, + }; } async activate() { diff --git a/tests/pick-selector.spec.ts b/tests/pick-selector.spec.ts index 9eaeb3bf6..b62421520 100644 --- a/tests/pick-selector.spec.ts +++ b/tests/pick-selector.spec.ts @@ -70,3 +70,23 @@ test('should highlight locator on edit', async ({ activate }) => { await expect(page.locator('x-pw-highlight')).toBeVisible(); expect(await page.locator('x-pw-highlight').boundingBox()).toEqual(box); }); + +test('should copy locator to clipboard', async ({ activate }) => { + const { vscode } = await activate({ + 'playwright.config.js': `module.exports = {}`, + }); + + const locatorsView = vscode.webViews.get('pw.extension.locatorsView')!; + await locatorsView.getByRole('checkbox', { name: 'Copy on pick' }).check(); + await locatorsView.getByRole('button', { name: 'Pick locator' }).first().click(); + + const browser = await connectToSharedBrowser(vscode); + const page = await waitForPage(browser); + await page.setContent(` +

Hello

+

World

+ `); + await page.locator('h1').first().click(); + + await expect.poll(() => vscode.env.clipboard.readText()).toBe(`getByRole('heading', { name: 'Hello' })`); +});