feat(recorder): generate toHaveValue/toBeEmpty/toBeChecked (#27913)
This commit is contained in:
parent
0f2de59b7c
commit
07da88dcf1
|
|
@ -433,6 +433,29 @@ class TextAssertionTool implements RecorderTool {
|
|||
if (event.detail !== 1 || this._getSelectionText())
|
||||
return;
|
||||
const target = this._recorder.deepEventTarget(event);
|
||||
|
||||
if (['INPUT', 'TEXTAREA'].includes(target.nodeName) || target.isContentEditable) {
|
||||
const highlight = generateSelector(this._recorder.injectedScript, target, { testIdAttributeName: this._recorder.state.testIdAttributeName });
|
||||
if (target.nodeName === 'INPUT' && ['checkbox', 'radio'].includes((target as HTMLInputElement).type.toLowerCase())) {
|
||||
this._recorder.delegate.recordAction?.({
|
||||
name: 'assertChecked',
|
||||
selector: highlight.selector,
|
||||
signals: [],
|
||||
// Interestingly, inputElement.checked is reversed inside this event handler.
|
||||
checked: !(target as HTMLInputElement).checked,
|
||||
});
|
||||
} else {
|
||||
this._recorder.delegate.recordAction?.({
|
||||
name: 'assertValue',
|
||||
selector: highlight.selector,
|
||||
signals: [],
|
||||
value: target.isContentEditable ? target.innerText : (target as HTMLInputElement).value,
|
||||
});
|
||||
}
|
||||
this._recorder.updateHighlight(highlight, true, '#6fdcbd38');
|
||||
return;
|
||||
}
|
||||
|
||||
const text = target ? elementText(new Map(), target).full : '';
|
||||
if (text) {
|
||||
this._selectionModel = { anchor: { node: target, offset: 0 }, focus: { node: target, offset: target.childNodes.length }, highlight: null };
|
||||
|
|
@ -443,6 +466,14 @@ class TextAssertionTool implements RecorderTool {
|
|||
|
||||
onMouseDown(event: MouseEvent) {
|
||||
consumeEvent(event);
|
||||
const target = this._recorder.deepEventTarget(event);
|
||||
if (['INPUT', 'TEXTAREA'].includes(target.nodeName) || target.isContentEditable) {
|
||||
this._selectionModel = null;
|
||||
this._syncDocumentSelection();
|
||||
const highlight = generateSelector(this._recorder.injectedScript, target, { testIdAttributeName: this._recorder.state.testIdAttributeName });
|
||||
this._recorder.updateHighlight(highlight, true, '#6fdcbd38');
|
||||
return;
|
||||
}
|
||||
const pos = this._selectionPosition(event);
|
||||
if (pos && event.detail <= 1) {
|
||||
this._selectionModel = { anchor: pos, focus: pos, highlight: null };
|
||||
|
|
@ -538,7 +569,7 @@ class TextAssertionTool implements RecorderTool {
|
|||
if (highlight?.selector === this._selectionModel.highlight?.selector)
|
||||
return;
|
||||
this._selectionModel.highlight = highlight;
|
||||
this._recorder.updateHighlight(highlight, false, '#6fdcbd38');
|
||||
this._recorder.updateHighlight(highlight, true, '#6fdcbd38');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -644,7 +675,7 @@ class Overlay {
|
|||
none: this._createToolElement(toolsListElement, 'none', 'Disable'),
|
||||
inspecting: this._createToolElement(toolsListElement, 'inspecting', 'Pick locator'),
|
||||
recording: this._createToolElement(toolsListElement, 'recording', 'Record actions'),
|
||||
assertingText: this._createToolElement(toolsListElement, 'assertingText', 'Assert text'),
|
||||
assertingText: this._createToolElement(toolsListElement, 'assertingText', 'Assert text and values'),
|
||||
};
|
||||
|
||||
this._overlayElement.addEventListener('mousedown', event => {
|
||||
|
|
|
|||
|
|
@ -156,6 +156,12 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
|||
return `await ${subject}.${this._asLocator(action.selector)}.SelectOptionAsync(${formatObject(action.options)});`;
|
||||
case 'assertText':
|
||||
return `await Expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? 'ToContainTextAsync' : 'ToHaveTextAsync'}(${quote(action.text)});`;
|
||||
case 'assertChecked':
|
||||
return `await Expect(${subject}.${this._asLocator(action.selector)})${action.checked ? '' : '.Not'}.ToBeCheckedAsync();`;
|
||||
case 'assertValue': {
|
||||
const assertion = action.value ? `ToHaveValueAsync(${quote(action.value)})` : `ToBeEmpty()`;
|
||||
return `await Expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,12 @@ export class JavaLanguageGenerator implements LanguageGenerator {
|
|||
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.selectOption(${formatSelectOption(action.options.length > 1 ? action.options : action.options[0])});`;
|
||||
case 'assertText':
|
||||
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${action.substring ? 'containsText' : 'hasText'}(${quote(action.text)});`;
|
||||
case 'assertChecked':
|
||||
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)})${action.checked ? '' : '.not()'}.isChecked();`;
|
||||
case 'assertValue': {
|
||||
const assertion = action.value ? `hasValue(${quote(action.value)})` : `isEmpty()`;
|
||||
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${assertion};`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -127,6 +127,12 @@ export class JavaScriptLanguageGenerator implements LanguageGenerator {
|
|||
return `await ${subject}.${this._asLocator(action.selector)}.selectOption(${formatObject(action.options.length > 1 ? action.options : action.options[0])});`;
|
||||
case 'assertText':
|
||||
return `await expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? 'toContainText' : 'toHaveText'}(${quote(action.text)});`;
|
||||
case 'assertChecked':
|
||||
return `await expect(${subject}.${this._asLocator(action.selector)})${action.checked ? '' : '.not'}.toBeChecked();`;
|
||||
case 'assertValue': {
|
||||
const assertion = action.value ? `toHaveValue(${quote(action.value)})` : `toBeEmpty()`;
|
||||
return `await expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -136,6 +136,12 @@ export class PythonLanguageGenerator implements LanguageGenerator {
|
|||
return `${subject}.${this._asLocator(action.selector)}.select_option(${formatValue(action.options.length === 1 ? action.options[0] : action.options)})`;
|
||||
case 'assertText':
|
||||
return `expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? 'to_contain_text' : 'to_have_text'}(${quote(action.text)})`;
|
||||
case 'assertChecked':
|
||||
return `expect(${subject}.${this._asLocator(action.selector)}).${action.checked ? 'to_be_checked()' : 'not_to_be_checked()'}`;
|
||||
case 'assertValue': {
|
||||
const assertion = action.value ? `to_have_value(${quote(action.value)})` : `to_be_empty()`;
|
||||
return `expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ export type ActionName =
|
|||
'select' |
|
||||
'uncheck' |
|
||||
'setInputFiles' |
|
||||
'assertText';
|
||||
'assertText' |
|
||||
'assertValue' |
|
||||
'assertChecked';
|
||||
|
||||
export type ActionBase = {
|
||||
name: ActionName,
|
||||
|
|
@ -99,7 +101,19 @@ export type AssertTextAction = ActionBase & {
|
|||
substring: boolean,
|
||||
};
|
||||
|
||||
export type Action = ClickAction | CheckAction | ClosesPageAction | OpenPageAction | UncheckAction | FillAction | NavigateAction | PressAction | SelectAction | SetInputFilesAction | AssertTextAction;
|
||||
export type AssertValueAction = ActionBase & {
|
||||
name: 'assertValue',
|
||||
selector: string,
|
||||
value: string,
|
||||
};
|
||||
|
||||
export type AssertCheckedAction = ActionBase & {
|
||||
name: 'assertChecked',
|
||||
selector: string,
|
||||
checked: boolean,
|
||||
};
|
||||
|
||||
export type Action = ClickAction | CheckAction | ClosesPageAction | OpenPageAction | UncheckAction | FillAction | NavigateAction | PressAction | SelectAction | SetInputFilesAction | AssertTextAction | AssertValueAction | AssertCheckedAction;
|
||||
|
||||
// Signals.
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ export const Recorder: React.FC<RecorderProps> = ({
|
|||
<ToolbarButton icon='record' title='Record actions' toggled={mode === 'recording'} onClick={() => {
|
||||
window.dispatch({ event: 'setMode', params: { mode: mode === 'recording' ? 'none' : 'recording' } });
|
||||
}}>Record</ToolbarButton>
|
||||
<ToolbarButton icon='text-size' title='Assert text' toggled={mode === 'assertingText'} onClick={() => {
|
||||
<ToolbarButton icon='text-size' title='Assert text and values' toggled={mode === 'assertingText'} onClick={() => {
|
||||
window.dispatch({ event: 'setMode', params: { mode: mode === 'assertingText' ? 'none' : 'assertingText' } });
|
||||
}}>Assert</ToolbarButton>
|
||||
<ToolbarButton icon='files' title='Copy' disabled={!source || !source.text} onClick={() => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue