E2E 테스트의 내용 - 부드러운 스크롤, 전자 표지 및 Cypress 클릭

Cypress는 매우 유행하는 단말기부터 단말기까지의 테스트 도구다.그것은 용도가 광범위하여 일반적으로 설치하고 사용하기 쉽다.자바스크립트로 테스트를 작성하는 것은 상당히 직관적이고 따르는 문법은 JQuery를 떠올리게 한다.
몇 가지 이런 공구가 있다.셀렌은 아마도 가장 오래된 것일 것이다. 2004년에 발표되었다.그것들의 작업 방식은 브라우저를 실행하고 위에서 사용자의 입력을 시뮬레이션하는 것이다.이것은 상당히 간단하게 들리지만, Cypress, Selenium, 또는 다른 e2e 달리기 선수와 합작한 사람은 모두 너에게 이것은 분명히 조금도 간단하지 않다고 말할 것이다.
나의 경험에서, 이 프로그램들은 항상 좀 크고 복잡하며, 행동이 이상하다. 왜냐하면 그들은 브라우저 API의 혼란스러운 개발 우호적인 전단일 뿐이기 때문이다.wait 문장은 항상 규범을 괴롭히기 시작하고DOM 먼지가 가라앉기를 기다린 다음에 다음 클릭을 한다.
내가 최근에 EcosiaCypress와 진행한 전투는 우리의 간단한 단편 룰렛을 테스트하는 것을 포함한다.

테스트 장면


나는 상당히 간단한 테스트 장면을 실시하기 시작했다.
- The mock data has 7 items (the UI shows 3);
- The last item is not visible initially;
- If I click "next", the first item should no longer be visible;
- If I then click "next" 3 more times, the last item should come into view;
- If I then click "previous", the last item should no longer be visible;
- If I then click "previous" 3 more times, the first item should come into view;

우선, 나는 더욱 간단한 테스트 장면 버전을 짰다.'다음'을 네 번 클릭한 다음에 첫 번째 항목이 보이지 않는지, 마지막 항목이 보이지 않는지 검사했다.
...
.get('.snippet-item')
.scrollIntoView()
.should('have.length', 7);

.get('.carousel-nav-button-next')
.click().click().click().click().click()

.get('.news-snippet-item').first()
.should('not.be.visible')
.get('.news-snippet-item').last()
.should('be.visible');
나는 자신의 능력에 대해 자신감이 충만해서 이번 테스트를 진행했지만 테스트에 실패했다.Cypress의 GUI를 로드할 때 클릭 이벤트가 트리거되고 있지만 아무 일도 일어나지 않았습니다.
그리고 나서 나는 갑자기 우리의 매끄러운 굴림이 일으킨 것일지도 모른다는 것을 생각했다.우리는 자바스크립트에서 scrollIntoView 와 이 회전목마의 옵션 behavior: smooth 을 사용합니다.Cypress는 원소가 클릭할 수 있는 것을 기다린 다음에 다른 클릭을 촉발해야 하는데, 나는 이 프레임워크의 행동이 확실하지 않다는 것을 발견하기 시작했다.
부드러운 스크롤을 사용하지 않습니다. 이벤트를 누르면 올바르게 터치할 것 같습니다.그런데 제가 어떻게 Cypress에만 부드러운 스크롤을 비활성화할 수 있습니까?

Cypress에만 부드러운 스크롤 비활성화


원래는 측백나무를 쉽게 발견할 수 있었다.런타임 전역window.Cypress에서 확인할 수 있는 항목은 다음과 같습니다.
const scrollOptions: {
    behavior: (typeof window === 'undefined' || window.Cypress) ? 'auto' : 'smooth',
}
이것은 실행할 수 있지만 결코 이상적이지는 않다.우리는 응용 프로그램 코드에 e2e 테스트 프레임워크와 관련된 코드를 포함시켜서는 안 된다.나의 다음 생각은 어떤 브라우저 로고를 사용해서 부드러운 스크롤을 사용하지 않는 것이다.

부드러운 스크롤을 비활성화할 브라우저 플래그가 없습니다.


모든 현대 브라우저"reduced motion preference"에는 보조 기능이 있다.이 기본 설정은 브라우저 크롬의 여러 애니메이션에 영향을 줍니다.너는 할 수 있다(당연하지!)응용 프로그램의 애니메이션 수를 줄이거나 음량을 줄일 수도 있다.그러나 그 자체로 부드러운 스크롤을 사용하지 않습니다.
CSS 또는 Javascript에서 미디어 쿼리를 사용하여 이 기능이 활성화되었는지 확인할 수 있습니다.
const prefersReducedMotion = typeof window === 'undefined' ? true :
    window.matchMedia('(prefers-reduced-motion: reduce)').matches;

