mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: rework assert dialog (#28043)
This commit is contained in:
parent
5f527fedb1
commit
b004c1a0a7
@ -14,16 +14,18 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const highlightCSS = `
|
:host {
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: system-ui, "Ubuntu", "Droid Sans", sans-serif;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
x-pw-tooltip {
|
x-pw-tooltip {
|
||||||
backdrop-filter: blur(5px);
|
backdrop-filter: blur(5px);
|
||||||
background-color: white;
|
background-color: white;
|
||||||
color: #222;
|
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 0.5rem 1.2rem rgba(0,0,0,.3);
|
box-shadow: 0 0.5rem 1.2rem rgba(0,0,0,.3);
|
||||||
display: none;
|
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-size: 12.8px;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -31,11 +33,27 @@ x-pw-tooltip {
|
|||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
padding: 4px;
|
||||||
}
|
}
|
||||||
x-pw-tooltip-body {
|
|
||||||
align-items: center;
|
x-pw-dialog {
|
||||||
padding: 3.2px 5.12px 3.2px;
|
background-color: white;
|
||||||
|
pointer-events: auto;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 0.5rem 1.2rem rgba(0,0,0,.3);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: absolute;
|
||||||
|
min-width: 500px;
|
||||||
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x-pw-dialog-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: auto;
|
||||||
|
}
|
||||||
|
|
||||||
x-pw-highlight {
|
x-pw-highlight {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -43,6 +61,7 @@ x-pw-highlight {
|
|||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-action-point {
|
x-pw-action-point {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
@ -52,20 +71,24 @@ x-pw-action-point {
|
|||||||
margin: -10px 0 0 -10px;
|
margin: -10px 0 0 -10px;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-separator {
|
x-pw-separator {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
margin: 6px 9px;
|
margin: 6px 9px;
|
||||||
background: rgb(148 148 148 / 90%);
|
background: rgb(148 148 148 / 90%);
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-gripper {
|
x-pw-tool-gripper {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
margin: 2px 0;
|
margin: 2px 0;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-gripper:active {
|
x-pw-tool-gripper:active {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-gripper > x-div {
|
x-pw-tool-gripper > x-div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -79,11 +102,20 @@ x-pw-tool-gripper > x-div {
|
|||||||
mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z' /></svg>");
|
mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z' /></svg>");
|
||||||
background-color: #555555;
|
background-color: #555555;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x-pw-tool-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 10px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
x-pw-tools-list {
|
x-pw-tools-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-bottom: 1px solid #dddddd;
|
border-bottom: 1px solid #dddddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-item {
|
x-pw-tool-item {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -91,9 +123,11 @@ x-pw-tool-item {
|
|||||||
width: 28px;
|
width: 28px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-item:not(.disabled):hover {
|
x-pw-tool-item:not(.disabled):hover {
|
||||||
background-color: hsl(0, 0%, 86%);
|
background-color: hsl(0, 0%, 86%);
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-item > x-div {
|
x-pw-tool-item > x-div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -105,45 +139,52 @@ x-pw-tool-item > x-div {
|
|||||||
mask-size: 16px;
|
mask-size: 16px;
|
||||||
background-color: #3a3a3a;
|
background-color: #3a3a3a;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-item.disabled > x-div {
|
x-pw-tool-item.disabled > x-div {
|
||||||
background-color: rgba(97, 97, 97, 0.5);
|
background-color: rgba(97, 97, 97, 0.5);
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-item.active > x-div {
|
x-pw-tool-item.active > x-div {
|
||||||
background-color: #006ab1;
|
background-color: #006ab1;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-item.record.active > x-div {
|
x-pw-tool-item.record.active > x-div {
|
||||||
background-color: #a1260d;
|
background-color: #a1260d;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-item.accept > x-div {
|
x-pw-tool-item.accept > x-div {
|
||||||
background-color: #388a34;
|
background-color: #388a34;
|
||||||
}
|
}
|
||||||
x-pw-tool-item.cancel > x-div {
|
|
||||||
background-color: #e51400;
|
|
||||||
}
|
|
||||||
x-pw-tool-item.record > x-div {
|
x-pw-tool-item.record > x-div {
|
||||||
/* codicon: circle-large-filled */
|
/* codicon: circle-large-filled */
|
||||||
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M8 1a6.8 6.8 0 0 1 1.86.253 6.899 6.899 0 0 1 3.083 1.805 6.903 6.903 0 0 1 1.804 3.083C14.916 6.738 15 7.357 15 8s-.084 1.262-.253 1.86a6.9 6.9 0 0 1-.704 1.674 7.157 7.157 0 0 1-2.516 2.509 6.966 6.966 0 0 1-1.668.71A6.984 6.984 0 0 1 8 15a6.984 6.984 0 0 1-1.86-.246 7.098 7.098 0 0 1-1.674-.711 7.3 7.3 0 0 1-1.415-1.094 7.295 7.295 0 0 1-1.094-1.415 7.098 7.098 0 0 1-.71-1.675A6.985 6.985 0 0 1 1 8c0-.643.082-1.262.246-1.86a6.968 6.968 0 0 1 .711-1.667 7.156 7.156 0 0 1 2.509-2.516 6.895 6.895 0 0 1 1.675-.704A6.808 6.808 0 0 1 8 1z'/></svg>");
|
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M8 1a6.8 6.8 0 0 1 1.86.253 6.899 6.899 0 0 1 3.083 1.805 6.903 6.903 0 0 1 1.804 3.083C14.916 6.738 15 7.357 15 8s-.084 1.262-.253 1.86a6.9 6.9 0 0 1-.704 1.674 7.157 7.157 0 0 1-2.516 2.509 6.966 6.966 0 0 1-1.668.71A6.984 6.984 0 0 1 8 15a6.984 6.984 0 0 1-1.86-.246 7.098 7.098 0 0 1-1.674-.711 7.3 7.3 0 0 1-1.415-1.094 7.295 7.295 0 0 1-1.094-1.415 7.098 7.098 0 0 1-.71-1.675A6.985 6.985 0 0 1 1 8c0-.643.082-1.262.246-1.86a6.968 6.968 0 0 1 .711-1.667 7.156 7.156 0 0 1 2.509-2.516 6.895 6.895 0 0 1 1.675-.704A6.808 6.808 0 0 1 8 1z'/></svg>");
|
||||||
mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M8 1a6.8 6.8 0 0 1 1.86.253 6.899 6.899 0 0 1 3.083 1.805 6.903 6.903 0 0 1 1.804 3.083C14.916 6.738 15 7.357 15 8s-.084 1.262-.253 1.86a6.9 6.9 0 0 1-.704 1.674 7.157 7.157 0 0 1-2.516 2.509 6.966 6.966 0 0 1-1.668.71A6.984 6.984 0 0 1 8 15a6.984 6.984 0 0 1-1.86-.246 7.098 7.098 0 0 1-1.674-.711 7.3 7.3 0 0 1-1.415-1.094 7.295 7.295 0 0 1-1.094-1.415 7.098 7.098 0 0 1-.71-1.675A6.985 6.985 0 0 1 1 8c0-.643.082-1.262.246-1.86a6.968 6.968 0 0 1 .711-1.667 7.156 7.156 0 0 1 2.509-2.516 6.895 6.895 0 0 1 1.675-.704A6.808 6.808 0 0 1 8 1z'/></svg>");
|
mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M8 1a6.8 6.8 0 0 1 1.86.253 6.899 6.899 0 0 1 3.083 1.805 6.903 6.903 0 0 1 1.804 3.083C14.916 6.738 15 7.357 15 8s-.084 1.262-.253 1.86a6.9 6.9 0 0 1-.704 1.674 7.157 7.157 0 0 1-2.516 2.509 6.966 6.966 0 0 1-1.668.71A6.984 6.984 0 0 1 8 15a6.984 6.984 0 0 1-1.86-.246 7.098 7.098 0 0 1-1.674-.711 7.3 7.3 0 0 1-1.415-1.094 7.295 7.295 0 0 1-1.094-1.415 7.098 7.098 0 0 1-.71-1.675A6.985 6.985 0 0 1 1 8c0-.643.082-1.262.246-1.86a6.968 6.968 0 0 1 .711-1.667 7.156 7.156 0 0 1 2.509-2.516 6.895 6.895 0 0 1 1.675-.704A6.808 6.808 0 0 1 8 1z'/></svg>");
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-item.pick-locator > x-div {
|
x-pw-tool-item.pick-locator > x-div {
|
||||||
/* codicon: inspect */
|
/* codicon: inspect */
|
||||||
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path fill-rule='evenodd' clip-rule='evenodd' d='M1 3l1-1h12l1 1v6h-1V3H2v8h5v1H2l-1-1V3zm14.707 9.707L9 6v9.414l2.707-2.707h4zM10 13V8.414l3.293 3.293h-2L10 13z'/></svg>");
|
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path fill-rule='evenodd' clip-rule='evenodd' d='M1 3l1-1h12l1 1v6h-1V3H2v8h5v1H2l-1-1V3zm14.707 9.707L9 6v9.414l2.707-2.707h4zM10 13V8.414l3.293 3.293h-2L10 13z'/></svg>");
|
||||||
mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path fill-rule='evenodd' clip-rule='evenodd' d='M1 3l1-1h12l1 1v6h-1V3H2v8h5v1H2l-1-1V3zm14.707 9.707L9 6v9.414l2.707-2.707h4zM10 13V8.414l3.293 3.293h-2L10 13z'/></svg>");
|
mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path fill-rule='evenodd' clip-rule='evenodd' d='M1 3l1-1h12l1 1v6h-1V3H2v8h5v1H2l-1-1V3zm14.707 9.707L9 6v9.414l2.707-2.707h4zM10 13V8.414l3.293 3.293h-2L10 13z'/></svg>");
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-item.assert > x-div {
|
x-pw-tool-item.assert > x-div {
|
||||||
/* codicon: check-all */
|
/* codicon: check-all */
|
||||||
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path fill-rule='evenodd' clip-rule='evenodd' d='M15.62 3.596L7.815 12.81l-.728-.033L4 8.382l.754-.53 2.744 3.907L14.917 3l.703.596z'/><path fill-rule='evenodd' clip-rule='evenodd' d='M7.234 8.774l4.386-5.178L10.917 3l-4.23 4.994.547.78zm-1.55.403l.548.78-.547-.78zm-1.617 1.91l.547.78-.799.943-.728-.033L0 8.382l.754-.53 2.744 3.907.57-.672z'/></svg>");
|
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path fill-rule='evenodd' clip-rule='evenodd' d='M15.62 3.596L7.815 12.81l-.728-.033L4 8.382l.754-.53 2.744 3.907L14.917 3l.703.596z'/><path fill-rule='evenodd' clip-rule='evenodd' d='M7.234 8.774l4.386-5.178L10.917 3l-4.23 4.994.547.78zm-1.55.403l.548.78-.547-.78zm-1.617 1.91l.547.78-.799.943-.728-.033L0 8.382l.754-.53 2.744 3.907.57-.672z'/></svg>");
|
||||||
mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path fill-rule='evenodd' clip-rule='evenodd' d='M15.62 3.596L7.815 12.81l-.728-.033L4 8.382l.754-.53 2.744 3.907L14.917 3l.703.596z'/><path fill-rule='evenodd' clip-rule='evenodd' d='M7.234 8.774l4.386-5.178L10.917 3l-4.23 4.994.547.78zm-1.55.403l.548.78-.547-.78zm-1.617 1.91l.547.78-.799.943-.728-.033L0 8.382l.754-.53 2.744 3.907.57-.672z'/></svg>");
|
mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path fill-rule='evenodd' clip-rule='evenodd' d='M15.62 3.596L7.815 12.81l-.728-.033L4 8.382l.754-.53 2.744 3.907L14.917 3l.703.596z'/><path fill-rule='evenodd' clip-rule='evenodd' d='M7.234 8.774l4.386-5.178L10.917 3l-4.23 4.994.547.78zm-1.55.403l.548.78-.547-.78zm-1.617 1.91l.547.78-.799.943-.728-.033L0 8.382l.754-.53 2.744 3.907.57-.672z'/></svg>");
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-item.accept > x-div {
|
x-pw-tool-item.accept > x-div {
|
||||||
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/></svg>");
|
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/></svg>");
|
||||||
mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/></svg>");
|
mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/></svg>");
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-tool-item.cancel > x-div {
|
x-pw-tool-item.cancel > x-div {
|
||||||
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/></svg>");
|
-webkit-mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/></svg>");
|
||||||
mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/></svg>");
|
mask-image: url("data:image/svg+xml;utf8,<svg width='16' height='16' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='currentColor'><path d='M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/></svg>");
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-overlay {
|
x-pw-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -152,21 +193,52 @@ x-pw-overlay {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-overlay x-pw-tools-list {
|
x-pw-overlay x-pw-tools-list {
|
||||||
background-color: #ffffffdd;
|
background-color: #ffffffdd;
|
||||||
box-shadow: rgba(0, 0, 0, 0.1) 0px 5px 5px;
|
box-shadow: rgba(0, 0, 0, 0.1) 0px 5px 5px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
x-pw-overlay x-pw-tool-item {
|
x-pw-overlay x-pw-tool-item {
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.locator-editor {
|
||||||
|
display: flex;
|
||||||
|
padding: 10px;
|
||||||
|
flex: none;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid #dddddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.locator-editor:focus,
|
||||||
|
textarea.text-editor:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.text-editor {
|
||||||
|
font-family: system-ui, "Ubuntu", "Droid Sans", sans-serif;
|
||||||
|
flex: auto;
|
||||||
|
border: none;
|
||||||
|
padding: 10px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
x-div {
|
x-div {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x-spacer {
|
||||||
|
flex: auto;
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
*[hidden] {
|
*[hidden] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}`;
|
}
|
@ -19,7 +19,7 @@ import type { ParsedSelector } from '../../utils/isomorphic/selectorParser';
|
|||||||
import type { InjectedScript } from './injectedScript';
|
import type { InjectedScript } from './injectedScript';
|
||||||
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
|
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
|
||||||
import type { Language } from '../../utils/isomorphic/locatorGenerators';
|
import type { Language } from '../../utils/isomorphic/locatorGenerators';
|
||||||
import { highlightCSS } from './highlight.css';
|
import highlightCSS from './highlight.css?inline';
|
||||||
|
|
||||||
type HighlightEntry = {
|
type HighlightEntry = {
|
||||||
targetElement: Element,
|
targetElement: Element,
|
||||||
@ -34,9 +34,6 @@ type HighlightEntry = {
|
|||||||
export type HighlightOptions = {
|
export type HighlightOptions = {
|
||||||
tooltipText?: string;
|
tooltipText?: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
anchorGetter?: (element: Element) => DOMRect;
|
|
||||||
toolbar?: Element[];
|
|
||||||
interactive?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Highlight {
|
export class Highlight {
|
||||||
@ -63,7 +60,12 @@ export class Highlight {
|
|||||||
this._glassPaneElement.style.pointerEvents = 'none';
|
this._glassPaneElement.style.pointerEvents = 'none';
|
||||||
this._glassPaneElement.style.display = 'flex';
|
this._glassPaneElement.style.display = 'flex';
|
||||||
this._glassPaneElement.style.backgroundColor = 'transparent';
|
this._glassPaneElement.style.backgroundColor = 'transparent';
|
||||||
|
for (const eventName of ['click', 'auxclick', 'dragstart', 'input', 'keydown', 'keyup', 'pointerdown', 'pointerup', 'mousedown', 'mouseup', 'mousemove', 'mouseleave', 'focus', 'scroll']) {
|
||||||
|
this._glassPaneElement.addEventListener(eventName, e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
});
|
||||||
|
}
|
||||||
this._actionPointElement = document.createElement('x-pw-action-point');
|
this._actionPointElement = document.createElement('x-pw-action-point');
|
||||||
this._actionPointElement.setAttribute('hidden', 'true');
|
this._actionPointElement.setAttribute('hidden', 'true');
|
||||||
this._glassPaneShadow = this._glassPaneElement.attachShadow({ mode: this._isUnderTest ? 'open' : 'closed' });
|
this._glassPaneShadow = this._glassPaneElement.attachShadow({ mode: this._isUnderTest ? 'open' : 'closed' });
|
||||||
@ -145,26 +147,12 @@ export class Highlight {
|
|||||||
let tooltipElement;
|
let tooltipElement;
|
||||||
if (options.tooltipText) {
|
if (options.tooltipText) {
|
||||||
tooltipElement = this._injectedScript.document.createElement('x-pw-tooltip');
|
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.top = '0';
|
||||||
tooltipElement.style.left = '0';
|
tooltipElement.style.left = '0';
|
||||||
tooltipElement.style.display = 'flex';
|
tooltipElement.style.display = 'flex';
|
||||||
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 });
|
this._highlightEntries.push({ targetElement: elements[i], tooltipElement, highlightElement, tooltipText: options.tooltipText });
|
||||||
}
|
}
|
||||||
@ -176,25 +164,7 @@ export class Highlight {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Position tooltip, if any.
|
// Position tooltip, if any.
|
||||||
const tooltipWidth = entry.tooltipElement.offsetWidth;
|
const { anchorLeft, anchorTop } = this.tooltipPosition(entry.box, entry.tooltipElement);
|
||||||
const tooltipHeight = entry.tooltipElement.offsetHeight;
|
|
||||||
const totalWidth = this._glassPaneElement.offsetWidth;
|
|
||||||
const totalHeight = this._glassPaneElement.offsetHeight;
|
|
||||||
|
|
||||||
const anchorBox = options.anchorGetter ? options.anchorGetter(entry.targetElement) : entry.box;
|
|
||||||
let anchorLeft = anchorBox.left;
|
|
||||||
if (anchorLeft + tooltipWidth > totalWidth - 5)
|
|
||||||
anchorLeft = totalWidth - tooltipWidth - 5;
|
|
||||||
let anchorTop = anchorBox.bottom + 5;
|
|
||||||
if (anchorTop + tooltipHeight > totalHeight - 5) {
|
|
||||||
// If can't fit below, either position above...
|
|
||||||
if (anchorBox.top > tooltipHeight + 5) {
|
|
||||||
anchorTop = anchorBox.top - tooltipHeight - 5;
|
|
||||||
} else {
|
|
||||||
// Or on top in case of large element
|
|
||||||
anchorTop = totalHeight - 5 - tooltipHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entry.tooltipTop = anchorTop;
|
entry.tooltipTop = anchorTop;
|
||||||
entry.tooltipLeft = anchorLeft;
|
entry.tooltipLeft = anchorLeft;
|
||||||
}
|
}
|
||||||
@ -219,6 +189,33 @@ export class Highlight {
|
|||||||
console.error('Highlight box for test: ' + JSON.stringify({ x: box.x, y: box.y, width: box.width, height: box.height })); // eslint-disable-line no-console
|
console.error('Highlight box for test: ' + JSON.stringify({ x: box.x, y: box.y, width: box.width, height: box.height })); // eslint-disable-line no-console
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
firstBox(): DOMRect | undefined {
|
||||||
|
return this._highlightEntries[0]?.box;
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltipPosition(box: DOMRect, tooltipElement: HTMLElement) {
|
||||||
|
const tooltipWidth = tooltipElement.offsetWidth;
|
||||||
|
const tooltipHeight = tooltipElement.offsetHeight;
|
||||||
|
const totalWidth = this._glassPaneElement.offsetWidth;
|
||||||
|
const totalHeight = this._glassPaneElement.offsetHeight;
|
||||||
|
|
||||||
|
let anchorLeft = box.left;
|
||||||
|
if (anchorLeft + tooltipWidth > totalWidth - 5)
|
||||||
|
anchorLeft = totalWidth - tooltipWidth - 5;
|
||||||
|
let anchorTop = box.bottom + 5;
|
||||||
|
if (anchorTop + tooltipHeight > totalHeight - 5) {
|
||||||
|
// If can't fit below, either position above...
|
||||||
|
if (box.top > tooltipHeight + 5) {
|
||||||
|
anchorTop = box.top - tooltipHeight - 5;
|
||||||
|
} else {
|
||||||
|
// Or on top in case of large element
|
||||||
|
anchorTop = totalHeight - 5 - tooltipHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { anchorLeft, anchorTop };
|
||||||
|
}
|
||||||
|
|
||||||
private _highlightIsUpToDate(elements: Element[], tooltipText: string | undefined): boolean {
|
private _highlightIsUpToDate(elements: Element[], tooltipText: string | undefined): boolean {
|
||||||
if (elements.length !== this._highlightEntries.length)
|
if (elements.length !== this._highlightEntries.length)
|
||||||
return false;
|
return false;
|
||||||
|
@ -20,10 +20,12 @@ import { generateSelector } from '../injected/selectorGenerator';
|
|||||||
import type { Point } from '../../common/types';
|
import type { Point } from '../../common/types';
|
||||||
import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes';
|
import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes';
|
||||||
import { Highlight, type HighlightOptions } from '../injected/highlight';
|
import { Highlight, type HighlightOptions } from '../injected/highlight';
|
||||||
import { enclosingElement, isInsideScope, parentElementOrShadowHost } from './domUtils';
|
import { isInsideScope } from './domUtils';
|
||||||
import { elementText } from './selectorUtils';
|
import { elementText } from './selectorUtils';
|
||||||
import { escapeWithQuotes, normalizeWhiteSpace } from '../../utils/isomorphic/stringUtils';
|
|
||||||
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
|
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
|
||||||
|
import { locatorOrSelectorAsSelector } from '@isomorphic/locatorParser';
|
||||||
|
import { parseSelector } from '@isomorphic/selectorParser';
|
||||||
|
import { normalizeWhiteSpace } from '@isomorphic/stringUtils';
|
||||||
|
|
||||||
interface RecorderDelegate {
|
interface RecorderDelegate {
|
||||||
performAction?(action: actions.Action): Promise<void>;
|
performAction?(action: actions.Action): Promise<void>;
|
||||||
@ -442,217 +444,187 @@ class RecordActionTool implements RecorderTool {
|
|||||||
|
|
||||||
class TextAssertionTool implements RecorderTool {
|
class TextAssertionTool implements RecorderTool {
|
||||||
private _hoverHighlight: HighlightModel | null = null;
|
private _hoverHighlight: HighlightModel | null = null;
|
||||||
private _selectionHighlight: HighlightModel | null = null;
|
private _action: actions.AssertAction | null = null;
|
||||||
private _selectionText: { selectedText: string, fullText: string } | null = null;
|
private _dialogElement: HTMLElement | null = null;
|
||||||
private _inputHighlight: HighlightModel | null = null;
|
|
||||||
private _acceptButton: HTMLElement;
|
private _acceptButton: HTMLElement;
|
||||||
private _cancelButton: HTMLElement;
|
private _cancelButton: HTMLElement;
|
||||||
|
private _keyboardListener: ((event: KeyboardEvent) => void) | undefined;
|
||||||
|
|
||||||
constructor(private _recorder: Recorder) {
|
constructor(private _recorder: Recorder) {
|
||||||
this._acceptButton = this._recorder.document.createElement('x-pw-tool-item');
|
this._acceptButton = this._recorder.document.createElement('x-pw-tool-item');
|
||||||
|
this._acceptButton.title = 'Accept';
|
||||||
this._acceptButton.classList.add('accept');
|
this._acceptButton.classList.add('accept');
|
||||||
this._acceptButton.appendChild(this._recorder.document.createElement('x-div'));
|
this._acceptButton.appendChild(this._recorder.document.createElement('x-div'));
|
||||||
this._acceptButton.addEventListener('click', () => this._commitAction());
|
this._acceptButton.addEventListener('click', () => this._commit());
|
||||||
|
|
||||||
this._cancelButton = this._recorder.document.createElement('x-pw-tool-item');
|
this._cancelButton = this._recorder.document.createElement('x-pw-tool-item');
|
||||||
|
this._cancelButton.title = 'Close';
|
||||||
this._cancelButton.classList.add('cancel');
|
this._cancelButton.classList.add('cancel');
|
||||||
this._cancelButton.appendChild(this._recorder.document.createElement('x-div'));
|
this._cancelButton.appendChild(this._recorder.document.createElement('x-div'));
|
||||||
this._cancelButton.addEventListener('click', () => this._cancelAction());
|
this._cancelButton.addEventListener('click', () => this._closeDialog());
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor() {
|
cursor() {
|
||||||
return 'text';
|
return 'pointer';
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
|
this._closeDialog();
|
||||||
this._hoverHighlight = null;
|
this._hoverHighlight = null;
|
||||||
this._selectionHighlight = null;
|
|
||||||
this._selectionText = null;
|
|
||||||
this._inputHighlight = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(event: MouseEvent) {
|
onClick(event: MouseEvent) {
|
||||||
|
if (!this._dialogElement)
|
||||||
|
this._showDialog();
|
||||||
consumeEvent(event);
|
consumeEvent(event);
|
||||||
const selection = this._recorder.document.getSelection();
|
|
||||||
if (event.detail === 1 && selection && !selection.toString() && !this._inputHighlight) {
|
|
||||||
const target = this._recorder.deepEventTarget(event);
|
|
||||||
selection.selectAllChildren(target);
|
|
||||||
this._updateSelectionHighlight();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMouseDown(event: MouseEvent) {
|
|
||||||
const target = this._recorder.deepEventTarget(event);
|
|
||||||
if (['INPUT', 'TEXTAREA'].includes(target.nodeName)) {
|
|
||||||
this._recorder.injectedScript.window.getSelection()?.empty();
|
|
||||||
this._inputHighlight = generateSelector(this._recorder.injectedScript, target, { testIdAttributeName: this._recorder.state.testIdAttributeName });
|
|
||||||
this._showHighlight(true);
|
|
||||||
consumeEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._inputHighlight = null;
|
|
||||||
this._hoverHighlight = null;
|
|
||||||
this._updateSelectionHighlight();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMouseUp(event: MouseEvent) {
|
|
||||||
this._updateSelectionHighlight();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseMove(event: MouseEvent) {
|
onMouseMove(event: MouseEvent) {
|
||||||
const selection = this._recorder.document.getSelection();
|
if (this._dialogElement)
|
||||||
if (selection && selection.toString()) {
|
|
||||||
this._updateSelectionHighlight();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this._inputHighlight || event.buttons)
|
|
||||||
return;
|
return;
|
||||||
const target = this._recorder.deepEventTarget(event);
|
const target = this._recorder.deepEventTarget(event);
|
||||||
if (this._hoverHighlight?.elements[0] === target)
|
if (this._hoverHighlight?.elements[0] === target)
|
||||||
return;
|
return;
|
||||||
this._hoverHighlight = elementText(new Map(), target).full ? { elements: [target], selector: '' } : null;
|
this._hoverHighlight = target.nodeName === 'INPUT' || target.nodeName === 'TEXTAREA' || elementText(new Map(), target).full ? { elements: [target], selector: '' } : null;
|
||||||
this._recorder.updateHighlight(this._hoverHighlight, true, { color: '#8acae480' });
|
this._recorder.updateHighlight(this._hoverHighlight, true, { color: '#8acae480' });
|
||||||
}
|
}
|
||||||
|
|
||||||
onDragStart(event: DragEvent) {
|
|
||||||
consumeEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyDown(event: KeyboardEvent) {
|
onKeyDown(event: KeyboardEvent) {
|
||||||
if (event.key === 'Escape') {
|
if (event.key === 'Escape')
|
||||||
const selection = this._recorder.document.getSelection();
|
this._recorder.delegate.setMode?.('recording');
|
||||||
if (selection && selection.toString())
|
|
||||||
this._resetSelectionAndHighlight();
|
|
||||||
else
|
|
||||||
this._recorder.delegate.setMode?.('recording');
|
|
||||||
consumeEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
this._commitAction();
|
|
||||||
consumeEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only allow keys that control text selection.
|
|
||||||
if (!['ArrowLeft', 'ArrowUp', 'ArrowRight', 'ArrowDown', 'Shift', 'Control', 'Meta', 'Alt', 'AltGraph'].includes(event.key)) {
|
|
||||||
consumeEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyUp(event: KeyboardEvent) {
|
|
||||||
consumeEvent(event);
|
consumeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
onScroll(event: Event) {
|
private _generateAction(): actions.AssertAction | null {
|
||||||
this._hoverHighlight = null;
|
const target = this._hoverHighlight?.elements[0];
|
||||||
this._showHighlight(false);
|
if (!target)
|
||||||
}
|
return null;
|
||||||
|
if (target.nodeName === 'INPUT' || target.nodeName === 'TEXTAREA') {
|
||||||
private _generateAction(): actions.Action | null {
|
const { selector } = generateSelector(this._recorder.injectedScript, target, { testIdAttributeName: this._recorder.state.testIdAttributeName });
|
||||||
if (this._inputHighlight) {
|
if (target.nodeName === 'INPUT' && ['checkbox', 'radio'].includes((target as HTMLInputElement).type.toLowerCase())) {
|
||||||
const target = this._inputHighlight.elements[0] as HTMLInputElement;
|
|
||||||
if (target.nodeName === 'INPUT' && ['checkbox', 'radio'].includes(target.type.toLowerCase())) {
|
|
||||||
return {
|
return {
|
||||||
name: 'assertChecked',
|
name: 'assertChecked',
|
||||||
selector: this._inputHighlight.selector,
|
selector,
|
||||||
signals: [],
|
signals: [],
|
||||||
// Interestingly, inputElement.checked is reversed inside this event handler.
|
// Interestingly, inputElement.checked is reversed inside this event handler.
|
||||||
checked: !(target as HTMLInputElement).checked,
|
checked: (target as HTMLInputElement).checked,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
name: 'assertValue',
|
name: 'assertValue',
|
||||||
selector: this._inputHighlight.selector,
|
selector,
|
||||||
signals: [],
|
signals: [],
|
||||||
value: target.value,
|
value: (target as HTMLInputElement).value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (this._selectionText && this._selectionHighlight) {
|
} else {
|
||||||
|
const { selector } = generateSelector(this._recorder.injectedScript, target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });
|
||||||
return {
|
return {
|
||||||
name: 'assertText',
|
name: 'assertText',
|
||||||
selector: this._selectionHighlight.selector,
|
selector,
|
||||||
signals: [],
|
signals: [],
|
||||||
text: this._selectionText.selectedText,
|
text: target.textContent!,
|
||||||
substring: this._selectionText.fullText !== this._selectionText.selectedText,
|
substring: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _generateActionPreview() {
|
private _renderValue(action: actions.Action) {
|
||||||
const action = this._generateAction();
|
|
||||||
// TODO: support other languages, maybe unify with code generator?
|
|
||||||
if (action?.name === 'assertText')
|
if (action?.name === 'assertText')
|
||||||
return `expect(${asLocator(this._recorder.state.language, action.selector)}).${action.substring ? 'toContainText' : 'toHaveText'}(${escapeWithQuotes(action.text)})`;
|
return normalizeWhiteSpace(action.text);
|
||||||
if (action?.name === 'assertChecked')
|
if (action?.name === 'assertChecked')
|
||||||
return `expect(${asLocator(this._recorder.state.language, action.selector)})${action.checked ? '' : '.not'}.toBeChecked()`;
|
return String(action.checked);
|
||||||
if (action?.name === 'assertValue') {
|
if (action?.name === 'assertValue')
|
||||||
const assertion = action.value ? `toHaveValue(${escapeWithQuotes(action.value)})` : `toBeEmpty()`;
|
return action.value;
|
||||||
return `expect(${asLocator(this._recorder.state.language, action.selector)}).${assertion}`;
|
|
||||||
}
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
private _commitAction() {
|
private _commit() {
|
||||||
const action = this._generateAction();
|
if (!this._action || !this._dialogElement)
|
||||||
if (action) {
|
|
||||||
this._resetSelectionAndHighlight();
|
|
||||||
this._recorder.delegate.recordAction?.(action);
|
|
||||||
this._recorder.delegate.setMode?.('recording');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _cancelAction() {
|
|
||||||
this._resetSelectionAndHighlight();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _resetSelectionAndHighlight() {
|
|
||||||
this._selectionHighlight = null;
|
|
||||||
this._selectionText = null;
|
|
||||||
this._inputHighlight = null;
|
|
||||||
this._recorder.injectedScript.window.getSelection()?.empty();
|
|
||||||
this._recorder.updateHighlight(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _updateSelectionHighlight() {
|
|
||||||
if (this._inputHighlight)
|
|
||||||
return;
|
return;
|
||||||
const selection = this._recorder.document.getSelection();
|
this._closeDialog();
|
||||||
const selectedText = normalizeWhiteSpace(selection?.toString() || '');
|
this._recorder.delegate.recordAction?.(this._action);
|
||||||
let highlight: HighlightModel | null = null;
|
this._recorder.delegate.setMode?.('recording');
|
||||||
if (selection && selection.focusNode && selection.anchorNode && selectedText) {
|
|
||||||
const focusElement = enclosingElement(selection.focusNode);
|
|
||||||
let lcaElement = focusElement ? enclosingElement(selection.anchorNode) : undefined;
|
|
||||||
while (lcaElement && !isInsideScope(lcaElement, focusElement))
|
|
||||||
lcaElement = parentElementOrShadowHost(lcaElement);
|
|
||||||
highlight = lcaElement ? generateSelector(this._recorder.injectedScript, lcaElement, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true }) : null;
|
|
||||||
}
|
|
||||||
const fullText = highlight ? normalizeWhiteSpace(elementText(new Map(), highlight.elements[0]).full) : '';
|
|
||||||
const selectionText = highlight ? { selectedText, fullText } : null;
|
|
||||||
if (highlight?.selector === this._selectionHighlight?.selector && this._selectionText?.fullText === selectionText?.fullText && this._selectionText?.selectedText === selectionText?.selectedText)
|
|
||||||
return;
|
|
||||||
this._selectionHighlight = highlight;
|
|
||||||
this._selectionText = selectionText;
|
|
||||||
this._showHighlight(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showHighlight(userGesture: boolean) {
|
private _showDialog() {
|
||||||
const options: HighlightOptions = {
|
const target = this._hoverHighlight?.elements[0];
|
||||||
color: '#6fdcbd38',
|
if (!target)
|
||||||
tooltipText: this._generateActionPreview(),
|
return;
|
||||||
toolbar: [this._acceptButton, this._cancelButton],
|
this._action = this._generateAction();
|
||||||
interactive: true,
|
if (!this._action)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._dialogElement = this._recorder.document.createElement('x-pw-dialog');
|
||||||
|
this._keyboardListener = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
this._closeDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
|
||||||
|
if (this._dialogElement)
|
||||||
|
this._commit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if (this._inputHighlight) {
|
this._recorder.document.addEventListener('keydown', this._keyboardListener, true);
|
||||||
this._recorder.updateHighlight(this._inputHighlight, userGesture, options);
|
const toolbarElement = this._recorder.document.createElement('x-pw-tools-list');
|
||||||
} else {
|
toolbarElement.appendChild(this._createLabel(this._action));
|
||||||
options.anchorGetter = (e: Element) => this._recorder.document.getSelection()?.getRangeAt(0)?.getBoundingClientRect() || e.getBoundingClientRect();
|
toolbarElement.appendChild(this._recorder.document.createElement('x-spacer'));
|
||||||
this._recorder.updateHighlight(this._selectionHighlight, userGesture, options);
|
toolbarElement.appendChild(this._acceptButton);
|
||||||
}
|
toolbarElement.appendChild(this._cancelButton);
|
||||||
|
|
||||||
|
this._dialogElement.appendChild(toolbarElement);
|
||||||
|
const bodyElement = this._recorder.document.createElement('x-pw-dialog-body');
|
||||||
|
const locatorElement = this._recorder.document.createElement('input');
|
||||||
|
locatorElement.classList.add('locator-editor');
|
||||||
|
locatorElement.value = asLocator(this._recorder.state.language, this._action.selector);
|
||||||
|
locatorElement.addEventListener('input', () => {
|
||||||
|
if (this._action) {
|
||||||
|
const selector = locatorOrSelectorAsSelector(this._recorder.state.language, locatorElement.value, this._recorder.state.testIdAttributeName);
|
||||||
|
const model: HighlightModel = {
|
||||||
|
selector,
|
||||||
|
elements: this._recorder.injectedScript.querySelectorAll(parseSelector(selector), this._recorder.document),
|
||||||
|
};
|
||||||
|
this._action.selector = selector;
|
||||||
|
this._recorder.updateHighlight(model, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const textElement = this._recorder.document.createElement('textarea');
|
||||||
|
textElement.value = this._renderValue(this._action);
|
||||||
|
textElement.classList.add('text-editor');
|
||||||
|
|
||||||
|
textElement.addEventListener('input', () => {
|
||||||
|
if (this._action?.name === 'assertText')
|
||||||
|
this._action.text = normalizeWhiteSpace(elementText(new Map(), textElement).full);
|
||||||
|
if (this._action?.name === 'assertChecked')
|
||||||
|
this._action.checked = textElement.value === 'true';
|
||||||
|
if (this._action?.name === 'assertValue')
|
||||||
|
this._action.value = textElement.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
bodyElement.appendChild(locatorElement);
|
||||||
|
bodyElement.appendChild(textElement);
|
||||||
|
this._dialogElement.appendChild(bodyElement);
|
||||||
|
this._recorder.highlight.appendChild(this._dialogElement);
|
||||||
|
const position = this._recorder.highlight.tooltipPosition(this._recorder.highlight.firstBox()!, this._dialogElement);
|
||||||
|
this._dialogElement.style.top = position.anchorTop + 'px';
|
||||||
|
this._dialogElement.style.left = position.anchorLeft + 'px';
|
||||||
|
textElement.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createLabel(action: actions.AssertAction) {
|
||||||
|
const labelElement = this._recorder.document.createElement('x-pw-tool-label');
|
||||||
|
labelElement.textContent = action.name === 'assertText' ? 'Assert text' : action.name === 'assertValue' ? 'Assert value' : 'Assert checked';
|
||||||
|
return labelElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _closeDialog() {
|
||||||
|
if (!this._dialogElement)
|
||||||
|
return;
|
||||||
|
this._dialogElement.remove();
|
||||||
|
this._recorder.document.removeEventListener('keydown', this._keyboardListener!);
|
||||||
|
this._dialogElement = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +114,7 @@ export type AssertCheckedAction = ActionBase & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Action = ClickAction | CheckAction | ClosesPageAction | OpenPageAction | UncheckAction | FillAction | NavigateAction | PressAction | SelectAction | SetInputFilesAction | AssertTextAction | AssertValueAction | AssertCheckedAction;
|
export type Action = ClickAction | CheckAction | ClosesPageAction | OpenPageAction | UncheckAction | FillAction | NavigateAction | PressAction | SelectAction | SetInputFilesAction | AssertTextAction | AssertValueAction | AssertCheckedAction;
|
||||||
|
export type AssertAction = AssertCheckedAction | AssertValueAction | AssertTextAction;
|
||||||
|
|
||||||
// Signals.
|
// Signals.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user