A princípio, o Cypress foi percebido como uma ferramenta de teste do e2e. Foi interessante observar o crescente interesse dos engenheiros de front-end em um tópico em que o Selenium governou toda a sua vida. Naquela época, um vídeo ou artigo típico demonstrando os recursos do Cypress limitava-se a vaguear por um site selecionado aleatoriamente e a merecidas críticas lisonjeiras sobre a API para entrada de dados.
Muitos de nós imaginamos usar o Cypress para testar componentes isolados fornecidos por ambientes como Storybook / Styleguidist / Docz. Um bom exemplo é o artigo de Stefano Magni "Testando um componente da lista virtual com Cypress e Storybook" . Ele propõe criar um Storybook Story, colocar um componente nele e inserir dados variáveis globais que serão úteis para o teste. Essa abordagem é boa, mas nela o teste é dividido entre o Storybook e o Cypress. Se tivermos muitos componentes, esses testes serão difíceis de ler e manter.
Neste artigo, tentarei mostrar como ir um pouco mais longe e aproveitar ao máximo a capacidade de executar JavaScript no Cypress. A fim de ver como ele funciona, faça o download do código fonte no endereço e executar o npm i e NPM Test Run comandos .
tl; dr:
- Você pode colocar um link em uma janela para um componente do Storybook Story para testá-lo completamente pelo Cypress (sem quebrar a lógica do teste em várias partes).
- O Cypress parecia tão poderoso para nossa equipe que abandonamos completamente as ferramentas que usam js-dom sob o capô para testar os componentes da interface do usuário.
Formulação do problema
, Datepicker . , .
Storybook
Storybook , — Story . , Story DOM-. — , Cypress .
import React from 'react';
import Datepicker from './Datepicker.jsx';
export default {
component: Datepicker,
title: 'Datepicker',
};
export const emptyStory = () => {
window.Datepicker = Datepicker;
return (
<div id="component-test-mount-point"></div>
)
};
Storybook. Cypress.
Cypress
-. , :
import React from 'react';
import ReactDOM from 'react-dom';
context('<Datepicker />', () => {
it('renders text field.', () => { });
it('renders desired placeholder text.', () => { });
it('renders chosen date.', () => { });
it('opens calendar after clicking on text field.', () => { });
})
. . Storybook. Story, "Open canvas in new tab" sidebar. URL Cypress:
const rootToMountSelector = '#component-test-mount-point';
before(() => {
cy.visit('http://localhost:12345/iframe.html?id=datepicker--empty-story');
cy.get(rootToMountSelector);
});
, div id=component-test-mount-point. , . :
afterEach(() => {
cy.document()
.then((doc) => {
ReactDOM.unmountComponentAtNode(doc.querySelector(rootToMountSelector));
});
});
. , :
const selectors = {
innerInput: '.react-datepicker__input-container input',
};
it('renders text field.', () => {
cy.window().then((win) => {
ReactDOM.render(
<win.Datepicker />,
win.document.querySelector(rootToMountSelector)
);
});
cy
.get(selectors.innerInput)
.should('be.visible');
});
? props. . . — Cypress!
,
, props.
<Popup /> c props "showed". "showed" true, <Popup /> . "showed" c true false, <Popup /> .
?
, React - .
state. state boolean, "showed" props.
let setPopupTestWrapperState = null;
const PopupTestWrapper = ({ showed, win }) => {
const [isShown, setState] = React.useState(showed);
setPopupTestWrapperState = setState;
return <win.Popup showed={isShown} />
}
, :
it('becomes hidden after being shown when showed=false passed.', () => {
cy.window().then((win) => {
ReactDOM.render(
<PopupTestWrapper
showed={true}
win={win}
/>,
win.document.querySelector(rootToMountSelector)
);
});
cy.then(() => { setPopupTestWrapperState(false); })
cy
.get(selectors.popupWindow)
.should('not.be.visible');
});
: hook setState , class.
, , . , - -.
Cypress . ref . , ref state .
<Popup /> , ( ). :
it('closes via method call.', () => {
let popup = React.createRef();
cy.window().then((win) => {
ReactDOM.render(
<win.Popup
showed={true}
ref={popup}
/>,
win.document.querySelector(rootToMountSelector)
);
});
cy.then(() => { popup.current.hide(); })
cy
.get(selectors.popupWindow)
.should('not.be.visible');
})
:
Storybook:
- Storybook Stories React .
- .
- Story window ( Cypress).
- Story , ( ).
- .
: Storybook . Stories .
Cypress:
- JavaScript .
- Stories, .
- (, ).
- .
- UI .
, . , , .
js-dom . ?
- Js-dom , . DOM .
- js-dom . .
- -, CSS z-index? Cypress, .
- - . ?
?
— !
— .
"" - react-lifecycle — … . . , ? , ?
cypress-react-unit-test? Storybook?
— . Storybook, Cypress, ..
Mas agora esta ferramenta tem vários problemas que não permitem usá-la como um ambiente completo para a execução de testes.
Espero que Gleb Bahmutov e a equipe de Cypress lidem com essas dificuldades.
PS: Minha opinião e a de meus colegas concordam que a abordagem proposta nos permite revisar o monopólio de ferramentas usando js-dom. O que você acha disso?