How To Fill And Submit Forms In Cypress

Sachinj34 - Nov 11 '22 - - Dev Community

Web forms are one of the most common types of forms that you may have encountered when interacting with websites. An example of a simple form is a login page, where the user needs to enter the login credentials to access the relevant features of the platform. On the other hand, complex forms can contain a combination of user details, captchas, date pickers, and more.

Be it Cypress automation testing or Selenium ; the tests are meant to interact with the WebElements on the page. As per my experience, automating the website’s behavior using appropriate methods is a must-have skill for Cypress testing.

In this blog of the ongoing Cypress tutorial series, I will be covering the integral aspects related to forms: How to use Cypress to fill out forms and How to use Cypress to submit forms. In case you are coming from a Selenium background and intrigued to know what is Cypress, make sure to check out the Selenium vs Cypress comparison.

What are Forms in HTML documents?

Before I deep dive into how to use Cypress to fill out forms or perform Cypress form validation, let me touch upon the essential aspects of Forms in HTML. Here, I will also focus on the different types of attributes that constitute the creation of a form.

HTML Forms are generally used to take different types of user inputs such as name, email address, phone numbers, card details, etc. These inputs are generally driven by forms through special elements (or controls) like checkboxes, text, password fields, input range, etc.

Users are generally required to complete a form by entering text, selecting items, etc., and submitting this form from the front-end. Once the content is submitted, a response is received from the server’s end. Understanding how to find HTML Elements using Cypress locators is a starting point to use Cypress to fill out forms and submit forms.

The

tag is used to create an HTML form. Here’s a simple example of a login form:
<html lang="en">
   <form>
       <label>User: <br>
       <input type="text"></label><br>
       <label>Password:<br>
       <input type="password"></label><br>
       <input type="submit" value="Submit">
   </form>
</html>

Now that we know the basic syntax of forms let’s implement the different input interactions with Cypress. I will walk you through each type of form that can be handled through the Cypress automation framework. I will also cover the potential limitations of Cypress from the perspectives of form submission and validation of forms.

Test your mobile testing lab app and websites on LambdaTest’s Mobile testing cloud and get instant feedback. Find bugs early on, improve performance, quality, user experience and save time with mobile application testing on LambdaTest.

Form submission with Cypress

Here are some of the major types of WebElements that most of us would have come across on web pages:

Input Elements

tag is the most used type of element in forms. This is because it allows you to add various types of user input fields that are majorly dependent on the attribute type.

An input element can be a text field, password field, checkbox, radio button, etc. Each of these elements serves different purposes, and the approach in handling them purely depends on the element type.

Here are some of the major input fields that can be handled with Cypress test automation:

Text Fields

Text fields are single text input controls. In text fields, the type attribute has a value of the text. Here’s an example text input used to take username:

Example: Handling Text fields in Cypress

For demonstrating how to handle text fields in Cypress, I will be using the following HTML form:

<html lang="en">
   <form action="https://www.automationtestinginsider.com/">
       First name:<br><input name="firstname" type="text"><br>
       Last name:<br><input name="lastname" type="text"><br>
     </form>
</html>

The output of the above example is shown below:

Here is the Cypress implementation for handling text fields on a test website:

beforeEach('Launch site', ()=>{
   cy.visit('https://www.automationtestinginsider.com/2019/08/textarea-textarea-element-defines-multi.html')
})

it('Interacting with text fields',()=>{
   cy.get('input[name="firstname"]')
     .type('Sachin')
.should('have.value','Sachin')
.get('input[name="lastname"]')
.type('Joshi')
.should('have.value','Joshi')
})

Code Walkthrough

I am using cy.visit() to open the target website in the above code. Once the site is loaded, the cy.get() function is called by passing the CSS web locator.

Appropriate CSS locators in Cypress are used to locate the desired WebElement and interact with the same. For example, on similar lines, the cy.get() method is used for entering the ‘last name’ in the text field.

For both the fields, I am asserting the value using .should() to validate if the text is showing the same value or not.

