У меня есть этот код, который выбирает дочерний элемент родительского элемента:
cy.get('form[data-cy = "user_add"]').find('[name = "username"]').type('test');
Это работает нормально и так, но это становится проблемой, когда мне нужно постоянно выбирать родительский элемент, чтобы найти дочерний элемент, который я хочу ввести/изменить.
cy.get('form[data-cy = "manage_users_addform"]').find('[name = "username"]').type('testusername');
cy.get('form[data-cy = "manage_users_addform"]').find('[name = "password"]').type('testpassword');
cy.get('form[data-cy = "manage_users_addform"]').find('[name = "repeatpassword"]').type('testpassword');
cy.get('form[data-cy = "manage_users_addform"]').find('[name = "firstname"]').type('testfname');
cy.get('form[data-cy = "manage_users_addform"]').find('[name = "lastname"]').type('testlname');
cy.get('form[data-cy = "manage_users_addform"]').find('[name = "per_station"]').click();
cy.get('form[data-cy = "manage_users_addform"]').find('[name = "per_cashier"]').click();
cy.get('form[data-cy = "manage_users_addform"]').find('[name = "per_manage"]').click();
Есть предложения, как не повторять постоянно выбор формы? В документации Cypress говорится, что переменные не следует использовать как можно чаще из-за их асинхронности по умолчанию. Я также нашел некоторую документацию по выходу из этой асинхронной природы и простому написанию кода JavaScript перед его оберткой и последующим утверждением, но мне просто интересно, есть ли более элегантный способ сделать это.
@bob Cypress на самом деле предлагает вам не использовать объекты страницы -> cypress.io/blog/2019/01/03/…
Мне нужно будет прочитать это более внимательно, когда у меня будет время. Похоже, Cypress рекомендует вызывать API тестируемого кода, чтобы установить состояние приложения, чтобы вы могли [манипулировать пользовательским интерфейсом несколькими конкретными способами, а затем] делать утверждения. Это верно? Если да, то это кажется мне неправильным (я не доверяю тестируемому коду, если делаю E2E; в этом вся идея), но я думаю, когда в Риме… может быть…?





Используйте .within(), чтобы изменить точку начала запроса (обычно это <body>).
cy.get('form[data-cy = "manage_users_addform"]').within(($form) => {
// $form is now the starting point for queries
cy.get('[name = "username"]').type(...)
cy.get('[name = "password"]').type(...)
...
})
сохранить родителя в некоторой константе и использовать его
cy.get('form[data-cy = "manage_users_addform"]').then(($addUserForm) => {
addUserForm.find('[name = "username"]').type('testusername');
addUserForm.find('[name = "password"]').type('testpassword');
addUserForm.find('[name = "repeatpassword"]').type('testpassword');
addUserForm.find('[name = "firstname"]').type('testfname');
addUserForm.find('[name = "lastname"]').type('testlname');
addUserForm.find('[name = "per_station"]').click();
addUserForm.find('[name = "per_cashier"]').click();
addUserForm.find('[name = "per_manage"]').click();
})
См. Переменные и псевдонимы — Вы не можете назначать возвращаемые значения или работать с ними.
Итак, addUserForm.find() использует jQuery, у которого нет повторных попыток.
you can try the then condition also.
cy.get('form[data-cy = "user_add"]').then(()=> {
cy.get('[name = "username"]').type('testusername');
cy.get('[name = "password"]').type('testpassword');
})
if the parent element contains more then 2
cy.get('form[data-cy = "user_add"]').first().within(() => {
cy.get('[name = "username"]').type('testusername');
cy.get('[name = "password"]').type('testpassword');
})
Зачем вам использовать .then()?
Ответ Д.Фагена имеет наибольший смысл и, скорее всего, именно это я и сделал бы, учитывая, что все ваши команды идут одна за другой. Однако, если ваши команды не были структурированы таким образом, а вместо этого имели другие команды, запросы которых вы не хотели начинать с родительского элемента формы, вы можете сохранить родительский элемент как псевдоним, и ссылаться на него немного проще.
cy.get('form[data-cy = "manage_users_addform"]').as('form');
// other commands
cy.get('@form').find('[name = "username"]').type('testusername');
Ответ Агоффа технически верен, но он не работает, если вы хотите использовать псевдоним в тестах, потому что, если вы не отключите изоляцию тестов, вы не сможете использовать значение псевдонима во втором тесте.
Использование переменной для хранения селектора родительского запроса будет работать с несколькими тестами.
const formSelector = 'form[data-cy = "manage_users_addform"]'
it('tests the form when logged-in', () => {
cy.get(formSelector).find('[name = "username"]').type('testusername')
...
})
it('tests the form when not logged in', () => {
cy.get(formSelector).find('[name = "username"]').type('testusername')
...
})
Или с помощью шаблонов для внедрения родителя,
const formSelector = 'form[data-cy = "manage_users_addform"]'
it('tests the form when logged-in', () => {
cy.get(`${formSelector} [name = "username"]`).type('testusername')
...
})
it('tests the form when not logged in', () => {
cy.get(`${formSelector} [name = "username"]`).type('testusername')
...
})
Единственный случай, когда это не сработает, — это когда один тест вызывает перезапуск тестового окна, обычно при переключении доменов с одного теста на другой, например, когда вам нужно войти в систему через стороннюю службу.
Я настоятельно рекомендую следовать парадигме Page Object Мартина Фаулера. Если вы это сделаете, это поможет вам решить эту проблему и сделает ваш тестовый код намного менее хрупким при внесении изменений в пользовательский интерфейс.