WebdriverIO function reusability pattern - webdriver-io

I am transitioning from Selenium to WebdriverIO and I'm running into some difficulty regarding function reusability. Let me demonstrate with an example:
<nav>
<div><a>Clients</a></div>
<div><a>Accounts</a></div>
<div><a>Packages</a></div>
</nav>
lets say I have a navigation bar with 3 links above. When I land on this page, I want to check if each link exists. My function may look something like this:
class LoginPage extends Page {
get clientsLink() { return $('//a[contains(., "Clients")]'); }
isTabDisplayed() {
if (this.clientsLink.isDisplayed()) {
return true;
} else {
false
}
}
}
this is fine except I would have to write 2 more getters for Accounts and Packages and so my class would look like this:
class LoginPage extends Page {
get clientsLink() { return $('//a[contains(., "Clients")]'); }
get accountsLink() { return $('//a[contains(., "Accounts")]'); }
get packagesLink() { return $('//a[contains(., "Packages")]'); }
isClientTabDisplayed(tab) {
if (this.clientsLink.isDisplayed()) {
return true;
} else {
false
}
}
isAccountsTabDisplayed(tab) {
if (this.accountsLink.isDisplayed()) {
return true;
} else {
false
}
}
isPackagesTabDisplayed(tab) {
if (this.packagesLink.isDisplayed()) {
return true;
} else {
false
}
}
}
at this point, my anxiety kicks in and I start to think of ways I can reuse the isTabDisplayed function where I can pass a string to the getter with my tab name, or something along the lines of that.
Unfortunately, getters do not accept parameters and so far I have not found any resources on google that can help me to solve this issue (most common being Page Object Model which doesn't seem to address this problem)
Is my thought process out of line that I am striving for reusable code in UI testing or am I not googling for correct patterns?

Page Objects in WebdriverIO are just plain ES6 classes. Have a look through the documentation on ES6 classes to understand how you can create functions that you can pass arguments in to.
Now, that being said, what you're doing here isn't necessary. Instead of creating a function which references a getter, why not just reference that getter directly in your test?
const login = new LoginPage();
const isAccountsTabDisplayed = login.accountsLink.isDisplayed();
There's really no reason to create a wrapper function around this.

Related

Modelina Csharp Generator Add Inheritance

I am playing around with asyncapi/modelina CSharpGenerator. I would like to add inheritance to the generated class something like this
public class UserCreated: IEvent
{
}
Is that possible? Can we add additional dependencies other than the generated ones?
Inheritance is, unfortunately, one of those features that have gotten put on the backburner, and still is.
Fortunately, it is possible to accomplish it, but it does require you to overwrite the entire rendering behavior, which might not be maintainable in the long run. You can find the full example in this PR: https://github.com/asyncapi/modelina/pull/772
const generator = new CSharpGenerator({
presets: [
{
class: {
// Self is used to overwrite the entire rendering behavior of the class
self: async ({renderer, options, model}) => {
//Render all the class content
const content = [
await renderer.renderProperties(),
await renderer.runCtorPreset(),
await renderer.renderAccessors(),
await renderer.runAdditionalContentPreset(),
];
if (options?.collectionType === 'List' ||
model.additionalProperties !== undefined ||
model.patternProperties !== undefined) {
renderer.addDependency('using System.Collections.Generic;');
}
const formattedName = renderer.nameType(model.$id);
return `public class ${formattedName} : IEvent
{
${renderer.indent(renderer.renderBlock(content, 2))}
}`;
}
}
}
]
});
What is happening here is that we create a custom preset for the class renderer and overwrite the entire rendering process of itself.
This will generate based on this input:
public class Root : IEvent
{
private string[] email;
public string[] Email
{
get { return email; }
set { email = value; }
}
}
Regarding dependencies, please see https://github.com/asyncapi/modelina/blob/master/docs/presets.md#adding-new-dependencies. You can do this in the self preset hook.
You can read more about the presets here: https://github.com/asyncapi/modelina/blob/master/docs/presets.md

How can I change css directly(without variable) in Blazor?

I am using the server-side of Blazor.
I want to change the CSS of the body.
In Jquery I can write the code like this easily:
$("body").css("overflow-y","hidden");
However, with this tutorial(Blazor Change Validation default css class names) said, it seems I can only change the CSS by changing the class name.
It is so complex while crossing the component, especially the body is at the top of all the components.
I wonder whether there is a way can changes CSS directly in Blazor. Thank you.
There are several ways of getting out of the "blazor way" of doing things and accomplishing css modification of an element.
Simplest: Just like you can use the class attribute, use the style attribute
<element style=#myStyle></element>
#code {
string myStyle;
void MyMethod() {
myStyle="overflow-y: hidden;"
}
}
Advanced: Use JS interop
a. In the main view (index.html or Pages/_Host.cshtml depending on project type), create a js endpoint for your component
<script>
window.applyStyleForElement = function(styleOp) {
document.getElementById(styleOp.id).style[styleOp.attrib] = styleOp.value;
}
</script>
b. In razor file:
#Inject IJRRuntime JSRuntime
<element id=#myId></element>
#code {
string myId = Guid.NewGuid().ToString("n");
async Task MyMethod() {
await JSRuntime.InvokeAsync("applyStyleForElement",
new { id = myId, attrib = "overflowY", value = "hidden" });
}
}
Finally, applying to your special case with body element ("advanced" method above).
a. In the main view (index.html or Pages/_Host.cshtml depending on project type), create a js endpoint
<script>
window.applyStyleForBody = function(style) {
document.body.style[style.attrib] = style.value;
}
</script>
b. In razor file:
#Inject IJRRuntime JSRuntime
(...)
#code {
async Task MyMethod() {
await JSRuntime.InvokeAsync("applyStyleForBody",
new { attrib = "overflowY", value = "hidden" });
}
}
Well, Blazor does not support direct css modification yet, since Web Assembly doesn't. Anyway heads up, it is on the road-map for Web Assembly/Blazor.
Therefor your best bet is, changing the class name with variables. At least for now.
I'M NOT SURE IT'S THE RECOMMENDED WAY BUT it works!
For one of my project, I include a in the page html itself, using params :
<style>
html
{
background-color:#_ColorCss;
}
</style>
//html stuff here
#code
{
public string Color{ get; set; } = "white";
string _ColorCss => $"{Color}"; //use this in case of formatting (ex : add 'px' or that kind of things)
//code stuff here
}
not the very sexiest way but it works
have fun !
Well, actually there is a way to do that and it works really good (it might suffer a little delay though).
I know this answer is a little bit late but it might help other people who face the same challenge.
We need to create some JS code that includes the wanted files:
function includeLeftStyle() {
appendStyle("left.css");
}
function includeRightStyle() {
appendStyle("right.css");
}
function appendStyle(path) {
var element = document.createElement("link");
element.setAttribute("rel", "stylesheet");
element.setAttribute("type", "text/css");
element.setAttribute("href", path);
document.getElementsByTagName("head")[0].appendChild(element);
}
The wished CSS can be called according to the language (any other coditions) in the MainLayout:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (// is left)
{
await JSRuntime.InvokeAsync<object>("includeLeftStyle");
}
else
{
await JSRuntime.InvokeAsync<object>("includeRightStyle");
}
}
}
Happy coding! :)
Beginnig from the #Tewr answer, we can also change the whole class:
a) In the main view (index.html or Pages/_Host.cshtml depending on project type), create a js endpoint
<script>
window.applyStyleForElement = function (styleOp) {
if (styleOp != null) {
document.getElementById(styleOp.id).className = styleOp.value;
}
}
</script>
b) Then in the razor file
async Task MyMethod(string sortColumn)
{
await JsRuntime.InvokeVoidAsync("applyStyleForElement",
new { id = sortColumn, value = "newClassName" });
}