const scrollOptions =  {
    behavior: prefersReducedMotion ? 'auto' : 'smooth',
};
또한 Firefox와 Chrome는 로고를 전달하여'저운동을 더 좋아한다'모드로 시작할 수 있다.Cypress를 사용하여 해당 깃발browser launch API을 전달할 수 있습니다.
on('before:browser:launch', (browser = {}, launchOptions) => {
  const REDUCE = 1;
  if (browser.family === 'firefox') {
    launchOptions.preferences['ui.prefersReducedMotion'] = REDUCE;
  }
  if (browser.family === 'chromium') {
    launchOptions.args.push('--force-prefers-reduced-motion');
  }
  return launchOptions;
});
Cypress의 GUI에서 이를 테스트하고 부드러운 스크롤이 유효하게 비활성화되었는지 확인했습니다.나는 내 능력에 대한 신뢰를 회복했다.나는 터널 끝의 빛을 볼 수 있다!

Electron은 이를 지원하지 않습니다.


Cypress는 기본적으로 Chrome이나 Firefox를 사용하지 않습니다.CI에 포함된 브라우저는 Electron입니다."그런데 전자는 크롬일 뿐이야."라고 나는 네가 말하는 것을 들었다.이것은 단지 부분적으로 정확할 뿐이다.Electron은 모든 기능과 API가 크롬처럼 공개되는 것은 아닌 패키지다.
Cypressbrowser launch API docs에 따르면'prefers reduced flag'은 내가 Electron에 전달할 수 있는 로고와 기본 설정 목록의 일부분이 아니다.
읽기some helpful github discussions를 통해 나는 마침내 일부 추가 표지가'응용 프로그램 스위치'를 통해 Electron에 전달될 수 있다는 것을 발견했다.이 스위치에 대해 설명했다further down in the docs.그러므로 나는 내가 원하는 로고를 사용해서 package.json 스크립트에서 환경 변수를 cypress에 전달하려고 한다.
{
  "scripts": {
    "test:e2e": "ELECTRON_EXTRA_LAUNCH_ARGS=\"--force-prefers-reduced-motion\" cypress run --project ./e2e-tests"
  }
}
이거 성공했어!내가 바라는 만큼 우아하지는 않았지만 해냈어.env VAR 대신 코드에서 이 전환을 활성화할 수 있는 방법이 있으면 알려 주십시오.

부드럽게 스크롤되지 않은 상태에서 테스트 실행


이제부터 내 테스트를 실시하는 것은 순조롭게 진행될 것이다.스크롤하지 않고 Cypress의 GUI에 제대로 등록하려면 클릭하십시오.
나는 헤더 없는 브라우저에서 이 테스트를 실행했는데, 그것은 성공했다.만세!아, 잠깐만요. 추가click()가 틀렸어요.나 진짜 바보야.나는 추가적인 click() 를 포기하고 여전히 자신의 심지 능력을 확신한다.그러나 분명히 알고 있는 바와 같이, 당신은 여전히 70퍼센트만이 이 글을 통과했기 때문에, 이야기는 여기서 끝나지 않았다.테스트에 실패했습니다.

브라우저 API를 혼동하는 프런트엔드


모든 개발자들은 때때로 자신이 알고 있는 모든 것을 의심할 수 있다.그래서 나는 로컬 회전 프로그램에서 손가락 클릭 횟수를 계산하는 동시에'다음'단추를 반복해서 눌렀다.그리고 나는 손가락을 세어 보았는데 모두 네 개의 손가락이 있었다.그래서 내가 아직 이성을 잃지 않았다는 걸 확인했어.
나는 클릭하기 전에 하나.wait(500)를 추가하려고 했지만 효과가 없었다.그래서 나는 인터넷에 접속했다.
나는 stack overflow thread 사람들이 그곳에서 이상한 건의를 하는 것을 발견했다.하나는 클릭할 때마다 추가.trigger('mouseover')(!)됩니다.또 하나는 실패.click()로 교체.click().click()(거기서 해냈다).그러나 가장 중요한 답안은 사용을 권장한다.click({ force: true }).
무력 사용이 효과를 보았다.다음날 내가 돌아올 때까지 그것은 다시는 작용하지 않았다.나는 왜 그것이 일을 할 수 있는지도, 왜 그것이 멈췄는지 말할 수도 없지만, 그것은 성공했고, 그리고 없어졌다. 나는 그것이 성공하지 못해서 매우 기쁘다. 왜냐하면 해결 방안이 매우 거칠고, 나에게 전혀 적합하지 않기 때문이다.특히 이것은 정의가 명확하지 않은 행위이기 때문에 장래에는 틀림없이 나를 거꾸로 물 것이다.
나 진짜 쓰고 싶어.참을 수 있을까요?그럼요.저 밤에 자도 돼요?이 가능하다, ~할 수 있다,...그러나 이것은 잘못된 것이다. 나는 아직 좀 이성적이다.
이때 나는 Cypress에 문제가 생겼기 때문에 내 두 동료에게'm I n u t e'를 비울 수 있느냐고 물었다.

