diff --git a/packages/playwright-core/src/server/injected/highlight.css.ts b/packages/playwright-core/src/server/injected/highlight.css.ts
new file mode 100644
index 0000000000..18955755ba
--- /dev/null
+++ b/packages/playwright-core/src/server/injected/highlight.css.ts
@@ -0,0 +1,172 @@
+/**
+ * Copyright (c) Microsoft Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export const highlightCSS = `
+x-pw-tooltip {
+ backdrop-filter: blur(5px);
+ background-color: white;
+ color: #222;
+ border-radius: 6px;
+ box-shadow: 0 0.5rem 1.2rem rgba(0,0,0,.3);
+ display: none;
+ font-family: 'Dank Mono', 'Operator Mono', Inconsolata, 'Fira Mono',
+ 'SF Mono', Monaco, 'Droid Sans Mono', 'Source Code Pro', monospace;
+ font-size: 12.8px;
+ font-weight: normal;
+ left: 0;
+ line-height: 1.5;
+ max-width: 600px;
+ position: absolute;
+ top: 0;
+}
+x-pw-tooltip-body {
+ align-items: center;
+ padding: 3.2px 5.12px 3.2px;
+}
+x-pw-highlight {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 0;
+ height: 0;
+}
+x-pw-action-point {
+ position: absolute;
+ width: 20px;
+ height: 20px;
+ background: red;
+ border-radius: 10px;
+ margin: -10px 0 0 -10px;
+ z-index: 2;
+}
+x-pw-separator {
+ height: 1px;
+ margin: 6px 9px;
+ background: rgb(148 148 148 / 90%);
+}
+x-pw-tool-gripper {
+ height: 28px;
+ width: 24px;
+ margin: 2px 0;
+ cursor: grab;
+}
+x-pw-tool-gripper:active {
+ cursor: grabbing;
+}
+x-pw-tool-gripper > x-div {
+ width: 100%;
+ height: 100%;
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-position: center;
+ -webkit-mask-size: 20px;
+ mask-repeat: no-repeat;
+ mask-position: center;
+ mask-size: 16px;
+ -webkit-mask-image: url("data:image/svg+xml;utf8,");
+ mask-image: url("data:image/svg+xml;utf8,");
+ background-color: #555555;
+}
+x-pw-tools-list {
+ display: flex;
+ width: 100%;
+ border-bottom: 1px solid #dddddd;
+}
+x-pw-tool-item {
+ pointer-events: auto;
+ cursor: pointer;
+ height: 28px;
+ width: 28px;
+ border-radius: 3px;
+}
+x-pw-tool-item:not(.disabled):hover {
+ background-color: hsl(0, 0%, 86%);
+}
+x-pw-tool-item > x-div {
+ width: 100%;
+ height: 100%;
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-position: center;
+ -webkit-mask-size: 20px;
+ mask-repeat: no-repeat;
+ mask-position: center;
+ mask-size: 16px;
+ background-color: #3a3a3a;
+}
+x-pw-tool-item.disabled > x-div {
+ background-color: rgba(97, 97, 97, 0.5);
+ cursor: default;
+}
+x-pw-tool-item.active > x-div {
+ background-color: #006ab1;
+}
+x-pw-tool-item.record.active > x-div {
+ background-color: #a1260d;
+}
+x-pw-tool-item.accept > x-div {
+ background-color: #388a34;
+}
+x-pw-tool-item.cancel > x-div {
+ background-color: #e51400;
+}
+x-pw-tool-item.record > x-div {
+ /* codicon: circle-large-filled */
+ -webkit-mask-image: url("data:image/svg+xml;utf8,");
+ mask-image: url("data:image/svg+xml;utf8,");
+}
+x-pw-tool-item.pick-locator > x-div {
+ /* codicon: inspect */
+ -webkit-mask-image: url("data:image/svg+xml;utf8,");
+ mask-image: url("data:image/svg+xml;utf8,");
+}
+x-pw-tool-item.assert > x-div {
+ /* codicon: check-all */
+ -webkit-mask-image: url("data:image/svg+xml;utf8,");
+ mask-image: url("data:image/svg+xml;utf8,");
+}
+x-pw-tool-item.accept > x-div {
+ -webkit-mask-image: url("data:image/svg+xml;utf8,");
+ mask-image: url("data:image/svg+xml;utf8,");
+}
+x-pw-tool-item.cancel > x-div {
+ -webkit-mask-image: url("data:image/svg+xml;utf8,");
+ mask-image: url("data:image/svg+xml;utf8,");
+}
+x-pw-overlay {
+ position: absolute;
+ top: 0;
+ max-width: min-content;
+ z-index: 2147483647;
+ background: transparent;
+ pointer-events: auto;
+}
+x-pw-overlay x-pw-tools-list {
+ background-color: #ffffffdd;
+ box-shadow: rgba(0, 0, 0, 0.1) 0px 5px 5px;
+ border-radius: 3px;
+ border-bottom: none;
+}
+x-pw-overlay x-pw-tool-item {
+ margin: 2px;
+}
+x-div {
+ display: block;
+}
+* {
+ box-sizing: border-box;
+}
+*[hidden] {
+ display: none !important;
+}`;
diff --git a/packages/playwright-core/src/server/injected/highlight.ts b/packages/playwright-core/src/server/injected/highlight.ts
index 16dd044949..d6253d3292 100644
--- a/packages/playwright-core/src/server/injected/highlight.ts
+++ b/packages/playwright-core/src/server/injected/highlight.ts
@@ -19,6 +19,7 @@ import type { ParsedSelector } from '../../utils/isomorphic/selectorParser';
import type { InjectedScript } from './injectedScript';
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
import type { Language } from '../../utils/isomorphic/locatorGenerators';
+import { highlightCSS } from './highlight.css';
type HighlightEntry = {
targetElement: Element,
@@ -34,7 +35,8 @@ export type HighlightOptions = {
tooltipText?: string;
color?: string;
anchorGetter?: (element: Element) => DOMRect;
- decorateTooltip?: (tooltip: Element) => void;
+ toolbar?: Element[];
+ interactive?: boolean;
};
export class Highlight {
@@ -67,44 +69,7 @@ export class Highlight {
this._glassPaneShadow = this._glassPaneElement.attachShadow({ mode: this._isUnderTest ? 'open' : 'closed' });
this._glassPaneShadow.appendChild(this._actionPointElement);
const styleElement = document.createElement('style');
- styleElement.textContent = `
- x-pw-tooltip {
- align-items: center;
- backdrop-filter: blur(5px);
- background-color: rgba(0, 0, 0, 0.7);
- border-radius: 2px;
- box-shadow: rgba(0, 0, 0, 0.1) 0px 3.6px 3.7px,
- rgba(0, 0, 0, 0.15) 0px 12.1px 12.3px,
- rgba(0, 0, 0, 0.1) 0px -2px 4px,
- rgba(0, 0, 0, 0.15) 0px -12.1px 24px,
- rgba(0, 0, 0, 0.25) 0px 54px 55px;
- color: rgb(204, 204, 204);
- display: none;
- font-family: 'Dank Mono', 'Operator Mono', Inconsolata, 'Fira Mono',
- 'SF Mono', Monaco, 'Droid Sans Mono', 'Source Code Pro', monospace;
- font-size: 12.8px;
- font-weight: normal;
- left: 0;
- line-height: 1.5;
- max-width: 600px;
- padding: 3.2px 5.12px 3.2px;
- position: absolute;
- top: 0;
- }
- x-pw-action-point {
- position: absolute;
- width: 20px;
- height: 20px;
- background: red;
- border-radius: 10px;
- pointer-events: none;
- margin: -10px 0 0 -10px;
- z-index: 2;
- }
- *[hidden] {
- display: none !important;
- }
- `;
+ styleElement.textContent = highlightCSS;
this._glassPaneShadow.appendChild(styleElement);
}
@@ -180,13 +145,26 @@ export class Highlight {
let tooltipElement;
if (options.tooltipText) {
tooltipElement = this._injectedScript.document.createElement('x-pw-tooltip');
- this._glassPaneShadow.appendChild(tooltipElement);
- const suffix = elements.length > 1 ? ` [${i + 1} of ${elements.length}]` : '';
- tooltipElement.textContent = options.tooltipText + suffix;
tooltipElement.style.top = '0';
tooltipElement.style.left = '0';
tooltipElement.style.display = 'flex';
- options.decorateTooltip?.(tooltipElement);
+ tooltipElement.style.flexDirection = 'column';
+ tooltipElement.style.alignItems = 'start';
+ if (options.interactive)
+ tooltipElement.style.pointerEvents = 'auto';
+
+ if (options.toolbar) {
+ const toolbar = this._injectedScript.document.createElement('x-pw-tools-list');
+ tooltipElement.appendChild(toolbar);
+ for (const toolbarElement of options.toolbar)
+ toolbar.appendChild(toolbarElement);
+ }
+ const bodyElement = this._injectedScript.document.createElement('x-pw-tooltip-body');
+ tooltipElement.appendChild(bodyElement);
+
+ this._glassPaneShadow.appendChild(tooltipElement);
+ const suffix = elements.length > 1 ? ` [${i + 1} of ${elements.length}]` : '';
+ bodyElement.textContent = options.tooltipText + suffix;
}
this._highlightEntries.push({ targetElement: elements[i], tooltipElement, highlightElement, tooltipText: options.tooltipText });
}
@@ -260,13 +238,10 @@ export class Highlight {
}
private _createHighlightElement(): HTMLElement {
- const highlightElement = this._injectedScript.document.createElement('x-pw-highlight');
- highlightElement.style.position = 'absolute';
- highlightElement.style.top = '0';
- highlightElement.style.left = '0';
- highlightElement.style.width = '0';
- highlightElement.style.height = '0';
- highlightElement.style.boxSizing = 'border-box';
- return highlightElement;
+ return this._injectedScript.document.createElement('x-pw-highlight');
+ }
+
+ appendChild(element: HTMLElement) {
+ this._glassPaneShadow.appendChild(element);
}
}
diff --git a/packages/playwright-core/src/server/injected/recorder.ts b/packages/playwright-core/src/server/injected/recorder.ts
index f262fa19bc..19545c15f5 100644
--- a/packages/playwright-core/src/server/injected/recorder.ts
+++ b/packages/playwright-core/src/server/injected/recorder.ts
@@ -36,8 +36,7 @@ interface RecorderDelegate {
interface RecorderTool {
cursor(): string;
- enable?(): void;
- disable?(): void;
+ cleanup?(): void;
onClick?(event: MouseEvent): void;
onDragStart?(event: DragEvent): void;
onInput?(event: Event): void;
@@ -70,7 +69,7 @@ class InspectTool implements RecorderTool {
return 'pointer';
}
- disable() {
+ cleanup() {
this._hoveredModel = null;
this._hoveredElement = null;
}
@@ -151,7 +150,7 @@ class RecordActionTool implements RecorderTool {
return 'pointer';
}
- disable() {
+ cleanup() {
this._hoveredModel = null;
this._hoveredElement = null;
this._activeModel = null;
@@ -447,25 +446,25 @@ class TextAssertionTool implements RecorderTool {
private _selectionText: { selectedText: string, fullText: string } | null = null;
private _inputHighlight: HighlightModel | null = null;
private _acceptButton: HTMLElement;
+ private _cancelButton: HTMLElement;
constructor(private _recorder: Recorder) {
- this._acceptButton = this._recorder.document.createElement('button');
- this._acceptButton.textContent = 'Accept';
- this._acceptButton.style.cursor = 'pointer';
- this._acceptButton.style.pointerEvents = 'auto';
+ this._acceptButton = this._recorder.document.createElement('x-pw-tool-item');
+ this._acceptButton.classList.add('accept');
+ this._acceptButton.appendChild(this._recorder.document.createElement('x-div'));
this._acceptButton.addEventListener('click', () => this._commitAction());
+
+ this._cancelButton = this._recorder.document.createElement('x-pw-tool-item');
+ this._cancelButton.classList.add('cancel');
+ this._cancelButton.appendChild(this._recorder.document.createElement('x-div'));
+ this._cancelButton.addEventListener('click', () => this._cancelAction());
}
cursor() {
return 'text';
}
- enable() {
- this._recorder.injectedScript.document.designMode = 'on';
- }
-
- disable() {
- this._recorder.injectedScript.document.designMode = 'off';
+ cleanup() {
this._hoverHighlight = null;
this._selectionHighlight = null;
this._selectionText = null;
@@ -473,11 +472,6 @@ class TextAssertionTool implements RecorderTool {
}
onClick(event: MouseEvent) {
- // Hack: work around highlight's glass pane having a closed shadow root.
- const box = this._acceptButton.getBoundingClientRect();
- if (box.left <= event.clientX && event.clientX <= box.right && box.top <= event.clientY && event.clientY <= box.bottom)
- return;
-
consumeEvent(event);
const selection = this._recorder.document.getSelection();
if (event.detail === 1 && selection && !selection.toString() && !this._inputHighlight) {
@@ -612,6 +606,10 @@ class TextAssertionTool implements RecorderTool {
}
}
+ private _cancelAction() {
+ this._resetSelectionAndHighlight();
+ }
+
private _resetSelectionAndHighlight() {
this._selectionHighlight = null;
this._selectionText = null;
@@ -643,7 +641,12 @@ class TextAssertionTool implements RecorderTool {
}
private _showHighlight(userGesture: boolean) {
- const options: HighlightOptions = { color: '#6fdcbd38', tooltipText: this._generateActionPreview(), decorateTooltip: tooltip => tooltip.appendChild(this._acceptButton) };
+ const options: HighlightOptions = {
+ color: '#6fdcbd38',
+ tooltipText: this._generateActionPreview(),
+ toolbar: [this._acceptButton, this._cancelButton],
+ interactive: true,
+ };
if (this._inputHighlight) {
this._recorder.updateHighlight(this._inputHighlight, userGesture, options);
} else {
@@ -665,119 +668,21 @@ class Overlay {
constructor(private _recorder: Recorder) {
const document = this._recorder.injectedScript.document;
this._overlayElement = document.createElement('x-pw-overlay');
- this._overlayElement.style.top = '0';
- this._overlayElement.style.position = 'absolute';
-
- const shadow = this._overlayElement.attachShadow({ mode: 'closed' });
- const styleElement = document.createElement('style');
- styleElement.textContent = `
- :host {
- position: fixed;
- max-width: min-content;
- z-index: 2147483647;
- background: transparent;
- }
-
- x-pw-tools-list {
- box-shadow: rgba(0, 0, 0, 0.1) 0px 5px 5px;
- backdrop-filter: blur(5px);
- background-color: hsla(0 0% 100% / .9);
- font-family: 'Dank Mono', 'Operator Mono', Inconsolata, 'Fira Mono', 'SF Mono', Monaco, 'Droid Sans Mono', 'Source Code Pro', monospace;
- display: flex;
- border-radius: 3px;
- }
-
- x-pw-separator {
- height: 1px;
- margin: 6px 9px;
- background: rgb(148 148 148 / 90%);
- }
-
- x-pw-tool-item {
- cursor: pointer;
- height: 28px;
- width: 28px;
- margin: 2px 4px;
- border-radius: 3px;
- }
- x-pw-tool-item:not(.disabled):hover {
- background-color: hsl(0, 0%, 86%);
- }
- x-pw-tool-item > div {
- width: 100%;
- height: 100%;
- -webkit-mask-repeat: no-repeat;
- -webkit-mask-position: center;
- -webkit-mask-size: 20px;
- mask-repeat: no-repeat;
- mask-position: center;
- mask-size: 16px;
- background-color: #3a3a3a;
- }
- x-pw-tool-item.disabled > div {
- background-color: rgba(97, 97, 97, 0.5);
- cursor: default;
- }
- x-pw-tool-item.active > div {
- background-color: #006ab1;
- }
- x-pw-tool-item.record.active > div {
- background-color: #a1260d;
- }
- x-pw-tool-gripper {
- height: 28px;
- width: 24px;
- margin: 2px 0;
- cursor: grab;
- }
- x-pw-tool-gripper:active {
- cursor: grabbing;
- }
- x-pw-tool-gripper > div {
- width: 100%;
- height: 100%;
- -webkit-mask-repeat: no-repeat;
- -webkit-mask-position: center;
- -webkit-mask-size: 20px;
- mask-repeat: no-repeat;
- mask-position: center;
- mask-size: 16px;
- -webkit-mask-image: url("data:image/svg+xml;utf8,");
- mask-image: url("data:image/svg+xml;utf8,");
- background-color: #555555;
- }
- x-pw-tool-item.record > div {
- /* codicon: circle-large-filled */
- -webkit-mask-image: url("data:image/svg+xml;utf8,");
- mask-image: url("data:image/svg+xml;utf8,");
- }
- x-pw-tool-item.pick-locator > div {
- /* codicon: inspect */
- -webkit-mask-image: url("data:image/svg+xml;utf8,");
- mask-image: url("data:image/svg+xml;utf8,");
- }
- x-pw-tool-item.assert > div {
- /* codicon: check-all */
- -webkit-mask-image: url("data:image/svg+xml;utf8,");
- mask-image: url("data:image/svg+xml;utf8,");
- }
- `;
- shadow.appendChild(styleElement);
const toolsListElement = document.createElement('x-pw-tools-list');
- shadow.appendChild(toolsListElement);
+ this._overlayElement.appendChild(toolsListElement);
const dragHandle = document.createElement('x-pw-tool-gripper');
dragHandle.addEventListener('mousedown', event => {
this._dragState = { offsetX: this._offsetX, dragStart: { x: event.clientX, y: 0 } };
});
- dragHandle.appendChild(document.createElement('div'));
+ dragHandle.appendChild(document.createElement('x-div'));
toolsListElement.appendChild(dragHandle);
this._recordToggle = this._recorder.injectedScript.document.createElement('x-pw-tool-item');
this._recordToggle.title = 'Record';
this._recordToggle.classList.add('record');
- this._recordToggle.appendChild(this._recorder.injectedScript.document.createElement('div'));
+ this._recordToggle.appendChild(this._recorder.injectedScript.document.createElement('x-div'));
this._recordToggle.addEventListener('click', () => {
this._recorder.delegate.setMode?.(this._recorder.state.mode === 'none' || this._recorder.state.mode === 'inspecting' ? 'recording' : 'none');
});
@@ -786,7 +691,7 @@ class Overlay {
this._pickLocatorToggle = this._recorder.injectedScript.document.createElement('x-pw-tool-item');
this._pickLocatorToggle.title = 'Pick locator';
this._pickLocatorToggle.classList.add('pick-locator');
- this._pickLocatorToggle.appendChild(this._recorder.injectedScript.document.createElement('div'));
+ this._pickLocatorToggle.appendChild(this._recorder.injectedScript.document.createElement('x-div'));
this._pickLocatorToggle.addEventListener('click', () => {
const newMode: Record = {
'inspecting': 'none',
@@ -802,7 +707,7 @@ class Overlay {
this._assertToggle = this._recorder.injectedScript.document.createElement('x-pw-tool-item');
this._assertToggle.title = 'Assert text and values';
this._assertToggle.classList.add('assert');
- this._assertToggle.appendChild(this._recorder.injectedScript.document.createElement('div'));
+ this._assertToggle.appendChild(this._recorder.injectedScript.document.createElement('x-div'));
this._assertToggle.addEventListener('click', () => {
if (!this._assertToggle.classList.contains('disabled'))
this._recorder.delegate.setMode?.(this._recorder.state.mode === 'assertingText' ? 'recording' : 'assertingText');
@@ -813,7 +718,7 @@ class Overlay {
}
install() {
- this._recorder.injectedScript.document.documentElement.appendChild(this._overlayElement);
+ this._recorder.highlight.appendChild(this._overlayElement);
this._measure = this._overlayElement.getBoundingClientRect();
}
@@ -877,7 +782,7 @@ export class Recorder {
private _currentTool: RecorderTool;
private _tools: Record;
private _actionSelectorModel: HighlightModel | null = null;
- private _highlight: Highlight;
+ readonly highlight: Highlight;
private _overlay: Overlay | undefined;
private _styleElement: HTMLStyleElement;
state: UIState = { mode: 'none', testIdAttributeName: 'data-testid', language: 'javascript', overlay: { offsetX: 0 } };
@@ -887,7 +792,7 @@ export class Recorder {
constructor(injectedScript: InjectedScript) {
this.document = injectedScript.document;
this.injectedScript = injectedScript;
- this._highlight = new Highlight(injectedScript);
+ this.highlight = new Highlight(injectedScript);
this._tools = {
'none': new NoneTool(),
'inspecting': new InspectTool(this),
@@ -913,7 +818,7 @@ export class Recorder {
installListeners() {
// Ensure we are attached to the current document, and we are on top (last element);
- if (this._highlight.isInstalled())
+ if (this.highlight.isInstalled())
return;
removeEventListeners(this._listeners);
this._listeners = [
@@ -932,7 +837,7 @@ export class Recorder {
addEventListener(this.document, 'focus', event => this._onFocus(event), true),
addEventListener(this.document, 'scroll', event => this._onScroll(event), true),
];
- this._highlight.install();
+ this.highlight.install();
this._overlay?.install();
this.injectedScript.document.head.appendChild(this._styleElement);
}
@@ -941,10 +846,9 @@ export class Recorder {
const newTool = this._tools[this.state.mode];
if (newTool === this._currentTool)
return;
- this._currentTool.disable?.();
+ this._currentTool.cleanup?.();
this.clearHighlight();
this._currentTool = newTool;
- this._currentTool.enable?.();
this.injectedScript.document.body?.setAttribute('data-pw-cursor', newTool.cursor());
}
@@ -957,13 +861,13 @@ export class Recorder {
// All good.
} else {
if (state.actionPoint)
- this._highlight.showActionPoint(state.actionPoint.x, state.actionPoint.y);
+ this.highlight.showActionPoint(state.actionPoint.x, state.actionPoint.y);
else
- this._highlight.hideActionPoint();
+ this.highlight.hideActionPoint();
}
this.state = state;
- this._highlight.setLanguage(state.language);
+ this.highlight.setLanguage(state.language);
this._switchCurrentTool();
this._overlay?.setUIState(state);
@@ -977,7 +881,7 @@ export class Recorder {
}
clearHighlight() {
- this._currentTool.disable?.();
+ this._currentTool.cleanup?.();
this.updateHighlight(null, false);
}
@@ -1062,7 +966,7 @@ export class Recorder {
private _onScroll(event: Event) {
if (!event.isTrusted)
return;
- this._highlight.hideActionPoint();
+ this.highlight.hideActionPoint();
this._currentTool.onScroll?.(event);
}
@@ -1091,13 +995,14 @@ export class Recorder {
updateHighlight(model: HighlightModel | null, userGesture: boolean, options: HighlightOptions = {}) {
if (options.tooltipText === undefined && model?.selector)
options.tooltipText = asLocator(this.state.language, model.selector);
- this._highlight.updateHighlight(model?.elements || [], options);
+ this.highlight.updateHighlight(model?.elements || [], options);
if (userGesture)
this.delegate.highlightUpdated?.();
}
private _ignoreOverlayEvent(event: Event) {
- return this._overlay?.contains(event.composedPath()[0] as Element);
+ const target = event.composedPath()[0] as Element;
+ return target.nodeName.toLowerCase() === 'x-pw-glass';
}
deepEventTarget(event: Event): HTMLElement {
diff --git a/packages/playwright-core/src/server/recorder/recorderApp.ts b/packages/playwright-core/src/server/recorder/recorderApp.ts
index 6f56db67b7..37ff723dff 100644
--- a/packages/playwright-core/src/server/recorder/recorderApp.ts
+++ b/packages/playwright-core/src/server/recorder/recorderApp.ts
@@ -176,9 +176,9 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
this._recorder.setMode('recording');
}
}
- await this._page.mainFrame().evaluateExpression(((selector: string) => {
- window.playwrightSetSelector(selector);
- }).toString(), { isFunction: true }, selector).catch(() => {});
+ await this._page.mainFrame().evaluateExpression(((data: { selector: string, userGesture?: boolean }) => {
+ window.playwrightSetSelector(data.selector, data.userGesture);
+ }).toString(), { isFunction: true }, { selector, userGesture }).catch(() => {});
}
async updateCallLogs(callLogs: CallLog[]): Promise {
diff --git a/packages/recorder/src/recorder.tsx b/packages/recorder/src/recorder.tsx
index 6a1c6eb325..7892791183 100644
--- a/packages/recorder/src/recorder.tsx
+++ b/packages/recorder/src/recorder.tsx
@@ -66,8 +66,10 @@ export const Recorder: React.FC = ({
};
const [locator, setLocator] = React.useState('');
- window.playwrightSetSelector = (selector: string) => {
+ window.playwrightSetSelector = (selector: string, focus?: boolean) => {
const language = source.language;
+ if (focus)
+ setSelectedTab('locator');
setLocator(asLocator(language, selector));
};
@@ -126,7 +128,6 @@ export const Recorder: React.FC = ({
'assertingText': 'recording-inspecting',
}[mode];
window.dispatch({ event: 'setMode', params: { mode: newMode } }).catch(() => { });
- setSelectedTab('locator');
}}>Pick locator
{
window.dispatch({ event: 'setMode', params: { mode: mode === 'assertingText' ? 'recording' : 'assertingText' } });
@@ -155,7 +156,7 @@ export const Recorder: React.FC = ({
}}>
toggleTheme()}>
-
+
copy(locator)} />] : []}
@@ -163,7 +164,7 @@ export const Recorder: React.FC = ({
{
id: 'locator',
title: 'Locator',
- render: () =>
+ render: () =>
},
{
id: 'log',
diff --git a/packages/web/src/components/codeMirrorWrapper.css b/packages/web/src/components/codeMirrorWrapper.css
index 5abb4cc722..1e79c18274 100644
--- a/packages/web/src/components/codeMirrorWrapper.css
+++ b/packages/web/src/components/codeMirrorWrapper.css
@@ -16,6 +16,10 @@
@import '../third_party/vscode/colors.css';
+.cm-wrapper {
+ line-height: 18px;
+}
+
.cm-wrapper, .cm-wrapper > div {
width: 100%;
height: 100%;
diff --git a/packages/web/src/components/codeMirrorWrapper.tsx b/packages/web/src/components/codeMirrorWrapper.tsx
index 3f64a1d944..738f0cfff1 100644
--- a/packages/web/src/components/codeMirrorWrapper.tsx
+++ b/packages/web/src/components/codeMirrorWrapper.tsx
@@ -93,7 +93,6 @@ export const CodeMirrorWrapper: React.FC = ({
// Either configuration is different or we don't have a codemirror yet.
codemirrorRef.current?.cm?.getWrapperElement().remove();
-
const cm = CodeMirror(element, {
value: '',
mode,
diff --git a/packages/web/src/components/toolbarButton.css b/packages/web/src/components/toolbarButton.css
index bdf6db9ad9..813d9e4b91 100644
--- a/packages/web/src/components/toolbarButton.css
+++ b/packages/web/src/components/toolbarButton.css
@@ -43,10 +43,6 @@
color: var(--vscode-notificationLink-foreground);
}
-.toolbar-button.toggled .codicon {
- font-weight: bold;
-}
-
.toolbar-separator {
flex: none;
background-color: var(--vscode-menu-separatorBackground);