singleton object in react native

I'm new in react native.I want store multiple small small strings to common singleton object class and want to access it from singleton object for all component. Can anyone help me singleton object implementation for react native.
Ex
Component 1 -- Login button -- >> success --> need to store userID into singleton object.
Component 2 --> get stored userID from singleton object. How can i implement it.
Here is a simple way of doing it...
export default class CommonDataManager {
static myInstance = null;
_userID = "";
/**
* #returns {CommonDataManager}
*/
static getInstance() {
if (CommonDataManager.myInstance == null) {
CommonDataManager.myInstance = new CommonDataManager();
}
return this.myInstance;
}
getUserID() {
return this._userID;
}
setUserID(id) {
this._userID = id;
}
}
And here is how to use it...
import CommonDataManager from './CommonDataManager';
// When storing data.
let commonData = CommonDataManager.getInstance();
commonData.setUserID("User1");
// When retrieving stored data.
let commonData = CommonDataManager.getInstance();
let userId = commonData.getUserID();
console.log(userId);
Hope this works out for you :)
I suggest making a static class that stores data using AsyncStorage.
You mentioned in a comment that you are already using AsyncStorage, but don't like spreading this functionality throughout your app. (i.e. try-catches all over the place, each component needing to check if a key is available, etc.) If this functionality were in a single class, it would clean up your code a lot.
Another bonus to this approach is that you could swap out the implementation pretty easily, for example, you could choose to use an in-memory object or AsyncStorage or whatever and you would only have to change this one file
NOTE: AsyncStorage is not a safe way to store sensitive information. See this question for more info on the security of AsyncStorage and alternatives.
That said, this is how I imagine a global data holder class might look:
export default class dataManager {
static storeKeyValue(key, value) {
// your choice of implementation:
// check if key is used
// wrap in try-catch
// etc.
}
static getValueForKey(key) {
// get the value out for the given key
}
// etc...
}
Then to use this class anywhere in your app, just import wherever it's needed like so:
import dataManager from 'path/to/dataManager.js';
// store value
dataManager.storeKeyValue('myKey', 'myValue');
// get value
const storedValue = dataManager.getValueForKey('myKey');
EDIT: Using Flux, Redux, or a similar technology is probably the preferred/suggested way to do this in most cases, but if you feel the Singleton pattern works best for your app then this is a good way to go. See You Might Not Need Redux
There is a workaround for this, react native packager require all the modules in the compilation phase for a generating a bundle , and after first require it generates an internal id for the module, which is from then on referenced in the whole run-time memory , so if we export an instance of a class from the file, that object will be referenced every-time whenever that file is imported .
TLDR;
Solution I :
class abc {
}
module.exports = new abc()
Solution II : I assume you want to get your strings which are static and wont change , so you can declare them as static and access them directly with class name
FYI :this works with webpack also.
I might be too late for this, but I might as well share my own implementation based on Yeshan Jay's answer.
export default class Data {
static instance = null;
_state = {};
static get inst() {
if (Data.instance == null) {
Data.instance = new Data();
}
return this.instance;
}
static get state() {
return Data.inst._state;
}
static set state(state) {
Data.inst._state = state;
}
static setState(state) {
Data.inst._state = {...Data.inst._state, ...state}
}
}
And here's how you use it. It's pretty much mimicking React Component's state behavior, so you should feel at home with little to no adjustment, without the need to frequently modify the Singleton to add new properties now and then.
import Data from './Data'
// change the whole singleton data
Data.state = { userId: "11231244", accessToken: "fa7sd87a8sdf7as" }
// change only a property
Data.setState ({ userId: "1231234" })
// get a single property directly
console.log("User Id: ", Data.state.userId)
// get a single property or more via object deconstruction
const { userId, property } = Data.state
console.log("User Id: ", userId)
TS Class Example:
export class SingletonClass
{
private static _instance: SingletonClass;
public anyMetod(_value:any):any
{
return _value;
}
public static getInstance(): SingletonClass
{
if (SingletonClass._instance == null)
{
SingletonClass._instance = new SingletonClass();
}
return this._instance;
}
constructor()
{
if(SingletonClass._instance)
{
throw new Error("Error: Instantiation failed: Use SingletonClass.getInstance() instead of new.");
}
}
}
Use:
SingletonClass.getInstance().anyMetod(1);