측백나무가 측백나무일까요?


측백나무는 베시브가 우리에게 가져다 준 무서운 도구라고 탓하기 쉽다.그러나 앞서 언급한 바와 같이 Cypress는 매우 혼란스러운 브라우저 환경에 우호적인 인터페이스를 제공했다.팀을 다음 위대한 사업에 진입시키는 어떤 꿈도 무시하다™️, 우리는 문제의 소재와 이 문제를 어떻게 해결하는지 찾아내기 시작했다.
첫 번째 클릭이 발생했을 때 클릭 이벤트를 설치할 수 없을 것 같습니다.그러나 .click().click() 이 문제를 해결할 수 있었지만 왜 두 번째 클릭이 유효한지 설명할 수 없었다.그러나 wait() 항상 무시되는 것은 깨우기 구성 요소인 것 같습니다.
진일보한 테스트에 따르면 이전 단추를 눌렀을 때, 심지어 다음 단추를 눌렀을 때도 이런 상황이 발생할 수 있다.나는 내가 이런 행위에 대해 해명할 수 있기를 바란다. 그러나 불행하게도 사실은 그렇지 않다.그러나 나는 확실히 효과적인 해결 방안이 하나 있다.

이 문제의 효과적인 해결 방안


우리는 요소가 클릭할 준비가 되어 있는지 확인하고 다음 클릭을 다시 호출할 수 있는 해결 방안을 개발했다.듣기에는 좀 지나치고, 보기에는 좀 지나치지만, 이것이 우리가 찾은 유일한 방탄 방법이다.그것도 우아하다.
const clickOnControl = (selector, times, callback) => {
  if (times > 0) {
    cy.get(selector).then(($next) => {
      cy.wrap($next).click().then(() => {
        clickOnControl(selector, times - 1);
      });
    });
  } else if (callback) {
    callback();
  }
};
최종 e2e 테스트는 간단하고 우아해 보입니다.
const getItems = () => cy.get(byTestId(`snippet-card`));
getItems();
getItems().should('have.length', 7);
getItems().first().should('be.visible');
getItems().last().should('not.be.visible');
cy.get(byTestId('result-snippet-control-previous')).should('not.be.visible');
cy.get(byTestId('result-snippet-control-next')).should('be.visible');

clickOnControl(byTestId('result-snippet-control-next'), 1,
  () => {
    getItems().first().should('not.be.visible');
    getItems().last().should('not.be.visible');
    cy.get(byTestId('result-snippet-control-previous')).should('be.visible');
    cy.get(byTestId('result-snippet-control-next')).should('be.visible');
  },
);

clickOnControl(byTestId('result-snippet-control-next'), 3,
  () => {
    getItems().first().should('not.be.visible');
    getItems().last().should('be.visible');
    cy.get(byTestId('result-snippet-control-previous')).should('be.visible');
    cy.get(byTestId('result-snippet-control-next')).should('not.be.visible');
  },
);

clickOnControl(byTestId('result-snippet-control-previous'), 1,
  () => {
    getItems().first().should('not.be.visible');
    getItems().last().should('not.be.visible');
    cy.get(byTestId('result-snippet-control-previous')).should('be.visible');
    cy.get(byTestId('result-snippet-control-next')).should('be.visible');
  },
);

clickOnControl(byTestId('result-snippet-control-previous'), 3,
  () => {
    getItems().first().should('be.visible');
    getItems().last().should('not.be.visible');
    cy.get(byTestId('result-snippet-control-previous')).should('not.be.visible');
    cy.get(byTestId('result-snippet-control-next')).should('be.visible');
  },
);

마지막 노트


나는 몇 년 전에 내가 프런트 파티에서 Cypress를 처음 알았을 때를 기억한다.그것은 정말 아주 유용한 도구로 나에게 팔렸다.나는 Cypress의 창립자와 관리자를 매우 존경한다. 그들은 github에서도 매우 활발하고 도움이 되는 것 같다.그러나 우리의 두통 정도와 e2e 테스트의 취약성은 다음 큰일을 진지하게 고려하기 시작한다™️.

좋은 웹페이지 즐겨찾기