How To Find HTML Elements Using Cypress Locators

Sachinj34 - Nov 10 '22 - - Dev Community

Cypress is a new yet upcoming automation testing tool that is gaining prominence at a faster pace. Since it is based on the JavaScript framework, it is best suited for end-to-end testing of modern web applications. Apart from the QA community, Cypress can also be used effectively by the front-end engineers, a requirement that cannot be met with other test automation frameworks like Selenium.

Since Cypress is an open-source framework suited for testing modern web applications, many QA engineers prefer Cypress test automation over testing with other automation frameworks.

To access any WebElement in the DOM (Document Object Model), you would need to use Cypress locators, comparable to locators in Selenium. Thus, by the end of this Cypress tutorial, you would be in a position to use Cypress locators to access WebElements in the DOM for realizing Cypress automation tests.

New to CSS Selectors? Check out this Ultimate CSS Selector cheat sheet to boost your web designing career.

Starting your journey with Cypress Testing? Check out how you can test your Cypress test scripts on LambdaTest’s online cloud.

What are Cypress Locators

If you are coming from a Selenium automation background and willing to jump ship to the Cypress framework, the Cypress vs Selenium comparison would be helpful to make a wiser decision! In addition, you can take a quick refresher of the Cypress framework by visiting our earlier blog that deep-dives into the Cypress architecture and design.

Akin to locators in Selenium, Cypress locators are also used to find WebElements in the HTML Document Object Model (DOM). Once you find multiple elements in Cypress, relevant operations can be performed on these elements or assert their presence for validation.

Locators are generally classified in the following categories, while they are being used to find multiple elements in Cypress:

  • tags — Locate WebElements using HTML tags like , , , etc.

  • ids — Locate WebElements using the ID of the elements

  • name — Locate WebElements using name attribute that is set for elements (especially form input elements)

  • class — Locate WebElements by class value

  • links — Locate WebElements by links text property

  • attributes — Locate WebElements by one or multiple sets of attributes

Since the core fundamentals of web locators are agnostic of the automation framework, you can refer to our locators in Selenium that deep dive into each locator (mentioned above) in more detail.

Get the best out of your Cypress tests with LambdaTest’s online Cypress automation tool. Check out how you can test your Cypress test scripts using LambdaTest’s online cloud.

Test your Puppeteer test scripts online. Deploy a quality build faster by running automated checks across over 3000+ browsers and OS combinations with the LambdaTest cloud. Try for free.

How to use Locators in Cypress

Cypress supports only CSS selectors out of the box for locating elements in the DOM. So in case you intend to use other web locators like XPath, ID, etc., with Cypress, you need to add relevant external packages that support using those with Cypress locators.

Let’s deep dive into details of how to use and work with locators in Cypress. For this blog in the Cypress tutorial series, I will be using ecommerce practice site for showcasing the usage of the relevant web locators.

You can go through the following video to learn Cypress GET and FIND Command Hacks supported by the Cypress test automation framework. This will help you write your first cypress test and target HTML elements using the GET command. However, visit the LambdaTest YouTube channel for more Cypress videos, so there’s never a shortage of Cypress learning opportunities.

Identify HTML elements using ‘ID’ Cypress locator

IDs are the unique attributes given to the HTML elements that can be identified and updated with minimal effort. The ID attribute is unique across the DOM, due to which it is the fastest way for locating the required WebElement. It also helps in accelerating the interactions with the underlying elements in the DOM.

Here is a sample HTML page that showcases the syntax of ID in HTML:

<ul id="lists">
   <li id="first"> Option 1 </li>
   <li id="second"> Option 2 </li>
   <li id="third"> Option 3 </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

