Cypress + Storybook. Speicherung von Testfall-, Daten- und Komponenten-Rendering an einem Ort

Cypress wurde zunächst als e2e-Testwerkzeug wahrgenommen. Es war interessant, das wachsende Interesse der Front-End-Ingenieure an einem Thema zu beobachten, bei dem Selen sein ganzes Leben lang regierte. Zu dieser Zeit beschränkte sich ein typisches Video oder ein typischer Artikel, der die Fähigkeiten von Cypress demonstrierte, darauf, auf einer zufällig ausgewählten Site herumzuwandern und wohlverdiente schmeichelhafte Bewertungen der API für die Dateneingabe abzugeben.


Viele von uns haben vermutet, Cypress zu verwenden, um Komponenten isoliert zu testen, die von Umgebungen wie Storybook / Styleguidist / Docz bereitgestellt werden. Ein gutes Beispiel ist Stefano Magnis Artikel "Testen einer virtuellen Listenkomponente mit Cypress und Storybook" . Es wird vorgeschlagen, eine Storybook-Story zu erstellen, eine Komponente darin zu platzieren und globale variable Daten einzugeben, die für den Test nützlich sind. Dieser Ansatz ist gut, aber darin ist der Test zwischen dem Storybook und Cypress hin und her gerissen. Wenn wir viele Komponenten haben, sind solche Tests schwer zu lesen und zu warten.


In diesem Artikel werde ich versuchen zu zeigen, wie man ein wenig weiter geht und das Maximum aus der Fähigkeit herausholt, JavaScript in Cypress auszuführen. Um zu sehen, wie es funktioniert, laden Sie bitte den Quellcode unter der Adresse herunter und führen Sie die Testbefehle npm i und npm run aus .


tl; DR:


  • Sie können in einem Fenster einen Link zu einer Komponente aus der Storybook Story platzieren, um sie vollständig von Cypress zu testen (ohne die Testlogik in mehrere Teile zu zerlegen).
  • Cypress schien unserem Team so mächtig zu sein, dass wir die Tools, die js-dom unter der Haube zum Testen von UI-Komponenten verwenden, komplett aufgegeben haben.

Formulierung des Problems


, 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 = () => {
    // Reference to retrieve it in Cypress during the test
    window.Datepicker = Datepicker;

    // Just a mount point
    return (
        <div id="component-test-mount-point"></div>
    )
};

Storybook. Cypress.


Cypress


-. , :


/// <reference types="cypress" />

import React from 'react';
import ReactDOM from 'react-dom';

/**
 * <Datepicker />
 * * renders text field.
 * * renders desired placeholder text.
 * * renders chosen date.
 * * opens calendar after clicking on text field.
 */

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.', () => {
    // arrange
    cy.window().then((win) => {
        // initial state - popup is visible
        ReactDOM.render(
            <PopupTestWrapper
                showed={true}
                win={win}
            />,
            win.document.querySelector(rootToMountSelector)
        );
    });

    // act
    cy.then(() => { setPopupTestWrapperState(false); })

    // assert
    cy
        .get(selectors.popupWindow)
        .should('not.be.visible');
});

: hook setState , class.


, , . , - -.


Cypress . ref . , ref state .


<Popup /> , ( ). :


it('closes via method call.', () => {
    // arrange
    let popup = React.createRef();
    cy.window().then((win) => {
        // initial state - popup is visible
        ReactDOM.render(
            <win.Popup
                showed={true}
                ref={popup}
            />,
            win.document.querySelector(rootToMountSelector)
        );
    });

    // act
    cy.then(() => { popup.current.hide(); })

    // assert
    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, ..


Dieses Tool weist jedoch eine Reihe von Problemen auf, die es nicht zulassen, es als vollwertige Umgebung für die Ausführung von Tests zu verwenden.


Ich hoffe, dass Gleb Bahmutov und das Cypress-Team diese Schwierigkeiten bewältigen werden.


PS: Meine Meinung und die Meinung meiner Kollegen stimmen darin überein, dass der vorgeschlagene Ansatz es uns ermöglicht, das Monopol der Tools mit js-dom zu überprüfen. Was denkst du darüber?


All Articles