How to interact with Stripe's iframes using WebdriverIO
Tags: testing, tutorial, webdriverio, stripe
Published: 2024-04-27 00:52:38

Many test automation developers will, at some point, come across the challenging task of automating a payment flow for the app that they're testing. This task becomes even more challenging when the integration with the payment services provider in the UI is done via **iframes**, as commonly happens with **Stripe**. In this guide, I will try to make the process of interacting with those iframes a little more straightforward using **JavaScript** and the **WebdriverIO** framework.

## What are iframes?
**Inline frames** or **iframes** can be thought of as little windows within a web page. Developers use them to embed one web page inside another, which allows them to display content from an external source within their own website. Stripe makes use of this concept and provides its own web elements, which are used inside iframes.
Now, while this is very handy for web developers as they can integrate different web elements smoothly, it's also tricky for QA engineers to automate tests that involve working with iframes, because of their encapsulated nature. The automation tool that we choose is important because it needs to be able to **switch the context** to interact with the elements inside the iframe.

## Example
We'll use WebdriverIO to automate the flow to add new credit card information to a web app that integrates with Stripe.

**1. Obtaining the iframe locator:**
![Screenshot of the iframe element locator through the DevTools.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/90xetnrrtxkmb1ibu7dg.jpeg)
We'll have to inspect the input element through the Chrome DevTools and search for the Stripe iframe that contains it. In this case, as we can see, the iframe doesn't have a unique ID, so one way to identify it could be with its _title_ value. The **xpath** will look like this: `'iframe[title="Secure card payment input frame"]'`.

**2. Switching the browsing context to the Stripe iframe:**
Once we have found the correct locator for pointing out to the desired iframe, we have to switch the context from the parent frame (webpage) to the particular one that we want to interact with, in WebdriverIO this can be done like this:
`await browser.switchToFrame(await $('iframe[title="Secure card payment input frame"]'));`
Basically, we create an **Element** object with _await $('locator')_ and pass it as a parameter to the [switchToFrame()](https://webdriver.io/docs/api/webdriver/#switchtoframe) command.

**3. Interacting with the elements inside the iframe:**
Now that we have switched the browsing context to the particular iframe, we can perform some actions to the elements inside of it. Like adding the card number:


![Screenshot of the card number input element locator inside the Stripe iframe through the DevTools.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/en7jbkbzedm83m7fofhs.jpeg)
One possible locator for the Card number input field could be `//input[@name="cardnumber"]`, so we can write something like: 
`await $('//input[@name="cardnumber"]').setValue(cardNumber);`
And the same goes for the rest of the input fields, like the _expiration date_, _CVC_ and _zip code_.
It's important to note that if we don't switch the context to the iframe like we did in **step 2**, this locator `//input[@name="cardnumber"]` will not be recognized by the test-runner, the only way to access it is by previously switching to the specific iframe context.

**4. Switching back to parent frame:**
After we have filled all the necessary values for the credit card info, we'll want to click the _“Save Payment Method”_ button, which is outside of the Stripe iframe, so if we try to run something like: 
`await $('//button[contains(text(),"SAVE PAYMENT METHOD")]').click()` 
it won't work since we're still inside the Stripe iframe context; we first need to switch back to the parent frame context to interact with the elements of the main webpage. For this we can use the [switchToParentFrame()](https://webdriver.io/docs/api/webdriver/#switchtoparentframe) WebdriverIO command:

```
await browser.switchToParentFrame();
await $('//button[contains(text(),"SAVE PAYMENT METHOD")]').click();
```

Finally, our method for adding the credit card info will look something like this:

```
async addNewCreditCard(cardNumber, expirationDate, cvc, zip) {
    await browser.switchToFrame(await $('iframe[title="Secure card payment input frame"]'));
    await $('//input[@name="cardnumber"]').setValue(cardNumber);
    await $('//input[@name="exp-date"]').setValue(expirationDate);
    await $('//input[@name="cvc"]').setValue(cvc);
    await $('//input[@name="zip"]').setValue(zip);
    await browser.switchToParentFrame();
    await $('//button[contains(text(),"SAVE PAYMENT METHOD")]').click();
}

```

And this is how, using WebdriverIO's switchToFrame() and switchToParentFrame() commands, we can interact with the elements inside Stripe's iframes after correctly identifying all the necessary locators.