I looking for a way to inherit from WebElement object that webdriverio returns, without monkey-patching and with TS types support (autocompletion is a must). Is there a way to do something like this?
class Checkbox extends WebdriverIOWebElement {
constructor() {
super($('div'))
}
// overriding base method
isDisplayed(): boolean {
// blabla some new logic here
}
check() {
if(!this.isChecked()) {
this.click()
}
}
uncheck() {
if(this.isChecked()) {
this.click()
}
}
}
Lets take an example , When we have a New Tag (my-app) in HTML and we have to build a case to Login using webdriverIO ,
Assume this is the HTML :
What we would do is using the component object pattern , component object pattern attempts to reduce that repetition and move the component's api into an object of its own. We know that in order to interact with an element's shadow DOM, we first need the host element. Using a base class for your component objects makes this pretty straightforward.
Here's a bare-bones component base class that takes the host element in its constructor and unrolls that element's queries up to the browser object, so it can be reused in many page objects (or other component objects), without having to know anything about the page itself.
class Component {
constructor(host) {
const selectors = [];
// Crawl back to the browser object, and cache all selectors
while (host.elementId && host.parent) {
selectors.push(host.selector);
host = host.parent;
}
selectors.reverse();
this.selectors_ = selectors;
}
get host() {
// Beginning with the browser object, reselect each element
return this.selectors_.reduce((element, selector) => element.$(selector), browser);
}
}
module.exports = Component;
then what we would do is , We will write a subclass for our app-login component:
const Component = require('./component');
class Login extends Component {
get usernameInput() {
return this.host.shadow$('input #username');
}
get passwordInput() {
return this.host.shadow$('input[type=password]');
}
get submitButton() {
return this.login.shadow$('button[type=submit]');
}
login(username, password) {
this.usernameInput.setValue(username);
this.passwordInput.setValue(password);
this.submitButton.click();
}
}
module.exports = Login;
Finally, we can use the component object inside our login page object:
const Login = require('./components/login');
class LoginPage {
open() {
browser.url('/login');
}
get app() {
return browser.$('my-app');
}
get loginComponent() {
// return a new instance of our login component object
return new Login(this.app.$('app-login'));
}
}
Now this component object can now be used in tests for any page or section of your app that uses an app-login web component, without having to know about how that component is structured. If you later decide to change the internal structure of the web component, you only need to update the component object.
Now we apply the same approach with the Check Box Component by using Shadow Dom Support :
public class CheckBox extends Component {
public CheckBox(element) {
this.element = element;
}
get checkBoxSelector() {
return this.host.shadow$(element);
}
get void toggle() {
checkBoxSelector().click();
}
get void check() {
if (!isChecked()) {
toggle();
}
}
get void uncheck() {
if (isChecked()) {
toggle();
}
}
get boolean isChecked() {
return checkBoxSelector().isSelected();
}
}
Then We can write a Check Box Controller component that can get the instance of check box using id and verify what every is necessary.
const CheckBox= require('./components/CheckBox');
class CheckBoxController{
open() {
browser.url('/login');
}
get checkboxComponent() {
// Using this we can verify whether the Specific Check Box has been Selected or Not
let element = browser.$('[id="lpagecheckbox"]');
return new CheckBox(element);
}
}
Note :
Please bear this is not the actual code , This is just a part of the template which can help us to move towards the solution of the Problem .
Source Contends :
https://webdriver.io/docs/api/element/isSelected.html
https://webdriver.io/blog/2019/02/22/shadow-dom-support.html
https://webdriver.io/blog/2019/04/03/react-selectors.html
https://webdriver.io/docs/pageobjects.html
Moreover if we are using Selenium Webdriver , This can help us to Achieve it
Here we have an interface which actually combines all the webdriver interfaces , then we create a Specific implementation by inheriting the Element Class , finally Lets assume of any component you need we should be inheriting and using it with its own implementation , In this case lets assume the Check box that should be inherited from then Element Implementation Class and finally a Cranky way of using it by instantiating the object. CheckBox cb = new CheckBox(element);cb.uncheck();
Step 1:
Create an Interface that combines all of the WebDriver interfaces:
public interface Element extends WebElement, WrapsElement, Locatable {}
Step 2:
Element Implementation Inheriting the element class:
public class ElementImpl implements Element {
private final WebElement element;
public ElementImpl(final WebElement element) {
this.element = element;
}
#Override
public void click() {
element.click();
}
#Override
public void sendKeys(CharSequence... keysToSend) {
element.sendKeys(keysToSend);
}
// And so on, delegates all the way down...
}
Step 3:
Consider any component you use , Lets assume Check Box in this case
public class CheckBox extends ElementImpl {
public CheckBox(WebElement element) {
super(element);
}
public void toggle() {
getWrappedElement().click();
}
public void check() {
if (!isChecked()) {
toggle();
}
}
public void uncheck() {
if (isChecked()) {
toggle();
}
}
public boolean isChecked() {
return getWrappedElement().isSelected();
}
}
Way of Using It :
CheckBox cb = new CheckBox(element);
cb.uncheck();
If you want More Clear way of Implementing Something Like this : refer the third Link
public class Part2ExampleTest {
private final WebDriver driver;
#FindBy(id = "checkbox")
CheckBox checkBox;
protected Part2ExampleTest(WebDriver driver) {
this.driver = driver;
}
protected static Part2ExampleTest initialize(WebDriver driver) {
return ElementFactory.initElements(driver, Part2ExampleTest.class);
}
#Test
public void simple() {
WebDriver driver = new FirefoxDriver();
Part2ExampleTest page = initialize(driver);
PageLoader.get(driver, "forms.html");
Assert.assertFalse(page.checkBox.isChecked());
page.checkBox.check();
Assert.assertTrue(page.checkBox.isChecked());
driver.close();
}
}
Sources :
Extend Selenium WebDriver WebElement?
http://elisarver.com/2012/12/09/wrapping-webelement-1/
http://elisarver.com/2012/12/10/wrapping-webelement-2
IWebElement is an interface that you can just implement inside your driver class.
Related
I am trying to write a function that sets some properties on its arguments (not return a new object), the argument is of a class that extends from the same basecalss ie
class BaseEvent {
string eventName!;
}
class DeleteEvent extends BaseEvent {
string deleteReason;
}
class UpdateEvent extends BaseEvent {
string updatedBy;
}
const modifyEvent = async ( event: BaseEvent){
switch event.eventName{
case "deleteEvent":
event.deleteReason="xyz"; //doesn't work deleteReason doesn't exist on base class
return;
case "updateEvent":
event.updatedBy="abc";
return;
}
}
and so I tried this:
switch (true){
case event instanceOf DeleteEvent:
event.deleteReason="xyz"; //doesn't work deleteReason doesn't exist on base class
return;
case event instanceOf UpdateEvent: //still doesn't work
event.updatedBy="abc";
return;
}
I've also tried typecasting
let modifiedEvent= event; //argument event
switch event.eventName{
case "updatedEvent":
modifiedEvent=<UpdatedEvent> modifiedEvent; //no luck here either
}
Now there should be a simple/clean solution for this but sadly my patience is exhausting. Any suggestion would be very helpful. Again I want to take an object of child class, see if its of childA or childB and if its childA then i update one property and if its childB then i update another property and these properties don't exist on both child types
EDIT: Also the function is async because based on what child class this is I'm doing some database read operations etc
I want to do something like this in a razor page:
#if (currentwidth<x)
{
le code
}
else
{
le other code
}
I have added javascript file and connected it to a utility service which works when I get the static width (which I tested).
js:
export function getCurrentWidth() {
return window.addEventListener("resize", () => {
window.innerWidth;
});
}
UtilityService method:
public async Task<double> GetCurrentWidth()
{
var m = await GetModule();
var result = await m.InvokeAsync<double>("getCurrentWidth");
return result;
}
Razor file:
double width;
protected async override Task OnInitializedAsync()
{
width = await utilityService.GetCurrentWidth();
}
Thus the problem is I can't call it from OnInitailzedAsync since this function only fired once, so I need a function that constantly checks the GetCurrentWIdth() method to check for resize.
Is there another way of doing this in blazor or what method could I use/ Thank you in advance.
First of all I'd like to point out that you may not have to solve your problem with javascript/C# code. If it's something in the html that you want to manipulate, you may just be better of using css. But I'll leave that up to you.
If however you truly need the window width like you mentioned above, then I would recommend registering a listener to the window (as you've already done) and have that listener call a dotnet function. Doing this with static methods is quite easy, but for instance component this can be a bit trickier as you have to pass an object reference of the current object.
The [JsInvokable] indicates that this method can be called from javascript, which allows communication from the javascript event listener to dotnet.
CSharpFromJs.razor.cs
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System.Threading.Tasks;
public partial class CSharpFromJS
{
private DotNetObjectReference<CSharpFromJS> _objectReference;
public int WindowWidth { get; set; }
[Inject]
public IJSRuntime JSRuntime { get; set; }
protected override void OnInitialized()
{
_objectReference = DotNetObjectReference.Create(this);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await InitWindowWidthListener();
}
}
[JSInvokable]
public void UpdateWindowWidth(int windowWidth)
{
WindowWidth = windowWidth;
StateHasChanged();
}
private async Task InitWindowWidthListener()
{
await JSRuntime.InvokeVoidAsync("AddWindowWidthListener", _objectReference);
}
public async ValueTask DisposeAsync()
{
await JSRuntime.InvokeVoidAsync("RemoveWindowWidthListener", _objectReference);
_objectReference?.Dispose();
}
}
CSharpFromJs.razor
#implements IAsyncDisposable
<h1>Window width: #WindowWidth</h1>
Javascript
// Manages the registered event listeners so they can be disposed later
let windowEventListeners = {};
function AddWindowWidthListener(objReference) {
let eventListener = () => UpdateWindowWidth(objReference);
window.addEventListener("resize", eventListener);
windowEventListeners[objReference] = eventListener;
}
function RemoveWindowWidthListener(objReference) {
window.removeEventListener("resize", windowEventListeners[objReference]);
}
function UpdateWindowWidth(objReference) {
objReference.invokeMethodAsync("UpdateWindowWidth", window.innerWidth);
}
The only thing you should be careful with is when a component is disposd. You should remove the registered handlers in the DisposeAsync function to ensure that they're not still registered to prevent memory leaks.
This link might provide some better instructions on how to use this, but they don't explain the part about disposing handlers.
Note: This only works in .net5 and later, as IAsyncDisposable was not yet implemented for component before that. If for some reason you're working with an earlier version, you could call it using IDisposable. But this could potentially cause deadlocks, so I would not recommend it.
For me works the NuGet package BlazorPro.BlazorSize
Implement the method:
async void WindowResized(object _, BrowserWindowSize window){}
and all necessary Dependencies (see description).
I faced the following problem. There's a part of the webpage:
document.getElementById('continueButton').addEventListener('click', function () {
window.parent.postMessage('PAYLANDS-ERROR', '*');
if (window.parent.HTMLOUT) {
window.parent.HTMLOUT.paylandsFail();
}
});
Here's my interface which contains methods with annotation #JavascriptInterface:
interface IAddCard3dsJavaScript {
#JavascriptInterface
fun paylandsSuccess()
#JavascriptInterface
fun paylandsFail()
companion object {
const val INTERFACE_NAME = "HTMLOUT"
}
}
There's the class which implements this interface:
class AddCard3dsJavaScript(private val presenter: IAddCard3dsCallback) : IAddCard3dsJavaScript {
override fun paylandsSuccess() {
presenter.onPaylands3dsSuccess()
}
override fun paylandsFail() {
presenter.onPaylands3dsFailed()
}
}
But when I run the app I get this line in console: Uncaught TypeError: window.parent.HTMLOUT.paylandsFail is not a function. I tried to change the interface name to window.parent.HTMLOUT instead of HTMLOUT, and in this case, I didn't get the error, but paylandsFail method body didn't execute. So, what's the problem and how can I solve it?
UPD
Here's the way how I add this interface to webview:
#SuppressLint("JavascriptInterface")
override fun loadUrlFor3ds(url: String) {
addCardWebView?.apply {
addJavascriptInterface(AddCard3dsJavaScript(getController()), IAddCard3dsJavaScript.INTERFACE_NAME)
loadUrl(url)
}
}
Fixed by adding annotation #JavascriptInterface to AddCard3dsJavaScript - IAddCard3dsJavaScript implementation
I have 73 methods to execute in one test case[This is test to verify Auto merge rules in Salesforce]
As of now i am calling in the below manner :
SFHomePage rule_Test = new SFHomePage(driver, test);
PageFactory.initElements(driver, rule_Test);
test.log(LogStatus.INFO, "About to start validation of Rule 1");
rule_Test.verifyRule1(); // Calling the method to verify Rule 1
test.log(LogStatus.INFO, "Verification for Rule 1 is completed");
as mentioned in the above code i have to call all the 73 rules dynamically.
All the rules have separate methods to maintain the accountability.
I just tried the below manner but i know this will not work :
for(int i=1;i<43;i++)
{
test.log(LogStatus.INFO, "About to start validation of Rule "+i);
rule_Test.verifyRule+i(); // Calling the method to verify Rule
test.log(LogStatus.INFO, "Verification for Rule"+i+" is completed");
}
Is there any way to call these methods dynamically ?
You could use reflection API and getMethod(String name, Class<?>... parameterTypes). With this you could dynamically change method name and then invoke it. Heres the example:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
private final Logger logger = LoggerFactory.getLogger(getClass());
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException,
IllegalAccessException {
Test test = new Test();
for(int i = 1; i < 3; i++) {
String methodName = "verifyRule"+i;
Method method = test.getClass()
.getMethod(methodName);
method.invoke(test);
}
}
public Test() {
}
public void verifyRule1() {
logger.info("Verified rule 1");
}
public void verifyRule2() {
logger.info("Verified rule 2");
}
}
Result:
Connected to the target VM, address: '127.0.0.1:52987', transport: 'socket'
14:36:47.243 [main] INFO Test - Verified rule 1
14:36:47.293 [main] INFO Test - Verified rule 2
Disconnected from the target VM, address: '127.0.0.1:52987', transport: 'socket'
Process finished with exit code 0
There is a way to invoke a method when method name is in string variable. You fetch the class then fetch the method then invoke the method. You can create loop and change the number in the end of the method name string everytime.
// get class.
Class<?> myClass = Class.forName("MyClassName");
// create object of class.
Object myClassObject= myClass.newInstance();
// get method from MyClassName class.
Method myMethod = myClassObject.getClass().getMethod("myMethodName");
// call/invoke myMethodName method.
myMethod.invoke(myClassObject);
I have typescript function. Here is code
export class Step1{
constructor(){
this.begin_search();
this.results_ready();
}
private begin_search():void {
setTimeout(() => {
Searchfield.show_searchfield()
}, 100);
$('.search_box_overlay').show()
$('.search_box_overlay_top').show()
if (gon.search['search_type'] == 'package')
$('.search_box_overlay_top .package').show()
else if (gon.search['search_type'] == 'hotel')
$('.search_box_overlay_top .hotel').show()
else
$('.search_box_overlay_top .air').show()
window.seach_status_task = setInterval(Itinerary.check_search_status, 2000)
window.search_loading_itineraries = false
}
And then I importing this code into pack
Like this
$(document).ready(() => {
Translation.addDict(gon.translations);
Track.initialize();
new Searchfield();
if (!gon.search['searched']) {
Step1.begin_search();
}
if (gon && gon.search['id'] && $('.offer_hotel_addon').length > 0) {
check_status();
}
});
But when I run project, I have this error.
WEBPACK_IMPORTED_MODULE_3__components_step1.a.begin_search is not a function
Where can be problem and how I need to fix it?
The code defines begin_search as an instance method (a part of an instance), but is trying to use them as if they were static (a part of the class itself). Mark the method as static so that it belongs to the Step1 class itself:
private static begin_search():void {
See the handbook for more info on static methods. I'd also remove the this.begin_search(); call from the constructor as well. When the method is static, it doesn't exist on this anymore.