Here,

    tag can be identified uniquely with CSS selectors command using $(‘#lists’).

    Demonstration: ID locator in Cypress

    Let’s look at using an ID locator in Cypress using the example site that we showed earlier. Here, we locate the search bar on the site; the HTML representation of the search bar is below:

    <input class=“search_query form-control ac_input” type=“text”
    id=“search_query_top” name=“search_query” placeholder=“Search” value="" autocomplete=“off”>
    
    Enter fullscreen mode Exit fullscreen mode

    Here is the implementation that demonstrates the use of the ID locator in Cypress:

    describe('Cypress Locators in action', ()=>{
       beforeEach(()=>{
           cy.visit('[http://automationpractice.com'](http://automationpractice.com'))
       })
       it('Interact with Elements using ID selector', ()=>{
           cy.get('#search_query_top') //ids are identified using # prefix
           .type('Dress')
           .type('{enter}')
           .get('#center_column')
           .should('be.visible')
       })
    })
    
    Enter fullscreen mode Exit fullscreen mode

    cy.get() *is used in Cypress to find multiple elements in DOM. The method accepts a parameter in the form of an identifier. Here we pass the *ID of the element with ‘#’ as a prefix. Since the call is chainable, we used type() to enter values and used should() to assert the visibility of the corresponding WebElement.

    Shown below is the execution screenshot, which indicates that the test execution was successful:

    Identify HTML elements using ‘NAME’ Cypress locator

    The HTML name attribute is mostly used by ,, tags where it helps to identify field details when submitted to the server. You can refer to our detailed blog on NAME locator in Selenium to get insights on the said web locator.

    Here is a sample HTML page that showcases the syntax of NAME in HTML:

    <input class=“search_query form-control ac_input” type=“text”
    id=“search_query_top” name=“search_query” placeholder=“Search” value="" autocomplete=“off”>
    
    Enter fullscreen mode Exit fullscreen mode

    Demonstration: NAME locator in Cypress

    Shown below is the syntax of the NAME locator in Cypress:

    cy.get(<tag>[name='value'])
    
    Enter fullscreen mode Exit fullscreen mode

    where can be HTML tags like input, form, etc. is optional, when not specified, it will match name against all the tags (wherever match is found).

    describe('Cypress Locators in action', ()=>{
       beforeEach(()=>{
           cy.visit('[http://automationpractice.com'](http://automationpractice.com'))
       })
    it('Interact with Elements using name attribute',()=>{
       cy.get('input[name="search_query"]') //observe how we use name attribute
       .type('Tshirt{enter}')
       .get('#center_column')
       .should('be.visible')
    })
    })
    
    Enter fullscreen mode Exit fullscreen mode

    Code walkthrough

    Once the site is opened after the beforeEach() execution, the test “Interact with Elements using name attribute” starts with identifying elements using the name attribute (‘search_query’) and then sends the search value to the text box using the type() method.

    it('Interact with Elements using name attribute',()=>{
       cy.get('input[name="search_query"]') //observe how we use name attribute
       .type('Tshirt{enter}')
    
    Enter fullscreen mode Exit fullscreen mode

    The Inspect tool in Google Chrome is used for locating the element using the CSS Selector. Here is how we use the browser console using $() as the CSS selector:

    Once the search results are available, we can use the ID locator in Cypress to find multiple elements and assert using Cypress’s built-in assertion method *should(‘be.visible’). *An assert is raised if the search results are not displayed properly.

    Execution

    Shown below is the execution snapshot, which indicates the respective WebElement was located successfully using the NAME locator:

    Identify HTML elements using ‘Attributes’ in Cypress

    Attributes are ways to set a particular property of the HTML element. They also help define the behavior of the elements when a specific condition is set and triggered. Attributes are applied to almost all the HTML tags and work similarly to the NAME locator in Cypress. Thus, we can use the same syntax for locating elements using other attributes as well.

    Shown below is the syntax to use attributes in Cypress:

    cy.get(<tag>[attribute_name=attribute_value])
    
    Enter fullscreen mode Exit fullscreen mode

    Additionally, consider a case where one would like to use Cypress to find multiple elements or all of them having attribute titles whose value contains ‘store’ (e.g., store-1, store-2, …). One of the brute force ways is to find each element one by one with the exact attribute value using the above syntax. However, this task is mundane and not beneficial when we need to find multiple elements in Cypress and add some common functional logic to them.

    A better approach would be to use the regex-supported syntax in the CSS selector to get the list of items in one go.

    Fortunately, Cypress provides an easier way to tackle this problem, where you can use patterns like starts-with and ends-with particular substring to match all the elements in the DOM.

    Purpose Syntax
    To find all the elements containing values *=, cy.get([attr_name *= attr_value])
    To find all the elements starting with attr_value, we use ^=, cy.get([attr_name ^= attr_value])
    To find all the elements ending with attr_value $=, cy.get([attr_name $= attr_value])

    Demonstration : Using Attributes in Cypress

    describe('Cypress Locators in action', ()=>{
       beforeEach(()=>{
           cy.visit('[http://automationpractice.com'](http://automationpractice.com'))
       })
    
       it('Finding elements using attribute values',()=>{
        cy.get('a[title*="store"]')
         .should('have.length.at.most',1)
    
        cy.get('a[title*="store" i]')  //case insensitive
         .should('have.length.gt',1)
           .each(i=> console.table([i.attr('href')]))
    })
    
    })
    
    Enter fullscreen mode Exit fullscreen mode

    Code Walkthrough

    Step1

    The cy.get() method is used in Cypress to find multiple elements based on attribute value using *title=store *for locating *Our Stores link from the footer as shown below:

    Step 2

    Once the intended WebElement is located, we validate that the page has at least one element that matches the required locator. Cypress built-in assertion method .should(‘have.length.at.most’,1) is used for checking length value where ‘have.length.at.most’ in Cypress evaluates the length of the located element.

    Step 3

    The cy.get(‘a[title=”store” i]’) *method is used to find all the links on the page that contains the title *“store”. *In the screenshot shown below, I have highlighted two elements on the page that matched the required locator:

    • The first one is the logo with the title = “My Store”

    • The second one is the footer with the title = “Our stores”

    Step 4

    The should() method is used for asserting if the length of the located WebElements is greater than 1. The length is checked using the *have.length.gt *method.

    .each() in Cypress is used to iterate on each of the located WebElements. Here I am printing the href *value using the *attr([key]) method that returns the attribute value for the key provided as the argument.

    .should('have.length.gt',1)
           .each(i=> console.table([i.attr('href')]))
    
    Enter fullscreen mode Exit fullscreen mode

    As seen below, the URL of each element that is located on the page is printed on the console:

    In this XCUITest tutorial, learn about XCUITest framework and its benefits for mobile automation testing. Take a look at how XCUITest works and see how to use it to test your mobile applications.

    Identify HTML elements using ‘By Class’ in Cypress

    HTML class attribute is mostly used for styling the element or group of elements. Unlike ID, class attributes need not be unique, which is why this is helpful when we need to apply a common style to multiple elements.

    In Cypress, class selectors can be identified using the following options:

    • cy.get(.)

    • cy.get([class=’<class_value’])

    Demonstration: Using ‘By Class’ in Cypress

    Consider the following HTML Page for demonstration:

    <a href="[http://automationpractice.com/index.php?id_category=3&controller=category](http://automationpractice.com/index.php?id_category=3&controller=category)" title=“Women” class=“sf-with-ul”>Women</a>
    
    Enter fullscreen mode Exit fullscreen mode

    Shown below is the implementation that demonstrates the usage of ‘By Class’ in Cypress:

    describe('Cypress Locators in action', ()=>{
       beforeEach(()=>{
           cy.visit('[http://automationpractice.com'](http://automationpractice.com'))
       })
    
      it('Interact with Elements using class selector',()=>{
            cy.get('.item')
            .eq(0) //0th element in the array of elements
            .click()
            cy.get('[class*="center_column"]') 
            .should('be.visible')
        })  
      it('Class selector short hand',()=>{
         //find class containing value .. using *=
         cy.get("a[class*='add_to_cart']")
         .eq(0)  // finding 0th element
         .click()
         .get("#layer_cart")
         .should('be.visible')
    
       //find class value that start with .. using ^=
        cy.get("[class^='layer_cart_product']")
        .should('be.visible')
        .get("a[title='Proceed to checkout']")
        .click()
    
       //find class that ends with... using $=
        cy.get('[class$="_delete"]')
        .click()
        .get('.alert')
        .should('be.visible')
     })
    })
    
    Enter fullscreen mode Exit fullscreen mode

    Code Walkthrough

    Test 1: Interact with Elements using class selector

    The cy.get(‘.item’) method is used to locate the element with class=”item”, which returns the list of elements matching the query.

    cy.get('.item')
    
    Enter fullscreen mode Exit fullscreen mode

    Here is how the locator is identified using the Inspect tool in Chrome:

    Once we get the list of the elements using the get() method, the eq() method in Cypress is used for locating the first element from the list. Once we locate the first element from the list, we click on the link that it points to (i.e. ‘Prices Drop’)

    .eq(0) //0th element in the array of elements
    .click()
    
    Enter fullscreen mode Exit fullscreen mode

    Next, we find the class containing value using the cy. get(‘ [class=”center_column”]’)* method. Raise an assert on the visibility of the element using the .should() method.

    Test 2: Class selector short hand

    We find the first product from the list of items and add that product to the cart. We then validate the product present in the cart, remove the product and verify if the cart is empty.

    For this, we make use of regex with the class selector so that the code looks cleaner.

    cy.get("a[class*='add_to_cart']").eq(0)
    
    Enter fullscreen mode Exit fullscreen mode

    The above code locates the ‘Add to cart button’ on the home page and returns the first button using eq(0).

    After clicking on the first button, we wait for the element with ID *#layer_car *to be visible to perform necessary operations on that WebElement.

    Once the button is visible, we click on the same by locating it using its title (i.e. ‘Proceed to checkout‘).

    cy.get("[class^='layer_cart_product']")
        .should('be.visible')
        .get("a[title='Proceed to checkout']")
        .click()
    
    Enter fullscreen mode Exit fullscreen mode

    We add a Product to the cart by clicking on the ‘Proceed to checkout’ button.

    Once the item is added to the cart, we delete the same and check if the shopping cart is empty. The delete button is located using class$=”_delete”.

    cy.get('[class$="_delete"]')
    .click()
    .get('.alert')
    .should('be.visible')
    
    Enter fullscreen mode Exit fullscreen mode

    Identify HTML elements by ‘Link Text’ in Cypress

    Links are ways for connecting web resources (or web pages) to another through anchor tags (i.e., ) and href attributes. For example, as shown below, My Orders is the link text displayed on the page.

    <a href="[http://automationpractice.com/index.php?controller=history](http://automationpractice.com/index.php?controller=history)">My orders</a>
    
    Enter fullscreen mode Exit fullscreen mode

    We can locate links in cypress using cy.get(:contains(‘’)). Shown below is the sample implementation that demonstrates the usage of Link Text in Cypress:

    it('Interact with links or button using partial text',()=>{
       cy.get('a:contains("My orders")')  // similar to partial link text
       .click()
       cy.get('.page-heading')
       .should('have.text','Authentication')
    })
    
    Enter fullscreen mode Exit fullscreen mode

    Shown below is the execution screenshot, which indicates that the test execution is successful:

    How to use the first() method in Cypress

    Consider a scenario where you want to get the list of elements in DOM having a common pattern. For example, it could be items in the list, and you might be interested in fetching only the first item from the list.

    To realize such a requirement, Cypress provides two approaches to get the first element in Cypress:

    • eq() — Fetch element at specified in the parameter. The example for this is covered above in the by class section.

    • first() — Fetch only the first element from the list. It is useful when we intend to validate only the first element in the list.

    To demonstrate the usage of the first() method in Cypress, we get all the links under the “item” class and use the first() method to click the first from the list.

    t.only('Find elements with first()',()=>{
       cy.get('.item a')
       .first()
       .click()
       .get('.navigation_page')
       .should('contain.text','Price')
    })
    
    Enter fullscreen mode Exit fullscreen mode

    Shown below is the approach through which the first element is located on the desired web page:

    Here is the execution snapshot:

    How to use the last() method in Cypress

    Akin to the first() method, Cypress’s last() method is used to locate the last element in a list of elements. This is a handy utility when working with applications like the ToDo app, where tasks are added at the end of the list and verification has to be done w.r.t the tasks’ addition (or deletion).

    For demonstrating the usage of the last() method, we use the React ToDo App where we would be adding items and accessing the last item from the list.

    it.only('Working with last()', ()=>{
       let task='Task at the end'
       cy.visit('[https://todomvc.com/examples/react/#/active'](https://todomvc.com/examples/react/#/active'))
       .get('.new-todo')
       .type(task)
       .type('{enter}')
       .get('.todo-list li')
       .last()
       .should('have.text',task)
    })
    
    Enter fullscreen mode Exit fullscreen mode

    Shown below is the execution snapshot:

    Starting your journey with Cypress End-to-End Testing? Check out how you can test your Cypress test scripts on LambdaTest’s online cloud.

    Test website on different browsers on a cloud platform. Get instant access of Browsers like Chrome, Safari, Firefox, Opera, Yandex, Brave, 3000+ desktop & mobile environments. Try for free!

    Handling ancestors and siblings elements in Cypress

    In the below HTML,

  • elements are grouped inside common
      and they all are at the same level from the root. These are called siblings.
        will be immediate parent to the lists while will be parent to
          &
        • .
          <div list="items">
             <ul id="social">
                   <li class='facebook'>facebook</li>
                   <li class='twitter'>twitter</li>
                   <li class='youtube'>youtube</li>
                    <li class='google-plus'>youtube</li>
             </ul>
          </div>
          

          There are certain cases in automation where we might need to check the sequence of the items displayed on the page and for this, we can use the next() call in the chainable form in Cypress to find multiple elements and assert the sequence. Furthermore, similar to next(), we can use the prev() call to find previous siblings of selected elements.

          nextUntil() and prevUntil() are the advanced ways of using the next() and prev() *methods in Cypress. Suppose you want to validate the sequence of all the elements under siblings until some conditions set through selectors are not met. The *nextUntil()/prevUntil() methods in Cypress validate the sequence of elements until certain conditions are met.

          Shown below is the implementation that demonstrates the handling of ancestors and siblings using Cypress:

          it.only('Working with siblings & parents', ()=>{
             cy.get('#social_block ul li')
             .first()
             .should('have.class','facebook')
             .next() //to next siblings
             .should('have.class','twitter')
             .next()
             .should('have.class','youtube')
             .next()
             .should('have.class','google-plus')
             .prev() // using prev() back to previous sibling of google-plus which should be 'youtube'
             .should('have.class', 'youtube') 
          
             cy.get('#social_block').siblings('div')  //siblings() method to check all sibling with <div> tag
             cy.get('#social_block').next('div').should('have.length',1)  //next() to find only next div
             cy.get('#social_block ul').children() //children() method to check sibling
          
             cy.get('#social_block>>li').next('.youtube') //next() with selector
             cy.get('#social_block').prev('div') //prev() with selector
          
             cy.get('.youtube').prevAll() //yields all before youtube
             cy.get('.facebook').nextAll() //yields all after facebook
          
             cy.get('.facebook').nextUntil('.google-plus') //yields twitter and youtube
             cy.get('#social_block').prevUntil('[class*="footer-block"]') //
          })
          

          Shown below is the execution snapshot that indicates that the test execution was successful:

          Take this certification to showcase your expertise with end-to-end testing using Cypress automahttps://www.lambdatest.com/blog/cypress-test-automation-framework/tion framework and stay one step ahead.

          Here’s a short glimpse of the Cypress 101 certification from LambdaTest:

          How to find HTML elements using Cypress XPath

          Although Cypress only supports CSS selectors by default, external packages are now available that let you use XPath with Cypress. To use Cypress XPath, perform the following steps:

          • Install cypress-xpath at the root of the project. using npm install cypress-path -d

          • Set require(‘cypress-xpath’) under cypress/support/index.js. With this, you are all set to use XPath in tests

          Using XPath is very popular with automation frameworks to locate elements in DOM. In practice, XPath uses generic format of //[@attribute=value]

          // is used in Cypress to find multiple elements anywhere in the DOM. It is also called the relative path. Similarly, there is ‘/’ , which can be used to find WebElement at an absolute location. is optional; if specified, all the elements matching the conditions will be returned.

          @attribute *is the attribute of the elements against which the match needs to be done. You can also use Cypress XPath functions like *contains(), text(), etc., to use more advanced features to locate relevant WebElements.

          Here is the sample implementation that demonstrates the usage of Cypress XPath [ i.e. *cy.xpath() *]:

          /// <reference types="cypress" />
          /// <reference types="cypress-xpath" />
          describe('Working with xpath in Cypress made easy', ()=>{
             beforeEach(()=>{
                 cy.visit('[http://automationpractice.com'](http://automationpractice.com'))
             })
             it('Xpath id usages', ()=>{  
                 cy.xpath('//*[[@id](http://twitter.com/id)="search_query_top"]')
                 .type('Dress')
                 .type('{enter}')
                 .get('#center_column')
                 .should('be.visible')
             })
             it('xpath class usage',()=>{
                 cy.xpath('//*[[@class](http://twitter.com/class)="item"]//a')
                 .eq(0) //0th element in the array of elements
                 .click()
                 cy.xpath('//*[contains([@class](http://twitter.com/class),"center_column")]')
                 .should('be.visible')
             })
             it('Interact with  Elements using name attribute',()=>{
                 cy.xpath('//input[[@name](http://twitter.com/name)="email"]')
                 .type('[xyz@yopmail.com](mailto:xyz@yopmail.com){enter}')
                 .get(".alert")
                 .should('contain.text','Newsletter')
             })
             it.only('Interact with elements using text', ()=>{
                 cy.xpath('//a[text()="Women"]')
                 .should('be.visible')
             })
          })
          

          Shown below is the execution snapshot:

          Perform free end to end Cypress testing online.

          Bonus Tip: Cypress Selector Playground

          Cypress has another useful feature for users that helps locate CSS selectors easily on pages directly from the test runner. Selector Playground will always match and find the unique locators for the elements to not create an issue when using them.

          Shown below is the priority of the unique locators that Cypress supports:

          • data-cy

          • data-test

          • data-testid

          • id

          • class

          • tag

          • attributes

          • nth-child

          Where data-* are the cypress recommended attributes added to the WebElements to isolate the elements from JS/CSS changes that might break the test.

          Conclusion

          Cypress is a cutting-edge browser automation solution that is fast gaining the attention of the software community. It offers a better, more effective approach to web testing and is gaining popularity due to its ability to help teams keep pace with a complete range of application testing.

          By using Cypress, you can take advantage of all built-in locators and features like intelligent matching and asynchronous commands to ensure that each test has no dependencies. In this Cypress tutorial, we have seen how we can use Cypress locators to access WebElements in the DOM for realizing Cypress automation tests. Moreover, although Cypress is built with a strict “no CSS selectors allowed” stance internally, external community packages now exist that allow you to write Cypress tests using XPath.

          Though Cypress has a rich feature set, the true capabilities of Cypress can only be leveraged by running Cypress tests on a scalable cloud-based cross browser testing platform like LambdaTest. LambdaTest Cypress CLI simplifies the task of executing your Cypress tests in parallel on a range of online browser and OS combinations hosted on the LambdaTest cloud grid. While writing this blog, there are 40+ browser versions for Cypress testing on the LambdaTest cloud grid.

          Happy Testing!

  • . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .