I am trying to improve my cucumber scenarios (BDD)
Lets say we have website, that can be in 3 states, and based on this state, it has different web elements in the page.
How would you write cucumber scenario making it as close to BDD methodologies, as simple to understand?
Currently I have:
Scenario Outline: View page in <PAGE STATE> state
Given I opened page in <PAGE STATE> state
Then I should see "<AVAILABLE ELEMENTS>
Examples:
| PAGE STATE | AVAILABLE ELEMENTS |
| State1 | Value input, Default slider, Active slider, |
| State2 | Value input, Default slider, Active slider, Type dropdown |
| State3 | Value input, Default slider, Active slider, Image uploader |
I would not recommend a scenario outline for all page states. You want each scenario to focus tightly on the thing you are asserting. For instance, the image uploader should be its own scenario:
Scenario: Users can upload an image when things are foobarred
# One or more `Given` steps to put the system into the correct state
Given a foo exists
And bar has happened
And a baz has been foobarred
When I am viewing the page
Then I should be able to upload an image
And a sample step making the assertion:
[Then(#"I (should|should not) be able to upload an image")]
public void ThenIShouldBeAbleToUploadAnImage(string assertion)
{
bool isFound = false;
try
{
var element = driver.FindElement(By.Css("input[type='file']"));
isFound = true;
}
catch (NoSuchElementException)
{
// do nothing
}
catch (WebDriverTimeoutException)
{
// do nothing
}
if (assertion == "should")
{
// Assert isFound is true
}
else
{
// Assert isFound is false
}
}
The scenario name clearly states a narrow test case. Each state of the page is clearly defined in the Given steps for this scenario. It also has only one assertion, and therefore only one reason this scenario should ever fail (the image upload field is not present).
You want your scenarios to focus on a single behavior. Your steps should not be describing the technical details of a page or screen, and instead should describe what the user is doing. You shouldn't assert that the upload field is visible. You should assert you are able to upload an image and have the step definition take care of finding the image upload field.
See BDD 101: Writing Good Gherkin for more advice on how to write good steps that describe behavior rather than a step by step process to accomplish a task.
Related
I would like to store the text from an object locator and use it for assertion. For instance, I have a trade number - 1234. This trade number only appears after a transaction, so it is not static on other screens. This number is located on several other screens and I need to validate that it appears. I am able to locate the element through inspect and Playwright accepts it, but having issues:
Grabbing the text (1234)
Then setting up an assertion statement to compare it
Below are my humble and naïve attempts:
async getConfirmNumber() {
//Store the contents in the page locator which has the trade number
const tradeNumber = page.locator('div:nth-of-type(2) > .col-md-9.display-value.ng-binding').textContent;
//Navigate to a different screen which now will display the trade number
await this.page.click('a[caption="History"]')
await this.page.click('a[href="#/trade-summary"]')
//Line of code that I am not sure how to correctly write. ".bidconfirmation" is the locator on the new screen which displays the trade number.
//If the contents or value of ".bidconfirmation" is NOT 1234 then an error needs to display.
await expect(tradeNumber).toHaveCSS('.bidconfirmation', tradeNumber);
}
Just to let you know I would change the tag on this post to playwright-JavaScript to better reach the intended audience.
However, if I understand your question correctly you are trying to get the text content of an element but the textContent() method is not working, I would try to use the innerText() method and see if that works.
Apologies if this is a little off as I work with the java version of Playwright but you could do:
const tradeNumber = page.locator('div:nth-of-type(2) > .col-md-9.display-value.ng-binding').innerText(); //BTW I would change this locator to something unique or a little more stable -- this should give you the tradeNumber
//then I'm not 100% sure what your trying to do here but if I understand correctly this might help
await expect(page.locator('.bidconfirmation').toHaveValue(tradeNumber));
I hope this helped a little, Im sorry I couldn't really get an understanding fully of the question you were asking but feel free to take a look at playwright.dev to find documentation surrounding Playwright.
I am working on creating automation test using Geb, Spock , Groovy, Selenium and page object model. This is more of a navigation test which I am working on.
In my test, I have the exact same steps (given, when, then) I follow, but in each test I am navigating to a different page. Then I verify if I have landed on correct page.
Since the steps are same and only the pages are changing, I am wondering if there is a way to use 1 test script (def) and iterate it multiple times using different parameters and perform page verifications. I am not sure if its doable, but if it is, can someone help me out?
Here is my current Geb spec with multiple feature methods, basically all following the same steps:
class NavSpec extends someTest{
def cleanupSpec() {}
def setup() {}
def cleanup() {}
def "Test1"() {
given:
TestPage testPage = to TestPage
when: "Do 1 thing"
testPage.clickLink1()
Test_Link1 test_Link1 = at Test_Link1
def cpage = test_Link1.cpd()
then: "You are on Page1"
!cpage || {at page1}
}
def "test2"() {
given:
TestPage testPage = to TestPage
when: "Do 2 thing"
testPage.clickLink2()
Test_Link2 test_Link2 = at Test_Link2
def cpage = test_Link2.cpd()
then: "You are on Page2"
!cpage || {at page2}
}
}
Any help would be really appreciated.
I have answered several of your questions, and of course Stack Overflow is a place to ask and find answers. But it seems to me that you never really thoroughly read either of
the Spock manual,
the Book of Geb.
Please try to reserve some time to do that, because all your questions are quite basic and answered in those valuable bodies of documentation. You will learn a lot, it is worth the effort. Thank you.
What you want is a parametrised Spock test, using either data tables or data pipes.
import spock.lang.Unroll
class TestNavigationInitialTestSpec extends LoginBaseTestSpec {
#Unroll("test portal page #number")
def "navigate to test portal pages"() {
given: "starting at home page"
to Test_HomePage
when: "clicking on navigation link"
page."$clickLink"()
then: "we are on target page"
at targetPage
!page.crashPageDisplayed()
where:
number | clickLink | targetPage
1 | 'clickLink1' | Test_Link1
2 | 'clickLink2' | Test_Link2
3 | 'clickLink3' | Test_Link3
}
}
Please note how I use the page variable, which is always bound in Geb specs and which points to the current page. At first, it points to the home page, then after at it is updated to point to the sub page you navigated to. That allows me to avoid checking at for the sub page twice, like you did in your sample code.
I also think it is unnecessary to close your browser at the end of the spec, assuming that you run multiple specs which can all use the same browser instance. Closing and reopening browsers slows down your tests.
BTW, please always try to provide a full MCVE when asking questions. In order to actually run the Geb spec above, I had to create 1 base spec, 4 dummy HTML pages and 4 Geb page classes, all matching your sample code. When I had that running, I refactored your 3 feature methods into 1. Next time, please publish that kind of stuff in your question. It is a bit tiresome for me to do your job, only to be able to answer your questions.
I am trying to complete a test case that involved opening propertypal.com, entering some values and validating the responses using the software cypress. I've never used cypress before but the company asking me to do this test want me to utilise it.
This is the website i'm testing https://www.propertypal.com/
I want to type bt6 into that text box, but I cant work out the correct locator to use. Everything I try either returns multiple elements or doesn't find anything.
Below are some of the things I tried with no success. The main things I have been honing in on are the placeholder text, the ID and element name.
I'm very new to this type of automation so any help would be amazing.
cy.get('query').type('bt6')
cy.get('input:first').should('have.attr', 'placeholder', 'Search Area, Address, Agent').click()
cy.get('search-form-textbox').type('bt6')
With this element
<input id="query"
type="text"
name="q"
class="search-form-textbox"
autocorrect="off"
spellcheck="false"
value=""
placeholder="Search Area, Address, Agent">
Using the id="query" should be best,
cy.get('input#query') // should only be one id "query" on the page
.type('bt6');
If there's multiple id's "query" and you want to flag it,
cy.get('input#query')
.then($els => {
expect($els.length).to.eq(1) // assert there's only one id found, otherwise fail
})
.type('bt6');
If there's multiple id's "query" and you don't really care, you can select the third which is the visible one.
cy.get('input#query')
.eq(2) // take the third, which is at center of the page
.type('bt6');
Taking the "nth" element found is always a bit fragile, but placeholder text is pretty good instead (provided the page isn't multi-lingual)
cy.get('input[placeholder="Search Area, Address, Agent"]') // easier with attribute in selector
.eq(1) // take the second, as the are two of these
.type('bt6');
Class is not so good, as can often be applied to multiple elements, but in this case it's pretty good because it's specific to the role,
cy.get('input.search-form-textbox') // prefix class with "."
.eq(2) // take the third, which is at center of the page
.type('bt6')
Web pages can often have multiple elements with the same selector, for example cy.get('input#query') has three elements with this id.
What happens is the developer creates a component, adds an id like <input id="query"> then adds the component in several places, so the page actually ends up with multiple ids of the same name.
When the page is complex with hidden sections, to find the element you want start by testing with a console.log
cy.get('input#query') // finds 3 elements
.then(console.log) // log them to dev tools
Open dev tools, click open the object printed and you can see the list of elements selected.
Now you can hover each element, and the corresponding element on the page is highlighted.
In this case the first two are hidden behind menu items, but the third is the one we need.
So now we can add an .eq(2) to select the third element
cy.get('input#query') // finds 3 elements
.eq(2) // take the third element
.type('bt6');
So when I ran the cypress Test for your website, I got an uncaught exception that is originating from your Application. This is something that should be analyzed and is possibly fixed.
If this is something that is not going to be fixed, then you can add the below in your test, so that cypress ignores the exception and doesn't throw an error.
Cypress.on('uncaught:exception', (err, runnable) => {
return false
})
Now I tried using the locator id(#query) to input the test, as it is always good to use id because it's unique, but unfortunately, it gave me an error as the element is not visible. Unfortunately adding {force: true} also didn't help me solve the issue.
cy.get('#query').type('bt6')
1.So, the locator that worked for me was .search-ctrl. So your Final Test will look like this:
cy.visit('https://www.propertypal.com/')
Cypress.on('uncaught:exception', (err, runnable) => {
return false
})
cy.get('.search-ctrl').type('bt6')
2.Now, if you want to globally handle the exception, then go to cypress/support/index.js and then write there:
Cypress.on('uncaught:exception', (err, runnable) => {
return false
})
And then in this case your test will just have:
cy.visit('https://www.propertypal.com/')
cy.get('.search-ctrl').type('bt6')
Django 1.9. I want to test that input forms are invisible normally. And only when a user presses toggle menu button, login and password input elements appear.
The problem is that this is all about methods of a class. I want to be sure that one method executes before another. I have found this solution with 0 and 1 in method names.
class FuncTestTablets(TestCase):
#classmethod
def setUpClass(cls):
pass
#classmethod
def tearDownClass(cls):
pass
def test_0_tablets_login_input_form_absent(self):
# At home page with tablet screen size Edith sees no login input element.
## 0 is for stating explicitly that this test goes before the ones with 1 in their name.
self.browser.get('http://localhost:8000')
login_input = self.browser.find_element_by_id('login')
self.assertFalse(login_input.is_displayed(), "Login input element is visible on large devices")
def test_1_tablets_login_input_form_present_when_menu_button_pressed(self):
# At home page Edith presses menu button and login input appears.
## 1 is for stating explicitly that this test goes after the ones with 0 in their name.
menu_button = self.browser.find_element_by_class_name('navbar-toggle')
menu_button.click()
login_input = self.browser.find_element_by_id('login')
self.assertTrue(login_input.is_displayed(), "Login input element is not visible on tablet devices when menu button is pressed.")
This seems to work. Could you tell me is there some generally known methodology for such cases. Maybe some best practice.
I've just started Django and I don't think that my solution is at once the best.
That's why I decided to ask you. Thank you in advance.
Best practice is that the order of your test methods does not matter at all and if you inherit from django.test.TestCase, it should not matter. I have, however, been in the situation where I wanted to test a feature 'foo' in one method and then, for convenience (like not having to wrap it in try-except), assume that it is working properly in other methods that test something else ('bar').
I named my test methods
test_a_foo
test_b_bar
This seems more appropriate then using numbers because tests are executed in lexicographic order where, e.g.
test_11_eleven
is executed before
test_2_two
If you then have to insert another test later, you can still change the names to:
test_b1_first_b
test_b2_second_b
I am creating a script that involved searching for a record and then updating the record. On the search screen, the user has the option of viewing advanced search options. To toggle showing or hiding advanced search is controlled by one button.
<a title="Searches" href="javascript:expandFilters()"><img border="0" align="absmiddle" alt="Advanced" src="****MASKED URL****"></a>
The only difference between the properties of the search button when it is showing or hiding the advanced search is the img src:
When advanced search is hidden the IMG src ends with "/Styles/_Images/advanced_button.jpg", when advanced search is visible, the IMG src ends with "/Styles/_Images/basic_button.png"
When I open the page, sometimes the Advanced search options are showing, sometimes they aren't. The value that I want to search on appears in the Advanced section, so for my script to work I have added an IF statement.
<input type="text" value="" maxlength="30" size="30" name="guiSystemID">
The IF statement looks for the fields that I need to enter data into, and if the field does not exist then that would indicate that the Advanced options are not visible I need to click on the button to expand the search option.
I created the following IF statement.
if (!driver.findElement(By.name("guiSystemID")).isDisplayed()) {
driver.findElement(By.cssSelector("img[alt='Advanced']")).click();
}
When I run the script and the Advanced search is expanded then the script runs successfully. However, when I run the script and the Advanced search is not expanded, the script fails, advising me that it could not find the object "guiSystemID". This is frustrating because if it can't find it then I want the script to continue, entering into the True path of the IF statement.
Has anyone got any suggestions about how else I could assess if the field is appearing without having the script fail because it can't find the field.
Thanks in advance
Simon
I might be late in answering this, but it might help someone else looking for the same.
I recently faced a similar problem while working with isDisplayed(). My code was something like this
if(driver.findElement(By.xpath(noRecordId)).isDisplayed() )
{
/**Do this*/
}
else
{
/**Do this*/
}
This code works pretty well when the element that isDisplayed is trying to find is present. But when the element is absent, it continues looking for that and hence throws an exception "NosuchElementFound". So there was no way that I could test the else part.
I figured out a way to work with this(Surround the {if, else} with try and catch block, say something like this.
public void deleteSubVar() throws Exception
{
try
{
if(driver.findElement(By.xpath(noRecordId)).isDisplayed() )
{
/**when the element is found do this*/
}
}
catch(Exception e)
{
/**include the else part here*/
}
}
Hope this helps :)
I've had mixed results with .isDisplayed() in the past. Since there are various methods to hide an element on the DOM, I think it boils down to a flexibility issue with isDisplayed(). I tend to come up with my own solutions to this. I'll share a couple things I do, then make a recommendation for your scenario.
Unless I have something very specific, I tend to use a wrapper method that performs a number of checks for visibility. Here's the concept, I'll leave the actual implementation approach to you. For general examples here, just assume "locator" is your chosen method of location (CSS, XPath, Name, ID, etc).
The first, and easiest check to make is to see if the element is even present on the DOM. If it's not present, it certainly isn't visible.
boolean isPresent = driver.findElements(locator).size() > 0;
Then, if that returns true, I'll check the dimensions of the element:
Dimension d = driver.findElement(locator).getSize();
boolean isVisible = (d.getHeight() > 0 && d.getWidth() > 0);
Now, dimensions, at times, can return a false positive if the element does in fact have height and width greater than zero, but, for example, another element covers the target element, making it appear hidden on the page (at least, I've encountered this a few times in the past). So, as a final check (if the dimension check returns true), I look at the style attribute of the element (if one has been defined) and set the value of a boolean accordingly:
String elementStyle = driver.findElement(locator).getAttribute("style");
boolean isVisible = !(elementStyle.equals("display: none;") || elementStyle.equals("visibility: hidden;"));
These work for a majority of element visibility scenarios I encounter, but there are times where your front end dev does something different that needs to be handled on it's own.
An easy scenario is when there's a CSS class that defines element visibility. It could be named anything, so let's assume "hidden" to be what we need to look for. In this case, a simple check of the 'class' attribute should yield suitable results (if any of the above approaches fail to do so):
boolean isHidden = driver.findElement(locator).getAttribute("class").contains("hidden");
Now, for your particular situation, based on the information you've given above, I'd recommend setting a boolean value based on evaluation of the "src" attribute. This would be a similar approach to the CSS class check just above, but used in a slightly different context, since we know exactly what attribute changes between the two states. Note that this would only work in this fashion if there are two states of the element (Advanced and Basic, as you've noted). If there are more states, I'd look into setting an enum value or something of the like. So, assuming the element represents either Advanced or Basic:
boolean isAdvanced = driver.findElement(locator).getAttribute("src").contains("advanced_button.jpg");
From any of these approaches, once you have your boolean value, you can begin your if/then logic accordingly.
My apologies for being long winded with this, but hopefully it helps get you on the right path.
Use of Try Catch defies the very purpose of isdisplayed() used as If condition, one can write below code without using "if"
try{
driver.findElement(By.xpath(noRecordId)).isDisplayed();
//Put then statements here
}
Catch(Exception e)
{//put else statement here.}