在每次加载页面时自动注入HTML/CSS元素,而无需手动调用Cypress命令。

huangapple go评论76阅读模式
英文:

Injecting HTML/CSS elements within cypress tests each time a page is loaded without manually invoking cypress command

问题

你好,提前感谢您的答复。

所以我目前正在尝试做一些我知道Cypress不是为之而制作的事情。我使用Cypress Studio,这是Cypress的实验性功能,允许用户在不编写任何代码的情况下在给定的站点上生成测试。长话短说,一个Python脚本首先创建Cypress E2E脚本,并要求用户提供目标站点的URL(只需使用cy.visit('url')命令),因此E2E脚本将自动导航到所需的站点,然后用户可以使用Cypress Studio录制测试。

这个Python脚本还写入一个调用JS函数的命令,该函数获取站点的HTML文档并在其前面添加一个简单的div,其中包含一个按钮。

我需要这个div保持在Cypress测试界面上,无论用户在使用Cypress Studio时执行了什么操作,因为他们在录制过程中应该与其进行交互。

因此,主要问题是处理在浏览站点时发生的重定向,因为每次重新加载页面时,div都会消失(这并不奇怪)。

为了实现这样的目标,我尝试使用Cypress提供的cy.on(...)功能(https://docs.cypress.io/api/cypress-api/catalog-of-events),使用事件'url:changed''window:load'windows:before:load,每次事件触发时调用该函数,如下所示:

import '../support/redefine'
import '../support/commands'
import { inject_stepper_toolbar_HTML } from '../support/commands';

it('First_test', function() {
    cy.on('url:changed', () => {
        inject_stepper_toolbar_HTML();
    })

    cy.viewport(1280,720)
    cy.visit('url');
});

该函数使用Cypress的cy.document().then()类似的promise功能,如下所示:

function inject_stepper_toolbar_HTML() {
  console.log('IN TRIGGER'); 
  return cy.document().then((doc) => {
      if(!doc.getElementById("cypress_recorder_toolbar")){
          console.log("TOOLBAR");

          //cy.load_recorder_styles();
          let $toolbar_div = doc.createElement('div');
          $toolbar_div.setAttribute('id', "cypress_recorder_toolbar");
          $toolbar_div.setAttribute('class', "cypress_recorder_toolbar");

          let $stepper_button = doc.createElement('button');
          $stepper_button.setAttribute('id', "cypress_recorder_stepper_button_start");
          $stepper_button.setAttribute('class', "cypress_recorder_stepper_button_class" );
          $stepper_button.innerHTML= "Start Step";

          $toolbar_div.innerHTML = '<div class="inner_recorder_div"></div>';

          $toolbar_div.appendChild($stepper_button);
          doc.body.prepend($toolbar_div);
          console.log("TOOLBAR HAS BEEN ADDED");
          //cy.inject_button_swapper();
      }
      else{
          console.log("TOOLBAR ALREADY HERE");
      }
  });

这是它的效果(也是预期的效果,维基百科的页面上有一个按钮),例如,在上面的脚本中启动Cypress时。但是,在浏览维基百科时,div有时会出现,有时会消失(大多数情况下不存在,但偶尔会出现)。

我正在努力理解的问题是为什么,事件会触发,但then()语句内的代码似乎以任何方式都不执行。日志IN TRIGGER始终存在,但TOOLBARTOOLBAR ALREADY HERE都不会出现。

我猜想这与Cypress管理then()的方式有关,因为我记得读过这是类似于promise但不是真正的promise。

您有没有想法如何解决这个问题?

一些说明:

  • 我不能将按钮添加到我想要处理的站点上。
  • 我尝试了inject_stepper_toolbar_HTML函数的Cypress命令版本,以及不使用return的版本,因为据说Cypress的then()不需要返回值。
  • 由于我猜测问题出在在on('url:changed')事件中执行的代码被强制异步执行的方式,我尝试覆盖Cypress的click()指令以使其默认等待。当然,这不适用于Cypress Studio中执行的内容,所以这里没有什么可看的。

再次感谢您的回复。

英文:

Hello and thanks in advance for your answers.

So I am currently trying to do something that I am aware cypress is not really made for to begin with.
I use Cypress Studio, the experimental feature of Cypress, to let a user generate a test on a given site which is not always the same without writing any line of code. Long story short, a Python script creates the cypress E2E script in the first place and asks for the target site URL to the user (simply using the cy.visit(&#39;url&#39;) command), so the E2E script will automatically lead to the desired site and the user can then use Cypress Studio to record the test.

This Python script also writes a call of a JS function that gets the site's HTML document and prepends a simple div containing a button.

I need this div to remain on the cypress testing interface whatever action is made by the user while they use Cypress Studio, as they are supposed to interact with it during the recording.

So, the main issue is to deal with the redirections that happen while browsing the site, as each time the page is reloaded the div disappears (which is not surprising).

So, to achieve such a thing, I tried to use the cy.on(...) feature proposed by Cypress (https://docs.cypress.io/api/cypress-api/catalog-of-events), with the events &#39;url:changed&#39;, &#39;window:load&#39; and windows:before:load to call the function each time the event triggers, as follows :

<!-- language: lang-js -->


import &#39;../support/redefine&#39;
import &#39;../support/commands&#39;
import { inject_stepper_toolbar_HTML } from &#39;../support/commands&#39;;

it(&#39;First_test&#39;, function() {
    cy.on(&#39;url:changed&#39;, () =&gt; {
        inject_stepper_toolbar_HTML();
    })

    cy.viewport(1280,720)
    cy.visit(&#39;url&#39;);
});

The function uses the cy.document().then() promise-like feature of Cypress, and goes as follows :

<!-- language: lang-js -->


function inject_stepper_toolbar_HTML() {
  console.log(&#39;IN TRIGGER&#39;); 
  return cy.document().then((doc) =&gt; {
      if(!doc.getElementById(&quot;cypress_recorder_toolbar&quot;)){
          console.log(&quot;TOOLBAR&quot;);

          //cy.load_recorder_styles();
          let $toolbar_div = doc.createElement(&#39;div&#39;);
          $toolbar_div.setAttribute(&#39;id&#39;, &quot;cypress_recorder_toolbar&quot;);
          $toolbar_div.setAttribute(&#39;class&#39;, &quot;cypress_recorder_toolbar&quot;);

          let $stepper_button = doc.createElement(&#39;button&#39;);
          $stepper_button.setAttribute(&#39;id&#39;, &quot;cypress_recorder_stepper_button_start&quot;);
          $stepper_button.setAttribute(&#39;class&#39;, &quot;cypress_recorder_stepper_button_class&quot; );
          $stepper_button.innerHTML= &quot;Start Step&quot;;

          $toolbar_div.innerHTML = &#39;&lt;div class=&quot;inner_recorder_div&quot;&gt;&lt;/div&gt;&#39;;

          $toolbar_div.appendChild($stepper_button);
          doc.body.prepend($toolbar_div);
          console.log(&quot;TOOLBAR HAS BEEN ADDED&quot;);
          //cy.inject_button_swapper();
      }
      else{
          console.log(&quot;TOOLBAR ALREADY HERE&quot;);
      }
  });

This is what it gives (and what is expected, Wikipedia with a button prepended to the top of the page), taking for example wikipedia, when launching Cypress with the above script. BUT, when navigating through Wikipedia, the div will sometimes appear and sometimes disappear (it is absent most of the time but comes back occasionally).

What I am struggling to understand is why, the event triggers but the code within the then()statement doesn't seem to be executed in any way. The log IN TRIGGERis always present, but neither TOOLBARnor TOOLBAR ALREADY HERE are.

I guess this has something to do with the way Cypress manages the .then() as I recall reading that this was promise-like but not a real promise.

Any idea how I would be able to deal with this issue ?

Some precisions :

I cannot add the button to the site I want to work on

I tried Cypress command version of the inject_stepper_toolbar_HTML function, along with a version that doesn't use the return since the Cypress .then() is supposed not to need one

Since my guess is that the issue comes from the forced asynchronous nature of the code executed within the on(&#39;url:changed&#39;) event, I tried overwriting the .click() instruction of Cypress to make it wait by default. Of course this doesn't apply to what is performed within Cypress Studio, so nothing to see here.

Thanks again for your replies.

答案1

得分: 3

Cypress测试运行器在测试脚本中的最后一个显式命令之后结束,例如您的示例中的 cy.visit(&#39;url&#39;),更确切地说是在页面加载事件触发后。

由于您在异步事件处理程序中执行操作,应在加载后添加对新元素的检查。

it(&#39;First_test&#39;, function() {
  cy.on(&#39;url:changed&#39;, () =&gt; {
    inject_stepper_toolbar_HTML()
  })

  cy.viewport(1280,720)
  cy.visit(&#39;url&#39;)

  cy.get(&#39;#cypress_recorder_toolbar&#39;)
})

但根据url:changedcy.visit()之后如何一致触发,这可能还不够。我认为应该可以,但可能会受到testIsolation开关之类的影响(启用时,会在测试之间将页面发送回 /about)。

更确定的方法会更好。

it(&#39;First_test&#39;, function() {

  cy.viewport(1280,720)
  cy.visit(&#39;url&#39;)

  cy.then(() =&gt; {
    inject_stepper_toolbar_HTML()
  })

  cy.get(&#39;#cypress_recorder_toolbar&#39;)
})
英文:

The Cypress test runner finishes after the last explicit command in the test script, which in your example is cy.visit(&#39;url&#39;) - or more precisely after the page-load event has fired.

Since you are taking action in an asynchronous event handler, you should add a check for the new elements after loading.

it(&#39;First_test&#39;, function() {
  cy.on(&#39;url:changed&#39;, () =&gt; {
    inject_stepper_toolbar_HTML()
  })

  cy.viewport(1280,720)
  cy.visit(&#39;url&#39;)

  cy.get(&#39;#cypress_recorder_toolbar&#39;)
})

That still might not cut it, depending on how consistently url:changed is fired after cy.visit(). I think it would be ok, but may be affected by things like the testIsolation switch (when on, this will send the page back to /about in between tests).

A more deterministic approach would be better

it(&#39;First_test&#39;, function() {

  cy.viewport(1280,720)
  cy.visit(&#39;url&#39;)

  cy.then(() =&gt; {
    inject_stepper_toolbar_HTML()
  })

  cy.get(&#39;#cypress_recorder_toolbar&#39;)
})

huangapple
  • 本文由 发表于 2023年7月13日 19:35:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76678928.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定