英文:
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
始终存在,但TOOLBAR
和TOOLBAR 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('url')
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 'url:changed'
, 'window:load'
and windows:before:load
to call the function each time the event triggers, as follows :
<!-- language: lang-js -->
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');
});
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('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");
}
});
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 TRIGGER
is always present, but neither TOOLBAR
nor 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('url:changed')
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('url')
,更确切地说是在页面加载事件触发后。
由于您在异步事件处理程序中执行操作,应在加载后添加对新元素的检查。
it('First_test', function() {
cy.on('url:changed', () => {
inject_stepper_toolbar_HTML()
})
cy.viewport(1280,720)
cy.visit('url')
cy.get('#cypress_recorder_toolbar')
})
但根据url:changed
在cy.visit()
之后如何一致触发,这可能还不够。我认为应该可以,但可能会受到testIsolation
开关之类的影响(启用时,会在测试之间将页面发送回 /about
)。
更确定的方法会更好。
it('First_test', function() {
cy.viewport(1280,720)
cy.visit('url')
cy.then(() => {
inject_stepper_toolbar_HTML()
})
cy.get('#cypress_recorder_toolbar')
})
英文:
The Cypress test runner finishes after the last explicit command in the test script, which in your example is cy.visit('url')
- 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('First_test', function() {
cy.on('url:changed', () => {
inject_stepper_toolbar_HTML()
})
cy.viewport(1280,720)
cy.visit('url')
cy.get('#cypress_recorder_toolbar')
})
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('First_test', function() {
cy.viewport(1280,720)
cy.visit('url')
cy.then(() => {
inject_stepper_toolbar_HTML()
})
cy.get('#cypress_recorder_toolbar')
})
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论