web-dev-qa-db-fra.com

Comment puis-je entrer des données dans une entrée de formulaire dans un iframe à l'aide de cyprès?

J'ai essayé de tester un formulaire de paiement à l'aide de cypress.io

Si quelqu'un a réussi à le faire fonctionner, faites-le moi savoir. J'ai trouvé un fil sur la question ici https://github.com/cypress-io/cypress/issues/136 et sur cette base, j'ai trouvé:

   cy.get('iframe.stripe_checkout_app')
      .wait(10000)
      .then($iframe => {
        const iframe = $iframe.contents()
        const myInput0 = iframe.find('input:eq(0)')
        const myInput1 = iframe.find('input:eq(1)')
        const myInput2 = iframe.find('input:eq(2)')
        const myButton = iframe.find('button')

        cy
          .wrap(myInput0)
          .invoke('val', 4000056655665556)
          .trigger('change')
        cy
          .wrap(myInput1)
          .invoke('val', 112019)
          .trigger('change')

        cy
          .wrap(myInput2)
          .invoke('val', 424)
          .trigger('change')

        cy.wrap(myButton).click({ force: true })
      })

Mais le problème est que la forme de bande n'enregistre toujours pas les valeurs d'entrée. Voici un petit gif de ce qui se passe http://www.giphy.com/gifs/xT0xeEZ8CmCTVMwOU8 . Fondamentalement, le formulaire n'enregistre pas le déclencheur d'entrée de modification.

Quelqu'un sait-il comment saisir des données dans un formulaire dans un iframe en utilisant du cyprès?

11
Josh Pittman

L'extrait suivant devrait fonctionner pour vous. Je l'ai copié/collé à partir du message de @ izaacdb dans ce fil .

cy.wait(5000)
cy.get('.__PrivateStripeElement > iframe').then($element => {

  const $body = $element.contents().find('body')

  let stripe = cy.wrap($body)
  stripe.find('.Input .InputElement').eq(0).click().type('4242424242424242')
  stripe = cy.wrap($body)
  stripe.find('.Input .InputElement').eq(1).click().type('4242')
  stripe = cy.wrap($body)
  stripe.find('.Input .InputElement').eq(2).click().type('424')
})

Cependant, pour que ce qui précède fonctionne, vous devez faire ce qui suit (copié/collé à partir du message de @ nerdmax à partir du même fil lié ci-dessus):

Un grand merci à @Vedelopment @ brian-mann!

J'ai testé avec le composant react-stripe-checkout et cela fonctionne.

Ajoutez simplement des détails sur la solution pour en faire gagner du temps aux autres.

chromeWebSecurity disable:

// cypress.json

{
  "chromeWebSecurity": false
}

--disable-site-isolation-trials:

Vérifiez: https://docs.cypress.io/api/plugins/browser-launch-api.html# AND # 1951

// /plugins/index.js

module.exports = (on, config) => {
  on("before:browser:launch", (browser = {}, args) => {
    if (browser.name === "chrome") {
      args.Push("--disable-site-isolation-trials");
      return args;
    }
  });
};
17
user8888

J'ai juste passé beaucoup trop de temps à essayer de faire fonctionner cela, aucune des réponses que j'ai trouvées ne fonctionnerait complètement. J'ai ajouté ma solution au cyprès problème github pour les iframes (il y a un peu plus de contexte là-bas), en le mettant également ici pour, espérons-le, faire gagner du temps aux autres.

J'ai volé la fonction onIframeReady () de cette réponse stackoverflow .

Fondamentalement, ce qu'il fait est de vérifier si l'iframe a été chargé, si l'iframe a été chargé, il fera $iframe.contents().find("body"); pour passer au contenu. S'il n'a pas été chargé, il raccordera ce même code à l'événement load et s'exécutera dès que l'iframe se chargera.

Ceci est écrit comme une commande personnalisée pour autoriser l'utilisation du chaînage de cyprès après le passage à l'iframe, alors mettez ce qui suit dans votre fichier support/commands.js:

Cypress.Commands.add("iframe", { prevSubject: "element" }, $iframe => {
  Cypress.log({
    name: "iframe",
    consoleProps() {
      return {
        iframe: $iframe,
      };
    },
  });

  return new Cypress.Promise(resolve => {
    onIframeReady(
      $iframe,
      () => {
        resolve($iframe.contents().find("body"));
      },
      () => {
        $iframe.on("load", () => {
          resolve($iframe.contents().find("body"));
        });
      }
    );
  });
});

