Challenge: Setup a locator using Xpath or CSS Selector that can be used to identify the tip on the worlds worst website. To make the challenge a little more difficult, you must not use the index or position number to identify the element.
Answer the this weeks challenge can be found here: https://louisegibbstest.wordpress.com/2021/12/06/weekly-xpath-css-selector-challenge-ministry-of-testing-website-6th-december-2021/

I guess I’m feeling a little mean this week. I’ve chosen the worlds worst website. It is a deliberately poorly designed website (at least I hope it is) with bright colours and flashy images that may hurt the eyes. One of its only features is a tips page, you’ll find the link on the home page (shown below).

Alternatively you could navigate straight to the tips page using the url (this is recommended): https://www.theworldsworstwebsiteever.com/tipz.php

Very few of the elements on this page have unique identifiers. This will make this challenge particularly difficult to complete. It may be tempting to come up with an xpath like this:

"(//p)[2]"

Or a CSS Selector like this:

"p:nth-of-type(2)"

This would successfully identify the tip, however would break if more text was added further up the page. I like to avoid using indexes or position numbers in xpaths or css selectors for this reason.

I don’t like setting challenges that I personally have not been able to solve, but I have done so on this occasion. I have successfully written an XPath that will identify this element, but not a CSS Selector. I hope someone else manages to do so, I have much to learn from people in the community.

Response To Last Weeks Challenge

Last week, I asked you to create an XPath or CSS Selector that could be used to retrieve the 2nd On This Day list item from the Wikipedia home page.

Congratulations to Emile Zwiggelaar, Vignesh and QAGooseUK for providing their own correct answers to the challenge.

Here are a few options that could be used:

XPaths:

"//div[@id='mp-otd']/li[2]"

We first identify that ‘on this day’ section of the page, which is a div element with the ID ‘mp-otd’. We then need to identify the list items that appear within the ‘on this day’ section. This will identify all ‘on this day’ list item.

Without specifying the index number, it will normally return just the first list item (unless we are trying to find a list of matching elements). To specifically identify the 2nd list item, we add [2] at the end of the xpath so it will only return the 2nd item.

Vignesh  submitted a similar XPath::

"//div[@id='mp-otd']/ul[1]/li[2]"

Instead of identifying just the list elements underneath the ‘on this day’ section, they chose to identify the first ul (unordered list) element under the ‘on this day’ element, and then identify the 2nd list item underneath this. By using a single forward slash (/), we only look at immediate descendants of the first element. A double forward slash (//) will identify all descendants.

In this case, both approaches work. However, if there were several lists each containing several list items, then Vignesh’s approach would work better as it allows us to only identify list items within a specific list.

CSS Selectors:

"#mp-otd li:nth-child(2)"

A hashtag (#) is used to identify an element using the ID. So #mp-otd will identify elements with an ID of ‘mp-otd’. We then want to identify 2nd available list item within the ‘mp-otd’ element, so this is done using “li:nth-child(2)”.

Alternative solutions included:

"#mp-otd>ul>li:nth-of-type(2)"

The > symbol can be used to identify immediate descendants of the element. So this approach would only identify the unordered list (ul) element within mp-otd, and then li elements within the ul element.

Notice that both solutions use different methods for locating the 2nd li element: nth-of-type and nth-child.

Both work slightly differently, and won’t always successfully identify the element.

  • li:nth-of-type(2) will identify the 2nd li element
  • li:nth-child(2) will identify the 2nd element but only if its an li type. If the element is not li, then it will not return the element.

As someone less experiences with CSS Selectors, I’ve only ever used nth-child and didn’t realise nth-of-type existed until I looked at the solutions others submitted. Looking at the benefits of both, I’ll probably start using nth-of-type more as I feel it would make the test more robust. If another element of a different type were to be added in, then the test might break if nth-child was used.

Most information about the difference between nth-child and nth-of-type can be found here: https://css-tricks.com/the-difference-between-nth-child-and-nth-of-type/

Other Examples

QAGooseUK provided an alternative solution which uses Playwright. I don’t know much about this myself, and didn’t get a chance to actually research this myself, so I’m assuming it works. Thanks so much for producing something different, this is the reason why I started this challenge so I could learn more about alternative locator strategies.

I’m going to go a little off-piste and offer a Playwright solution as well:

const otd = await page.locator(‘id=mp-otd >> :nth-match(li, 2)’)

Which essentially means “find the ‘on this day’ box, then inside the result of that query find the 2nd li.

I stuck the full example over at https://github.com/qagoose/weeklyXpathNov2021/blob/main/tests/wiki.spec.js which is written in the NodeJS version of Playwright.

Useful Resources

Two ‘games’ that will help you learn how to write XPaths and CSS Selectors.

Cheat Sheets

Information about differences between nth-child and nth-of-type for CSS Selectors:

Sample Code

For this code to work, you’ll need to install the following NuGet packages:

  • Selenium.WebDriver
  • Selenium.WebDriver.ChromeDriver
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Interactions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;

namespace LocatorChallenge
{
    public class Program
    {
        public static void Main(string[] args)
        {
            IWebDriver Driver = new ChromeDriver(Environment.CurrentDirectory);
            Driver.Navigate().GoToUrl("https://www.theworldsworstwebsiteever.com/tipz.php");
            Driver.Manage().Window.Maximize();

            //IWebElement TipText = Driver.FindElement(By.CssSelector(""));
            IWebElement TipText = Driver.FindElement(By.XPath(""));
            Console.WriteLine(TipText .Text);
        }
    }
}