Check out the below video that deep dives into Assertions in Cypress:

However, you can visit the LambdaTest YouTube channel for more videos in the Cypress Testing tutorial and videos on Selenium testing, so there’s never a shortage of learning opportunities.

Password Field

The characters in the password fields are masked; hence, they are shown as dots or asterisks. Akin to text fields, password fields are also input controls created using an element with attribute type as password.

Example: Handling Password field in Cypress

Here’s an example of a password input inside

for user password:
<html lang="en">
   <form>
       <label for="pwd">Password:</label>
       <input type="password" name="password" id="user-pwd">
   </form>
</html>

The output of the above example is shown below:

Here is the Cypress implementation for handling password field on a test website:

it('Interacting with password fields' ,()=>{
   cy.visit('https://opensource-demo.orangehrmlive.com/index.php/auth/login')
   .get('#divUsername')
   .type("Admin")
   .get('#txtPassword').type('admin123')
   .type('{enter}')
   .get('.head')
   .should('be.visible');
})

Code Walkthrough

Here, I am visiting the site’s login page and locating the elements using the .get() method. An assert is raised using the using should() method if the login is unsuccessful.

Along with it, the visibility of the .head class is also checked to ensure that the required elements are available on the page.

Radio Buttons are used when the user selects exactly one option out of the listed ones when submitting the HTML form. The Radio button is created using an element with type attribute value as radio.

One of its common applications is when the user has to select their gender, marital status, or submit answers that have binary (i.e., yes/no) options.

Example: Handling Radio buttons in Cypress

Here’s an example of a radio buttons inside

tag:
<html lang="en">
   <form>
   <input type="radio" name="gender" id="male">
    <label for="male">Male</label>
    <input type="radio" name="gender" id="female">
    <label for="female">Female</label>
   </form>
</html>

Here is the output of the HTML code:

Here is the Cypress implementation for handling radio buttons on a test website:

it('Interacting with radio buttons',()=>{
   cy.visit('https://www.automationtestinginsider.com/2019/08/textarea-textarea-element-defines-multi.html')
   .get('input[value="Yes"')
   .click()
   .get('input[value="No"')
   .click()
   .get('input[value="Don\'t Know"')
   .click()
})

Code Walkthrough

The first step is to locate (or identify) the radio button with the Value attribute. For realizing this, I am leveraging the tag [Yes, No, etc.].

The click() method is used for selecting the required value (using the radio button on the form).

Checkboxes

Checkboxes in forms help you select more than one option from a list of predefined options. It is created using an element with attribute type as checkbox.

Example: Handling Checkboxes in Cypress

For demonstrating how to handle checkboxes in Cypress, I have created a simple HTML form that comprises a few checkboxes.

<html lang="en">
   <form>
       <input type="checkbox" name="policy" id="policy1">
       <label for="policy">Policy1 - I Agree</label>
       <br>
       <input type="checkbox" name="policy" id="policy2">
       <label for="policy">Policy2 - I Agree</label>
       <br>
       <input type="checkbox" name="policy" id="policy3">
       <label for="policy">Policy3 - I Agree</label>
       </form>
</html>

Here is the output of the following HTML code:

Here is the Cypress implementation for handling checkboxes on a test website:

it('Interacting with checkboxes',()=>{
 cy.visit('https://www.automationtestinginsider.com/2019/08/textarea-textarea-element-defines-multi.html')
     .get('input[value="Checkbox1"]')
     .click()
     .get('input[value="Checkbox2"]')
     .click()
     .get('input[value="Checkbox3"]')
     .click()
     .get('input[value="Checkbox1"]')
     .click()  //this to uncheck first one again
})

I have checked all the four checkboxes in the above example and unchecked them one by one.

Code Walkthrough

To get started, the required WebElement is identified using the Value attribute. Then, depending on your convenience, you can also use other attributes like ID, Name, etc.

