I'm having issues with findByXpath ignore previous selection (selection done with the findXxxxxXxxx Commands).
My Page object looks like this
class DeleteModalUiComponent {
constructor(private remote: Remote) { }
getComponentContainer() {
return this.remote.findDisplayedByCssSelector('.delete-modal');
}
clickModalBotton(text: string) {
return this.getComponentContainer()
.findByXpath(`//button//*[contains(text(),'${text}')]`)
.then(elem => elem.click())
.waitForDeletedByCssSelector('.delete-modal');
}
}
I would like to base my Xpath find according the the container but findByXpath ignores the selection of getComponentContainer() and selecting from the root.
In the leadfoot the API says the following:
findByXpath
Gets the first element inside this element matching the given XPath
selector.
But using the // prefix in Xpath ignores this element.
The // prefix in Xpath ignores the current context element because // explicitly means 'search from the document root'. To search within the current context element, use .//.
Related
I have a SlickGrid Table, in which there are compound filters, currently when i try to change the compound filter (lets say from Equal To to Less Than), then it makes an API call.
I don't want to make an API call, how do i achieve this?
I searched in slickgrid docs, but couldn't find any property(if it is available).
Image
Please note that I'm the author of Angular-Slickgrid
So I looked at the problem you're having and it seems like a valid problem to look into, I agree that for some filters like the Compound Date Filter Operator we shouldn't query right away, that is after changing a the operator dropdown without providing a date. So, for that reason I am adding a new grid option skipCompoundOperatorFilterWithNullInput which will avoid triggering a filter change (it will also avoid querying the backend when implemented) when we first change the operator dropdown without providing a date being entered.
Note that this new option will only be available with Angular-Slickgrid v5.1.0+ (via this PR, now supports this and it will only be enabled by default on the Compound Date Filter (any other filters will have to explicitly enable this new flag either via grid option or via a column definition).
What if I cannot upgrade to 5.1.0? Are there any other ways of dealing with this?
Yes, it's just a bit more involving dealing with this though, it however requires a lot more work from your side. The information you need to know is that nearly every piece of code from Angular-Slickgrid and Slickgrid-Universal are protected TypeScript classes and functions which mean that you can simply use TypeScript to extends any of them. Let's take for example the CompoundDateFilter class, we could extend it this way to skip the callback triggering without a date provided (this._currentDate)
import { CompoundDateFilter, OperatorString } from '#slickgrid-universal/common';
export class CustomCompoundDateFilter extends CompoundDateFilter {
protected onTriggerEvent(e: Event | undefined) {
if (this._clearFilterTriggered) {
this.callback(e, { columnDef: this.columnDef, clearFilterTriggered: this._clearFilterTriggered, shouldTriggerQuery: this._shouldTriggerQuery });
this._filterElm.classList.remove('filled');
} else {
const selectedOperator = this._selectOperatorElm.value as OperatorString;
(this._currentValue) ? this._filterElm.classList.add('filled') : this._filterElm.classList.remove('filled');
// -- NEW CODE BELOW -- (to skip triggering callback on undefined date)
// when changing compound operator, we don't want to trigger the filter callback unless the date input is also provided
if (this._currentDate !== undefined) {
this.callback(e, { columnDef: this.columnDef, searchTerms: (this._currentValue ? [this._currentValue] : null), operator: selectedOperator || '', shouldTriggerQuery: this._shouldTriggerQuery });
}
}
this._clearFilterTriggered = false;
this._shouldTriggerQuery = true;
}
}
then use this new custom filter class in your column definitions
import { CustomCompoundDateFilter } from './custom-compoundDateFilter';
initGrid() {
this.columnDefinitions = [{
id: 'start', name: 'Start', field: 'start',
filterable: true, filter: { model: CustomCompoundDateFilter },
}];
}
and there you have it, below is a proof that it is working since I changed the operator and as you can see below this action is no longer resulting in 0 row returned. However if I had done the inverse, which is to input the date but without an operator, it would have execute the filtering because "no operator" is defaulting to the "equal to" operator.
I want to use assertion for this checkbox. It depends on duration. If it's checked duration = forever, if not = a month.
cy.wrap(cy.get('span.ant-checkbox').should('have.class','ant-checkbox-checked')).then((a) => {
if a == true {
cy.log('Forever')
}
})
A few thoughts:
You don't need to cy.wrap() your entire statement. cy.wrap() would primarily be used to wrap a JQuery yielded from cy.get() or a similar command, and insert it back into the Cypress command chain.
Your assertion that the element has a certain class will fail and stop your test before even getting to the .then() part of the command if the element does not have the ant-checkbox-checked class.
Instead, if we get the element, we can use JQuery functions (in this case, .hasClass())to determine if it has the class we want.
cy.get('span.ant-checkbox').then(($el) => {
// cy.get yields a JQuery<HTMLElement>
if ($el.hasClass('ant-checkbox-checked')) {
cy.log('Forever');
} else {
cy.log('A month');
}
});
I'm using Cypress for my automated tests. I'm trying to find a product on a page and click on it. If the product not displayed on the page, go to the next one until it is found.
I've been trying a lot of things already: while loop, each loop, simple cy.get but none of them work. Can anyone help me to solve this?
You'll need a recursive command, implementation of which will depend on your specific scenario. There's no one-size-fits-all solution, but generally it will look something like this:
function findElem ( targetSelector ) {
// first, we need to query a container (could be table, or a generic item
// container). The important thing is, this container must exist on every
// page you'll traverse.
cy.get('.someContainer').then( $container => {
// synchronously find the target element, however you want. In this
// example I use an attribute selector, but you can do whatever you
// want.
if ( $container.find(targetSelector).length ) {
return true;
} else {
return false;
}
}).then( found => {
if ( found ) {
return cy.log(`found elem "${targetSelector}"`);
} else {
// synchronously check if there's a next page/table
if ( Cypress.$('.nextPageButton').length ) {
// when know that there is a next page, click it using cypress
cy.get('.nextPageButton').click();
// here, assert that next page/table is loaded, possibly by
// asserting that current page/table is removed from DOM
// then, recurse
return findElem(targetSelector);
} else {
throw new Error(`Couldn't find elem "${targetSelector}" on any page`);
}
}
});
}
it('test', () => {
findElem(`[data-id="42"]`);
});
The crux of the solution is using a combination of commands to query a container, and synchronous inspection (using jQuery or whatever) to find the actual element. Learn more at Conditional Testing.
For a concrete implementation, you can refer to an older answer I gave to a similar question.
I need to check if the pop up exists, if it does then I need to check if its displayed then perform certain action on it.
I have implemented the below. I was wanting to know if there is any better way of achieving this.
licenseUpdate.isPresent().then(function (item) {
if (item == true) {
licenseUpdate.isDisplayed().then(function (res) {
if (res == true){
licenseUpdate.click();
};
});
}
});
If you are using page object (you should) you can write something like this:
clickLicenseUpdate() {
const licenseUpdate = $(licenseUpdateCssSelector);
return licenseUpdate.isPresent()
.then((isPresent) => {
if (!isPresent) { return false; }
return licenseUpdate.isDisplayed();
})
.then((isDisplayed) => {
if (!isDisplayed) { return false; }
return licenseUpdate.click().then(() => true);
})
}
Note that if you are using and old JS version (you shouldn't) you need to replace arrow functions with traditional anonymous functions.
Some helpful links about Page Object Design Pattern:
PageObjects
Martin Fowler PageObject
Code explained (or at least, that is the plan):
Using $ to locate an element but you can use any strategy
supported by Protractor.
browser.findElement(by.className('license')) equivalent to
$('license'), browser.findElement(by.id('license')) equivalent to
$('#license'). Check Protractor documentation for more examples.
Once you a have found a web element that match your locator, you can
use isPresent method to determine whether the element is present on
the page. isPresent returns a promise that resolve to a boolean
value.
then always return a promise. You can return a primitive value from
onFulfilled callback and that value would be cast to a promise with
resolve with same value. That is what is done here: if (!isPresent) { return false; }; or you can return another promise
from onFulfilled callback and the promise returned by then will be resolved or rejected with same value of returned promise.
That is what is done here: return licenseUpdate.isDisplayed();. isDisplayed() also return a
promise that will resolve with whether this element is currently
visible on the page.
This can be a bit overwhelming if you are not
used to deal with promises. Check this out Promises/A+
Finally, if the element is present and is displayed, click the element with theclick method that, surprise, also return a
promise (WebDriverJS API is based on promises).
Note that if element is not present, isPresent is false in this
line if (!isPresent), returning false immediately bypass
licenseUpdate.isDisplayed() execution and resolve with a false value. In that
case isDisplayed value is false and again false is returned
immediately bypassing the licenseUpdate.click() execution.
Also note that clickLicenseUpdate return a promise that will
resolve to false if the element is not present or if is present but not
displayed. To keep clickLicenseUpdate returned value consistent, I recommend you to wait for licenseUpdate.click() and then return a boolean value as it is done here: return licenseUpdate.click().then(() => true); (using implicit return from arrow functions) because promise returned by click() resolve with a void value.
That is harmless but is considered a good practice maintain a consistent return value, always a boolean value, not sometime a boolean and others a void value.
I am using a CellTable to display results of a query and I need these results to be shown as (html) links. I would like to react to a click on these links. So far, I had implemented it like this:
// ClickHandler on CellTable
cellTable.addCellPreviewHandler(new Handler<OperationalItemMultipleSearchRowData>() {
#Override
public void onCellPreview(CellPreviewEvent<OperationalItemMultipleSearchRowData> event) {
boolean isClick = "click".equals(event.getNativeEvent().getType());
if (isClick)
AdminUtils.EVENT_BUS.fireEvent(new SimpleSearchEvent(event.getValue().getName()));
}
});
The Problem is that this reacted to a Click on the whole row instead of the link. Due to architectural restrictions, the link itself is not a real html link, but a SafeHtml link that leads nowhere. I just needed the look&feel of a Link:
Column<OperationalItemMultipleSearchRowData, SafeHtml> nameColumn = new Column<OperationalItemMultipleSearchRowData, SafeHtml>(new SafeHtmlCell()) {
#Override
public SafeHtml getValue(final OperationalItemMultipleSearchRowData object) {
return new SafeHtml() {
#Override
public String asString() {
return "" + object.getName() + "";
}
};
}
};
How can I react to a click on this link only ? (instead of the whoel row)
Is there a more elegant way to implement this ?
Cheers
As with any other use of event delegation, the basic idea is to find walk up the hierarchy starting from the target of the event up until you find the link element you're looking for, or some other element that signals the search is over and the click was targetted outside the link (e.g. you reached the cell, the row or the table).
That being said, I think you should merge your behavior inside a specific Cell implementation rather than using a CellPreviewHandler (copy/paste the ActionCell or TextButtonCell as a starting point).
As a side note, I also believe you should not use a link when you're not actually linking anywhere, or try to provide a target for the link if the behavior is the one of a link (that way, right-clicking, middle-clicking or ctrl-clicking will Just Workâ˘). If you want the look of a link (without the "feel"), then just use an ActionCell or TextButtonCell and style it accordingly.