Grails 3.0.x Interceptor matchAll().excludes for multiple controllers

Following Grails 3.0.11 Interceptors document, I code my own Interceptors as below:
class AuthInterceptor {
int order = HIGHEST_PRECEDENCE;
AuthInterceptor() {
println("AuthInterceptor.AuthInterceptor(): Enter..............");
// ApiController.index() and HomeController.index() don't need authentication.
// Other controllers need to check authentication
matchAll().excludes {
match(controller:'api', action:'index);
match(controller:'home', action:'index');
}
}
boolean before() {
println "AuthInterceptor.before():Enter----------------->>>>>>";
log.debug("AuthInterceptor.before(): params:${params}");
log.debug("AuthInterceptor.before(): session.id:${session.id}");
log.debug("AuthInterceptor.before(): session.user:${session.user?.englishDisplayName}");
if (!session.user) {
log.debug("AuthInterceptor.before(): display warning msg");
render "Hi, I am gonna check authentication"
return false;
} else {
return true;
}
}
boolean after() {
log.debug("AuthInterceptor.after(): Enter ...........");
true
}
void afterView() {
// no-op
}
}
class P2mController {
def index() {
log.debug("p2m():Enter p2m()..............")
render "Hi, I am P2M";
}
}
When I test http://localhost:8080/p2m/index, from log console, I saw that P2mController.index() is executed without been checked authentication.
However, when I test http://localhost:8080/api/index or http://localhost:8080/home/index, AuthInterceptor.check() is executed and the browser displays
Hi, I am gonna check authentication
I wish P2mController been checked authentication, and HomeController.index() and ApiController.index() don't need to be checked authentication. But from the log and response, the result is opposite.
Where is wrong in my AuthInterceptor ?
You want to do this instead:
matchAll().excludes(controller:'api', action:'index')
.excludes(controller:'home', action:'index')
And don't forget the single-quote after the first 'index'.

Aurelia DataTables Recompile

I've been exploring Aurelia and so far have loved what I've seen. I've come accross an issue that I'm not really sure how to solve. I used jquery datatables for large results in my current app with angular, using server side fetches. Datatables has a function you can call whenever a new row is added to the table (fnRowCallback - http://legacy.datatables.net/ref#fnRowCallback, or "createdRow" - https://datatables.net/examples/advanced_init/row_callback.html#) - This is really handy as you can recompile the dom after each row (costly I know).
This enables you to reference functions that exist in the current scope (or viewModel) that the datatable exists in. For example:
In my view model:
export class DataTableTest{
test(){
alert('this is a test');
}
}
In the return results from a datatable fetch:
{name:'blah',age:40,actions:"<a click.delegate='test();'>Test</a>"}
For some reason I can't seem to figure out how to recompile an element once it has been added to the dom.
Does anyone have any ideas how you could do this?
UPDATE:
These are the original options I pass to datatables:
var options = {
"fnRowCallback": function (nRow) {
$compile($(nRow).contents())(scope);
}
};
I've tried the following after injecting that compiler service:
"fnRowCallback": function (nRow) {
this.compiler.compile($(nRow).contents()).fragment.innerHTML;
},
But I always get Uncaught TypeError: Cannot read property 'compile' of undefined - I do this in the "attached" function.. If I console.log(this.compiler) outside of these options, it's available. Also, we don't need to return html back to datatables, just run the compile on the contents. Many thanks for all your help!
You can use a compiler service to compile the element:
import {inject, ViewCompiler, ViewResources, Container} from 'aurelia-framework';
/**
* Compiler service
*
* compiles an HTML element with aurelia
*/
#inject(ViewCompiler, ViewResources, Container)
export class Compiler {
viewCompiler: any;
resources: any;
container: any;
constructor(viewCompiler, resources, container) {
this.viewCompiler = viewCompiler;
this.resources = resources;
this.container = container;
}
compile(templateOrFragment, ctx = null, viewSlot = null):any {
if (typeof templateOrFragment === "string") {
var temp = document.createElement('span');
temp.innerHTML = templateOrFragment;
templateOrFragment = temp;
}
var view = this.viewCompiler.compile(templateOrFragment, this.resources).create(this.container, ctx);
return view;
}
}
I use this in Kendo in the cell template callback function (it lets you return a string that will become the cell contents)
function(dataItem) {
var cellctx = { "$item": dataItem, "$parent": ctx };
return this.compiler.compile(templateString, cellctx).fragment.innerHTML;
}
(this happens in Aurelia's bind callback so the ctx is the executionContext)
I just wrap the current data item up in a context and alias it as $item so I can work with it.
Looks something like this:
<kendo-grid>
<kendo-grid-col title="Main Office" field="IsMainOffice">
<kendo-template><img if.bind="$item.IsMainOffice" src="/content/img/accept.png" /></kendo-template>
</kendo-grid-col>
</kendo-grid>