Я пытаюсь взаимодействовать с некоторыми элементами внутри iframe с помощью кипариса. Если я использую подход в https://bparkerproductions.com/how-to-interact-with-iframes-using-cypress-io/ только для одного элемента на тест, то все нормально.
# commands.js
Cypress.Commands.add(
'iframe',
{ prevSubject: 'element' },
($iframe) => {
return new Cypress.Promise(resolve => {
$iframe.on('load', () => {
resolve($iframe.contents().find('body'))
})
})
})
# landing_page.spec.js
cy.get('iframe').iframe().find('#happybutton').should('be.visible')
Однако я хочу найти несколько элементов, щелкнуть по ним и проверить, правильно ли они отображаются, но если я назначу содержимое iframe переменной и повторно использую его для поиска другого элемента (например, кнопки), кипарис попытается найти второй элемент (например, меню) из первого элемента (кнопки, которая обречена на провал, потому что кнопка не содержит меню).
# landing_page.spec.js
let iframeContent = cy.get('iframe').iframe()
iframeContent.find('#happybutton').should('be.visible')
iframeContent.find('#myMenu').should('be.visible')
Я пытался использовать разные переменные или вызывать напрямую cy.get('iframe').iframe()
каждый раз, когда хотел взаимодействовать с разными элементами, но кипарис попадает в бесконечный цикл, и тест никогда не заканчивается (но никаких ошибок или предупреждений не выдается).
Кто-нибудь знает способ избежать этого бесконечного цикла? Поскольку я хочу воспроизвести последовательность шагов для создания тестового примера, невозможно изолировать каждое взаимодействие в отдельном тесте.
Или кто-нибудь знает фреймворк, который больше подходит для работы с фреймами?
Проблема в том, что $iframe.on('load',
срабатывает только один раз, поэтому вы не можете вызвать cy.get('iframe').iframe()
дважды, что фактически делают обе команды .find()
.
let iframeContent = cy.get('iframe').iframe()
не хранит тело iframe, он хранит «цепочку», которая обрабатывается как функция или геттер.
«Бесконечный цикл» — это Cypress, ожидающий вызова promise resolve() во второй раз, чего никогда не происходит.
Таким образом, вы можете вкладывать такие команды
cy.get('iframe').iframe().then(body => {
cy.wrap(body).find('#happybutton').should('be.visible')
cy.wrap(body).find('#myMenu').should('be.visible')
});
или вы можете улучшить команду, добавив тег, когда срабатывает событие загрузки
Cypress.Commands.add('iframe', { prevSubject: 'element' }, ($iframe) => {
return $iframe._hasloaded
? $iframe.contents().find('body')
: new Cypress.Promise(resolve => {
$iframe.on('load', () => {
$iframe._hasloaded = true;
resolve($iframe.contents().find('body'))
})
})
})
Благодаря ответу Марион я нашел способ реорганизовать свой код, так что теперь он работает!
Примечание: функция iframe()
осталась нетронутой.
# commands.js
Cypress.Commands.add(
'iframe',
{ prevSubject: 'element' },
($iframe) => {
return new Cypress.Promise(resolve => {
$iframe.on('load', () => {
resolve($iframe.contents().find('body'))
})
})
})
# landing_page.spec.js
cy.get('iframe').iframe().as('iframeContent')
cy.get('@iframeContent').then((iframeContent) => {
cy.get(iframeContent).find('#happybutton').click()
cy.get(iframeContent).find('#myMenu')
cy.get(iframeContent).find('#anotherElement').should('be.visible')
})
Хорошо, это работает, но когда .as('iframeContent')
следует непосредственно за cy.get('@iframeContent')
, это просто утомляет кончики пальцев.
Приведенные выше ответы указали мне правильное направление. Опустив фразу «тогда» и первый cy.get('@iframeContent'), решение Semiramis можно немного упростить и сделать более понятным следующим образом:
cy.get('iframe').iframe().as('iframeContent')
cy.get('@iframeContent').find('#happybutton').click()
cy.get('@iframeContent').find('#myMenu')
cy.get('@iframeContent').find('#anotherElement').should('be.visible')
Для новичков Cypress (таких как я): Переменные и псевдонимы Cypress
Взгляните на cypress-iframe, который использует вложение для запуска нескольких команд.