السرو + كتاب القصة. تخزين حالة الاختبار والبيانات والمكونات في مكان واحد

في البداية ، كان يُنظر إلى السرو على أنه أداة اختبار e2e. كان من المثير للاهتمام ملاحظة الاهتمام المتزايد لمهندسي الواجهة في موضوع حكمت فيه السيلينيوم طوال حياته. في ذلك الوقت ، اقتصر مقطع فيديو أو مقال نموذجي يوضح إمكانات Cypress على التجول حول موقع تم اختياره عشوائيًا ومراجعات جذابة حول API لإدخال البيانات.


لقد خمن الكثير منا استخدام Cypress لاختبار المكونات في عزلة توفرها بيئات مثل Storybook / Styleguidist / Docz. مثال جيد هو مقال Stefano Magni "اختبار مكون القائمة الافتراضية مع السرو وقصة القصص" . يقترح إنشاء قصة Storybook ، ووضع مكون فيها ووضعها في بيانات متغيرة عالمية ستكون مفيدة للاختبار. هذا النهج جيد ، ولكن الاختبار فيه ممزق بين Storybook و Cypress. إذا كان لدينا العديد من المكونات ، فسيكون من الصعب قراءة هذه الاختبارات وصيانتها.


في هذه المقالة سأحاول أن أبين كيف أذهب إلى أبعد من ذلك وأخذ الحد الأقصى من القدرة على تنفيذ JavaScript في Cypress. لمعرفة كيفية عملها ، يرجى تنزيل شفرة المصدر على العنوان وتشغيل أوامر اختبار npm i و npm .


ليرة تركية ؛ دكتور:


  • يمكنك وضع رابط في نافذة لمكون من Storybook Story لاختباره بالكامل بواسطة Cypress (بدون تقسيم منطق الاختبار إلى عدة أجزاء).
  • بدا السرو قويًا جدًا لفريقنا لدرجة أننا تخلوا تمامًا عن الأدوات التي تستخدم js-dom تحت غطاء المحرك لاختبار مكونات واجهة المستخدم.

صياغة المشكلة


تخيل أننا نكتب محولًا لمكون Datepicker الحالي الذي سيتم استخدامه في جميع مواقع الشركة. لكي لا نكسر أي موقع عن طريق الخطأ ، نريد تغطيته بالاختبارات.


قصة


على جانب Storybook ، كل ما نحتاجه هو قصة فارغة يتم فيها تخزين مرجع للمكون قيد الاختبار في متغير عام. لكي لا تكون عديم الفائدة تمامًا ، ستقوم هذه القصة برسم عقدة DOM واحدة لنا. دورها هو توفير مكان لأرض الاختبار حيث سيختبر السرو المكون الهدف.


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>
    )
};

لقد انتهينا من كتاب القصة. الآن ننتقل كل انتباهنا إلى السرو.


شجرة السرو


أفضل البدء في العمل على المكون من خلال سرد حالات الاختبار. بعد أن نقرر طلاء الاختبار ، نحصل على الفراغ التالي:


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


ولكن هذه الأداة لديها الآن عدد من المشاكل التي لا تسمح باستخدامها كبيئة كاملة لإجراء الاختبارات.


آمل أن يتعامل جليب باهموتوف وفريق السرو مع هذه الصعوبات.


ملاحظة: رأيي ورأي زملائي يتفقون على أن النهج المقترح يسمح لنا بمراجعة احتكار الأدوات باستخدام js-dom. ما رأيك في ذلك؟


All Articles