function onIframeReady($iframe, successFn, errorFn) {
  try {
    const iCon = $iframe.first()[0].contentWindow,
      bl = "about:blank",
      compl = "complete";
    const callCallback = () => {
      try {
        const $con = $iframe.contents();
        if ($con.length === 0) {
          // https://git.io/vV8yU
          throw new Error("iframe inaccessible");
        }
        successFn($con);
      } catch (e) {
        // accessing contents failed
        errorFn();
      }
    };

    const observeOnload = () => {
      $iframe.on("load.jqueryMark", () => {
        try {
          const src = $iframe.attr("src").trim(),
            href = iCon.location.href;
          if (href !== bl || src === bl || src === "") {
            $iframe.off("load.jqueryMark");
            callCallback();
          }
        } catch (e) {
          errorFn();
        }
      });
    };
    if (iCon.document.readyState === compl) {
      const src = $iframe.attr("src").trim(),
        href = iCon.location.href;
      if (href === bl && src !== bl && src !== "") {
        observeOnload();
      } else {
        callCallback();
      }
    } else {
      observeOnload();
    }
  } catch (e) {
    // accessing contentWindow failed
    errorFn();
  }
}

Ensuite, vous appelleriez cela ainsi à partir de vos tests:

cy.get('iframe.stripe_checkout_app')
  .iframe()
  .find('input:eq(0)')
  .type("4000056655665556")

Vous pouvez . Alias ​​() après avoir appelé .iframe() pour vous y référer pour le reste de vos entrées ou .get() l'iframe plusieurs fois, je laisse cela à vous de comprendre.

2
Brendan

Le flux de travail iframe est toujours assez maladroit (jusqu'à ce que cette fonctionnalité soit implémentée). Pour l'instant, vous pouvez essayer de forcer à peu près toutes les interactions DOM:

cy.visit("https://jsfiddle.net/1w9jpnxo/1/");
cy.get("iframe").then( $iframe => {

    const $doc = $iframe.contents();
    cy.wrap( $doc.find("#input") ).type( "test", { force: true });
    cy.wrap( $doc.find("#submit") ).click({ force: true });
});
2
dwelle

Le '.Input .InputElement' le sélecteur de @ user8888 n'a pas fonctionné pour moi. Au lieu de cela, j'accède à chaque input par son attribut name.

        cy.get(".__PrivateStripeElement > iframe").then(($element) => {
                const $body = $element.contents().find("body");

                let stripe = cy.wrap($body);
                stripe
                    .find('[name="cardnumber"]')
                    .click()
                    .type(MOCK_CC_NUMBER);

                stripe = cy.wrap($body);
                stripe
                    .find('[name="exp-date"]')
                    .click()
                    .type(MOCK_CC_EXP);

                stripe = cy.wrap($body);
                stripe
                    .find('[name="cvc"]')
                    .click()
                    .type(MOCK_CC_CVC);

                stripe = cy.wrap($body);
                stripe
                    .find('[name="postal"]')
                    .click()
                    .type(MOCK_CC_Zip);
            });
1
theUtherSide

La solution dans ce lien fonctionne pour moi. Fondamentalement, les étapes sont les suivantes:

  1. Définissez chromeWebSecurity sur false dans cypress.json
  2. Ajoutez la commande cypress pour obtenir l'iframe dans command.js
  3. Utilisez la commande iframe dans le script
1
Juniada

Cela ne directement répond pas à votre question, mais après plusieurs jours à essayer de manipuler des éléments de manipulation dans l'iframe à l'aide de jQuery, à réimplémenter un tas de choses que Cypress a déjà fait, je me suis giflé et j'ai commencé à faire ce:

Cypress.Commands.add('openiframe', () => {
    return cy.get("iframe[src^='/']").then(iframe => {
        cy.visit(Cypress.$(iframe).attr('src'), { timeout: Cypress.config("pageLoadTimeout") });
    });
});

Cela m'a permis de cy.openiframe (). Then (() => {}); et procédez comme si le site que je testais n'avait pas mis un tas de fonctionnalités dans un iframe en premier lieu.

L'inconvénient est que vous devez terminer ce que vous faites pas dans l'iframe avant de faire quoi que ce soit dans l'iframe, donc vous ne pouvez pas aller et venir trop facilement.

Cela peut ne pas fonctionner pour votre cas d'utilisation, mais si/quand c'est le cas, c'est la solution de contournement la plus simple que j'ai trouvée.

1
DrShaffopolis