Au dĂ©but, Cypress Ă©tait perçu comme un outil de test e2e. Il Ă©tait intĂ©ressant d'observer l'intĂ©rĂȘt croissant des ingĂ©nieurs front-end pour un sujet dans lequel le sĂ©lĂ©nium a rĂ©gnĂ© toute sa vie. Ă cette Ă©poque, une vidĂ©o ou un article typique dĂ©montrant les capacitĂ©s de Cypress se limitait Ă se promener sur un site sĂ©lectionnĂ© au hasard et Ă des critiques flatteuses bien mĂ©ritĂ©es sur l'API pour la saisie de donnĂ©es.
Beaucoup d'entre nous ont deviné utiliser Cypress pour tester les composants isolés fournis par des environnements tels que Storybook / Styleguidist / Docz. Un bon exemple est l'article de Stefano Magni "Tester un composant de liste virtuelle avec Cypress et Storybook" . Il propose de créer un Storybook Story, d'y placer un composant et de mettre dans une variable globale des données qui seront utiles pour le test. Cette approche est bonne, mais le test est partagé entre le livre de contes et Cypress. Si nous avons de nombreux composants, ces tests seront difficiles à lire et à maintenir.
Dans cet article, je vais essayer de montrer comment aller un peu plus loin et tirer le maximum de la possibilité d'exécuter JavaScript dans Cypress. Pour voir comment cela fonctionne, veuillez télécharger le code source à l'adresse et exécuter les commandes de test npm i et npm run .
tl; dr:
- Vous pouvez placer un lien dans une fenĂȘtre vers un composant du Storybook Story pour le tester dans son intĂ©gralitĂ© par Cypress (sans casser la logique de test en plusieurs parties).
- Cypress semblait si puissant à notre équipe que nous avons complÚtement abandonné les outils qui utilisent js-dom sous le capot pour tester les composants de l'interface utilisateur.
Formulation du problĂšme
, 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, ..
Mais maintenant, cet outil a un certain nombre de problÚmes qui ne permettent pas de l'utiliser comme un environnement à part entiÚre pour exécuter des tests.
J'espÚre que Gleb Bahmutov et l'équipe Cypress feront face à ces difficultés.
PS: Mon avis et l'avis de mes collÚgues conviennent que l'approche proposée nous permet de revoir le monopole des outils utilisant js-dom. Qu'est ce que tu penses de ça?