Like XPath in Selenium, you can also choose to use XPath in Cypress if XPath is your go-to WebLocator for locating WebElements. As seen in the output below, Checkbox1 is unchecked (if checked) once we perform a click operation on it.

TextArea

Textarea is another form of text input field where users can enter more than one line of text. Textarea field is particularly useful when users submit lengthy text like feedback, instructions, etc.

With the attributes like rows, cols, and maxlength, you can limit the visibility and number of characters in the text area. The tag element is used to create multi-line text fields.

The attribute rows is used to specify the visible number of lines, whereas cols is used to specify the visible width of a text area.

Example: Handling Textarea in Cypress

To demonstrate how to handle textarea in Cypress, I have created a simple HTML form containing a single text area.

<html>
   <body>
       <label for="address">About Yourself:</label>
         <form>
             <textarea rows="10" cols="30" name="address" id="address"></textarea>
         </form>
     </body>
</html>

Here is the output of the following HTML code:

Here is the Cypress implementation for handling the text area (where rows are 10 in number). This means that text will be visible only till 10 rows. Post that, users will have to scroll to see the remaining text.

Shown below is the implementation to enter multiple rows with the use of new line character (i.e. “\n”):

it('Ensure the text limits in text area', () => {
   cy.visit('https://www.automationtestinginsider.com/2019/08/textarea-textarea-element-defines-multi.html')
    .get('textarea')
    .first()
    .clear()
    .type(Row1\n Row2\nRow3\nRow4\nRow5\nRow6\nRow7\nRow8\n
   Row9\nRow10\nRow11\nRow12')
    }
)

Code Walkthrough

The cy.get(‘textarea’) method is used for locating the textarea field. The clear() method in Cypress is used for clearing the input text that might be pre-populated by default.

The type() method in Cypress is used for entering text across rows till Row-12. The text till row 10 is visible in the below screenshot, after which you will have to scroll down to see the content entered in the remaining rows. This is because we have limited the maximum number of rows to 10 for the element.

Dropdown Menu

A dropdown menu provides a list of options that users can select before submitting the form. A common usage of dropdown menus is choosing the appropriate country/state/city from the pre-populated list.

It is created using the element along with the tag which lists the options available for selection. The tag can be used without any attributes; however, a value attribute is a must if you want to send the selected values to the server once the form is submitted.

Example: Handling Dropdown menu in Cypress

Here, I have created a simple downtown list that lets the user select their preferred automobile brand:

<html>
   <form action="/action_page.php">
       <label for="cars">Choose a car:</label>
       <select name="cars" id="cars">
         <option value="volvo">Volvo</option>
         <option value="saab">Saab</option>
         <option value="opel">Opel</option>
         <option value="audi">Audi</option>
       </select>
       <br><br>
       <input type="submit" value="Submit">
     </form>
</html>

Here is the Cypress implementation for handling dropdown menu in Cypress:

describe.only('DropDown selection',()=>{
   it('Select boxes using Visible option',()=>{       
 cy.visit('https://www.automationtestinginsider.com/2019/08/textarea-textarea-element-defines-multi.html')
           .get('select')
           .eq(0)
           .select('Fiat')
           .should('have.value','fiat')
       })

       it('Select boxes using values',()=>{
           cy.visit('https://www.automationtestinginsider.com/2019/08/textarea-textarea-element-defines-multi.html')
           .get('select')
           .eq(0)
           .select('fiat')
           .should('have.value','fiat')
       })
})

Code Walkthrough

The cy.select() method in Cypress is used for handling dropdowns in Cypress. The select(params) method accepts only one parameter. We can use attribute value as well as visible text value as a parameter to find and select the options from the list.

Once the option is selected from the dropdown, should() assert the values using the built-in assertion method ‘have.value’. The method returns the value of the selected option.

Submit and Reset Buttons

Input control of the submit type is used for sending the form data to the server. When the submit button is clicked, the data entered by the user is submitted (or passed) to either retrieve from the database or perform actions on the submitted data.

For instance, when a user enters login details, the username and password combination is sent to the server for verification. Then, the next set of actions are performed depending on the verification status. In scenarios where the user clicks the submit button without filling in the required entries, an error message is displayed on the screen.

On similar lines, reset type of input control is used to clear all the details that the users enter. This is especially useful if the user has to re-enter the details in the form and clean all the respective fields if the submission has to be done freshly.

Submit and Reset are created using the tag, where the attribute value is set to “submit” and “reset,” respectively.

Example: Using Submit button in Cypress

I have created a simple form to demonstrate the handling of submit and reset buttons in Cypress:

<html>
   <form action="https://www.automationtestinginsider.com/">
       First name:<br> <input name="firstname" type="text"><br>
       Last name:<br> <input name="lastname" type="text"><br><br>
       <input type="submit" value="Submit">
       <input type="reset">
     </form>
</html>

Here is the sample output with the submit and reset buttons:

Shown below are the two approaches for handling Submit button in Cypress:

Approach 1: Using ‘Submit Button’ in Cypress

Shown below is the sample demonstration to use Submit button on a form with Cypress:

it('Form submit with cypress submit() method', () => {
   cy.visit('https://opensource- demo.orangehrmlive.com/index.php/auth/login')
        .get('#divUsername')
        .type("Admin")
        .get('#txtPassword').type('admin123')
        .get('form#frmLogin')
        .submit()
        .get('.head')
        .should('be.visible');
})

Code Walkthrough

The code is pretty simple 🙂 After entering the username and password, the submit() method is invoked to submit the contents entered in the form. In order to use the submit() method, we need to identify the form that has to be submitted.

For this, the form ID is used to identify the form and invoke the submit() method to submit the contents entered in the form. To check if the form is submitted successfully or not, the visibility of the dashboard is checked by invoking the should() method. Here, the in-built be.visible parameter is used for confirming the visibility.

Shown below is the execution snapshot:

Approach 2: Using ‘Submit Button’ in Cypress

In this approach, the click() method is used for locating the Submit button using the appropriate locators in Cypress.

it('Form submit with usual click method', () => {
   cy.visit('https://opensource-demo.orangehrmlive.com/index.php/auth/login')
       .get('#divUsername')
       .type("Admin")
       .get('#txtPassword').type('admin123')
       .get('input[type="submit"]')
       .click()
       .get('.head')
       .should('be.visible');
})

Code Walkthrough

Form submission using the click() method is akin to how clicks are performed on any button on a page. However, the major difference between the earlier approach and this one is the methodology used for locating the button using the get(‘input[type=”submit”]’) method.

Once the button is located, the click() method is invoked for performing the requisite actions.

Shown below is the execution snapshot:

Example: Using Reset button in Cypress

Here is the implementation for using Reset buttons in Cypress:

it('Form reset after filling up',()=>{
  cy.visit('https://www.automationtestinginsider.com/2019/08/textarea-textarea-element-defines-multi.html')
   cy.get('input[name="firstname"]')
       .type('Sachin')
       .should('have.value', 'Sachin')
       .get('input[name="lastname"]')
       .type('Joshi')
       .should('have.value', 'Joshi')
       .get('input[type="reset"]')
       .click()
       .get('input[name="firstname"]')
       .should('have.value','')
})

Code Walkthrough

The Reset button is identified using the get(‘input[type=”reset”]’) method. Once the button is identified, a click operation is performed to empty the data entered in the username and password fields.

The should() method is used to by-pass the in-built parameter ‘have.value’ to ‘equal to.’

Take this certification to showcase your expertise with end-to-end testing using Cypress automation framework and stay one step ahead.

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

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 your native app and website on Cloud Mobile Testing Labs on the cloud with real iOS and Android devices. LambdaTest is a convenient, cost-effective and centralized solution for running real-time and Automated tests on real devices cloud.

File Upload Testing in Cypress

Input type file is a special control that lets the user upload documents such as image, pdf, docx, ppt, etc., when submitting the forms. File uploads is a common input type that is primarily used for uploading files like ID proofs, profile photos, etc. File upload control is created with input tags that have attribute value type=file.

Example: File Upload in Cypress

I have created a simple form to demonstrate file upload functionality in Cypress:

<html>
<form method="POST" enctype="multipart/form-data">
   <input id="file-upload" type="file" name="file">
    <br>
   <input class="button" id="file-submit" type="submit"
   value="Upload">
</form> 
</html>

Here is the output snapshot:

Demo: File Upload testing in Cypress

Cypress, by default, does not have built-in functionality to handle file uploads. Instead, the cypress-file-upload package is used for handling file uploads in Cypress.

Here is the series of steps for realizing file upload in Cypress:

Step 1: Installing plugin

We first install the package by invoking the “npm install cypress-file-upload –save” command on the terminal.

Step 2: Import package in command.js

Once the installation is done, add the statement import ‘cypress-file-upload’ in the command.js file. You can locate the command.js file under the support folder within the project.

Step 3: Adding upload file to fixtures

We shall now place the file that we need to upload under the fixtures folder.

Step 4: Cypress implementation to upload files

Here is the sample implementation for uploading files using Cypress:

it('Test file upload with valid file extension', () => {
   const filePath='somefile.txt';
   cy.visit("https://the-internet.herokuapp.com/upload")
   cy.get('#file-upload').attachFile(filePath)
   cy.get('#file-submit').click()
   cy.get('#uploaded-files').contains('somefile')
})

Code Walkthrough

Once you get the locator for file input using .get(‘#file-upload’), I am attaching a file placed under a fixture using attachFile(). A verification process of the uploaded file is done to ensure that the uploaded filename is the same as the original file. The contains() method in Cypress is used for realizing the above requirement.

There is no need to specify the file path, as Cypress automatically looks for the required file under the fixtures folder.

Here is the output snapshot:

Handling Date picker in Form using Cypress

Date Picker is another type of input field using which users can select the required date from the options. Date picker can be created either using or .

<html>
   <form action="/action_page.php">
       <label for="birthday">Birthday:</label>
       <input type="date" id="birthday" name="birthday">
       <input type="submit">
   </form>
</html>

Here is the Cypress implementation for handling date pickers:

it.only('Datepicker in form', ()=>{
   cy.visit('https://demoqa.com/automation-practice-form')
   .get('#dateOfBirth-wrapper')
   .click()
   .get('.react-datepicker__day--013')
   .click()
   .get('#dateOfBirthInput')
   .should('have.value', '13 Nov 2021')
})

Code Walkthrough

First we identify (or locate) the date picker field with .get(‘#dateOfBirth-wrapper’). The fields in the date picker are shown once a click is performed on the picker.

For example, .get(‘.react-datepicker__day–013’) sets the date to “13 Nov 2021”. The date is verified using the .should() method by passing the ‘have.value’ as “13 Nov 2021”.

Here is the execution snapshot:

Using Cypress to fill out Forms and submit them on a Cloud Grid

Now that I have covered the essentials of using Cypress to fill out forms, and submit & reset buttons, let me demonstrate everything using an end-to-end example. Rather than using a local Cypress Grid, I will be running tests on a cloud Cypress Grid like LambdaTest.

LambdaTest 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.

There are umpteen benefits of cloud testing, the major ones being improved scalability, reliability, security, and improved browser coverage. You can refer to the getting started guide to Cypress testing on cloud to understand the benefits of cloud Cypress Grid better.

Debug Android app on the cloud and get instant feedback. Find bugs early on, improve performance, quality, user experience, and save time with instant support on LambdaTest.

Demo: Using Cypress to fill out Forms

I will perform relevant actions on the form placed on the website under test. The implementation shown below covers all the standard input types that can be used when filling up forms:

it('Interacting with checkboxes',()=>{
   cy.visit('https://www.automationtestinginsider.com/2019/08/textarea-textarea-element-defines-multi.html')
     .get('input[value="Checkbox1"]')
     .click()
     .get('input[value="Checkbox2"]')
     .click()
     .get('input[value="Checkbox3"]')
     .click()
     .get('input[value="Checkbox1"]')
     .click()  //this to uncheck first one again
})

describe('Form Submission on LambdaTest', () => {

   it('Fillup details and submit', () => {

       const filePath = 'somefile.txt';

       cy.visit('https://demoqa.com/automation-practice-form')
           .get('#firstName')
           .type('Sachin')
           .get('#lastName').type('Joshi')
           .get('#userEmail').type('test.me@yopmail.com')
           .get('input[value="Male"]').click({force:true})
           .get('#userNumber').type('1231231231')

       cy.get('#dateOfBirth-wrapper')
           .click()
           .get('.react-datepicker__day--013')
           .click()
           .get('#subjectsInput').type('Sports')
           .get('input[type="checkbox"][value="1"]').click({force:true})

       cy.get('#uploadPicture').attachFile(filePath)
       cy.get('#currentAddress').type('This is long text area as you can type \n Multiple \n lines')
       cy.get('#state').type('Rajasthan{enter}')
       cy.get('#city').type('Jaipur')
       cy.get('#submit').click()

   })
})

LambdaTest config helps run tests in parallel against Chrome and Firefox on Windows & macOS platforms. There are a number of benefits of parallel testing with frameworks like Cypress, as it expedites the testing and release processes.

Perform Cypress Parallel Testing on LambdaTest and speed up the testing and release process. Check out how you can test your Cypress test scripts on LambdaTest’s online cloud.

Here is the lambdatest-config.json file that houses the configuration-related information for running Cypress tests at scale on LambdaTest. I have used the LambdaTest Cypress CLI to override the essential configuration from a JSON file.

Cypress CLI lets you manipulate the test configuration file [i.e., lambdatest-config.json] through the command-line interface (CLI), which allows you to override and/or manually modify your tests at any time you want.

`{
  "lambdatest_auth": {
     "username": "<username>",
     "access_key": "<access_key>"
  },
  "browsers": [
     {
        "browser": "Chrome",
        "platform": "Windows 10",
        "versions": [
           "94.0"
        ]
     },

     {
        "browser": "Firefox",
        "platform": "Windows 10",
        "versions": [
           "92.0"
        ]
     },
     {
        "browser": "Chrome",
        "platform": "macOS Big Sur",
        "versions": [
           "94.0"
        ]
     },

     {
        "browser": "Firefox",
        "platform": "macOS Big Sur",
        "versions": [
           "92.0"
        ]
     }
  ],
  "run_settings": {
     "cypress_config_file": "cypress.json",
     "build_name": "Cypress",
     "parallels": 4,
     "specs": "./cypress/integration/practice/formSubmit.spec.js",
     "ignore_files": "",
     "npm_dependencies": {
        "cypress": "8.1.0"
     },
     "feature_file_suppport": false,
     "video":true
  },
  "tunnel_settings": {
     "tunnel": false,
     "tunnelName": null
  }
}

Once we are ready with the tests and configuration file demonstrating cross browser testing with Cypress, we can run the same on the LambdaTest grid by triggering the following command on the terminal:

lambdatest-cypress run

After a successful run, you will see the tests appended on the LambdaTest Grid.

You can navigate to the LambdaTest automation dashboard to check the status of the test. As we have executed the tests parallely on Chrome and Firefox browsers, the same can be seen in the dashboard.

Conclusion

I hope you enjoyed reading the blog on using Cypress to fill out forms and submit them, where we saw different types of form inputs that we come across in most websites and how we can automate the form submission with Cypress. We also covered the cross-browser capability of the Cypress testing tools like LambdaTest, and as evident from screenshots of the LambdaTest dashboard, it took us only 1 minute to execute the form test across four different combinations of firefox and chrome for Windows and macOS systems. Such is the power of cloud-based testing platforms.

Thank you and Happy Learning!!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .