Выбор дочерних элементов в родительском без постоянного повторного выбора родительского элемента

У меня есть этот код, который выбирает дочерний элемент родительского элемента:

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 перед его оберткой и последующим утверждением, но мне просто интересно, есть ли более элегантный способ сделать это.

Я настоятельно рекомендую следовать парадигме Page Object Мартина Фаулера. Если вы это сделаете, это поможет вам решить эту проблему и сделает ваш тестовый код намного менее хрупким при внесении изменений в пользовательский интерфейс.

bob 20.05.2024 07:03

@bob Cypress на самом деле предлагает вам не использовать объекты страницы -> cypress.io/blog/2019/01/03/…

agoff 20.05.2024 15:29

Мне нужно будет прочитать это более внимательно, когда у меня будет время. Похоже, Cypress рекомендует вызывать API тестируемого кода, чтобы установить состояние приложения, чтобы вы могли [манипулировать пользовательским интерфейсом несколькими конкретными способами, а затем] делать утверждения. Это верно? Если да, то это кажется мне неправильным (я не доверяю тестируемому коду, если делаю E2E; в этом вся идея), но я думаю, когда в Риме… может быть…?

bob 20.05.2024 19:35
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
3
88
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Используйте .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();
})

См. Переменные и псевдонимы — Вы не можете назначать возвращаемые значения или работать с ними.

Daniel.D.Abraham 20.05.2024 09:27

Итак, addUserForm.find() использует jQuery, у которого нет повторных попыток.

Minchin 20.05.2024 21:54
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()?

Daniel.D.Abraham 20.05.2024 09:25

Ответ Д.Фагена имеет наибольший смысл и, скорее всего, именно это я и сделал бы, учитывая, что все ваши команды идут одна за другой. Однако, если ваши команды не были структурированы таким образом, а вместо этого имели другие команды, запросы которых вы не хотели начинать с родительского элемента формы, вы можете сохранить родительский элемент как псевдоним, и ссылаться на него немного проще.

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')
  ...
})

Единственный случай, когда это не сработает, — это когда один тест вызывает перезапуск тестового окна, обычно при переключении доменов с одного теста на другой, например, когда вам нужно войти в систему через стороннюю службу.

Другие вопросы по теме

Похожие вопросы