<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Semi Design</title>
    <description>The latest articles on DEV Community by Semi Design (@semidesign).</description>
    <link>https://dev.to/semidesign</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F868607%2Fdbcccf03-cf07-4c93-af13-f63269c8ebad.jpg</url>
      <title>DEV Community: Semi Design</title>
      <link>https://dev.to/semidesign</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/semidesign"/>
    <language>en</language>
    <item>
      <title>Semi Design ❤️ Figma Dev Mode</title>
      <dc:creator>Semi Design</dc:creator>
      <pubDate>Fri, 07 Jul 2023 07:58:46 +0000</pubDate>
      <link>https://dev.to/semidesign/semi-design-figma-dev-mode-igl</link>
      <guid>https://dev.to/semidesign/semi-design-figma-dev-mode-igl</guid>
      <description>&lt;p&gt;🎉 Happy to announce that  &lt;a href="https://www.figma.com/community/plugin/1166339852662786534/"&gt;Semi Design Plugin&lt;/a&gt; has already compatible with Figma Dev Mode.&lt;/p&gt;

&lt;p&gt;Now we can quickly view all code directly without open plugin，convert UI to code can be finished in seconds with fully design system support.&lt;/p&gt;

&lt;p&gt;More info 👉🏻 &lt;a href="https://semi.design/code/en-US"&gt;https://semi.design/code/en-US&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--noQ4E4Js--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://sf16-sg.tiktokcdn.com/obj/eden-sg/slepweh7nupqpognuhbo/dev-to/Release-DevMode.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--noQ4E4Js--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://sf16-sg.tiktokcdn.com/obj/eden-sg/slepweh7nupqpognuhbo/dev-to/Release-DevMode.jpg" alt="devmode design2code usage" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>design2code</category>
      <category>figmadevmode</category>
      <category>designsystem</category>
      <category>ui</category>
    </item>
    <item>
      <title>How We Test Semi Design React Component</title>
      <dc:creator>Semi Design</dc:creator>
      <pubDate>Thu, 04 Aug 2022 10:55:00 +0000</pubDate>
      <link>https://dev.to/semidesign/how-we-test-semi-design-react-component-55a0</link>
      <guid>https://dev.to/semidesign/how-we-test-semi-design-react-component-55a0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TL;DR&lt;br&gt;
Through this article you can learn &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the basic method of React component testing&lt;/li&gt;
&lt;li&gt;how we test Semi Design react component with Jest/ Cypress.io/ Chromatic.&lt;/li&gt;
&lt;li&gt;how to combine test report of multiple test tools &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;Estimated reading time: 25 ~ 30 min&lt;/p&gt;

&lt;h4&gt;
  
  
  Related Introduction
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://semi.design/en-US/"&gt;Semi Design&lt;/a&gt; is maintained by the Douyin front-end and UED teams. It is committed to efficiently connecting designers and developers, linking DesignOps &amp;amp; DevOps, and providing a modern design system with high customization capabilities, 🎨 Provide more than 2,300+ Design Tokens and powerful DSM tools, easy to make Semi Design to Any Design&lt;/p&gt;

&lt;p&gt;At present, Semi Design (@douyinfe/semi-ui) provides 60+ common components for Enterprise project. These include simple components such as Button, Input, Card, and complex components such as Form, Table, TreeSelect, Cascader. These components follow Semi’s design language by default, and users can also customize the package based on application scenarios.&lt;/p&gt;

&lt;p&gt;In order to maintain the consistency of UI and interaction for each component during version iterations, the testing of component libraries is particularly important. In this article, we will share the following content around the theme of “How We Test Semi Design React Component”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why should we test the component library?&lt;/li&gt;
&lt;li&gt;How to evaluate the test effect？&lt;/li&gt;
&lt;li&gt;How to implement component library testing？&lt;/li&gt;
&lt;li&gt;How to combine CI for testing？&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ew0zvFK9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bgtt55ylyxubo6fwndmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ew0zvFK9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bgtt55ylyxubo6fwndmd.png" alt="Semi Design Component Library" width="880" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  1 Why test and what to test？
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1.1 Why
&lt;/h2&gt;

&lt;p&gt;Semi ui provides general and customizable React components. We need to ensure that the basic interaction of the components can work normally, for example, clicking a button can trigger a button click event, and clicking a selector can trigger an option selection event, etc. &lt;br&gt;
In addition, Semi components have a set of standard design language by default, including text, color, size, spacing, etc. We need to ensure that the component display conforms to Semi's design specifications.&lt;/p&gt;
&lt;h2&gt;
  
  
  1.2 Where
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Semi Github Repository Directory &lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;semi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;          &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="nx"&gt;animation&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;semi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;react&lt;/span&gt;    &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt; &lt;span class="nx"&gt;animation&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;semi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;styled&lt;/span&gt;   &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="nx"&gt;animation&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;👉🏻&lt;/span&gt; &lt;span class="nx"&gt;semi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;foundation&lt;/span&gt;      &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;foundation&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;semi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;icons&lt;/span&gt;              &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;semi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;illustrations&lt;/span&gt;      &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;illustrations&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;semi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;      &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;👉🏻&lt;/span&gt; &lt;span class="nx"&gt;semi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ui&lt;/span&gt;              &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt; &lt;span class="nx"&gt;adapter&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;                     &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Packaging&lt;/span&gt; &lt;span class="nx"&gt;related&lt;/span&gt;

&lt;span class="c1"&gt;// https://github.com/DouyinFE/semi-design/tree/main/packages&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Semi component library is a React component library based on the foundation + adapter architecture. The foundation layer includes TypeScript code that has nothing to do with front-end frameworks such as React, Vue, etc. The adapter layer is a React implementation based on foundation. Our test scope is these two layers related code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;foundation&lt;/strong&gt;: &lt;code&gt;@douyinfe/semi-foundation&lt;/code&gt;, includes framework-agnostic TS code, which is converted to JavaScript code when the component is published&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;adapter&lt;/strong&gt;: &lt;code&gt;@douyinfe/semi-ui&lt;/code&gt;, includes React UI code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--prCokywA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmbfgpr41sphjlns1u06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--prCokywA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmbfgpr41sphjlns1u06.png" alt="semi foundation adapter" width="880" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition, the animation of Semi components relies on &lt;code&gt;semi-animation&lt;/code&gt; related packages, which are not in our testing scope due to less dynamic changes and high testing complexity.&lt;/p&gt;
&lt;h1&gt;
  
  
  2 How to evaluate the test?
&lt;/h1&gt;

&lt;p&gt;Test evaluation includes two aspects: test pass rate and test coverage. The test pass rate is the bottom line, which ensures that the tested functions will not be affected by version iterations, and the test coverage rate measures whether the test code is comprehensive.  &lt;/p&gt;

&lt;p&gt;During the development of the component, we will use the manual test method to check whether the component function can run normally, and in the iteration of the version, we need to use the automated test to help us test.  &lt;/p&gt;
&lt;h2&gt;
  
  
  2.1 Manual testing
&lt;/h2&gt;

&lt;p&gt;In Semi's component development process, we will first start a &lt;a href="https://storybook.js.org/"&gt;Storybook&lt;/a&gt; project, develop component  based on Storybook, and write use cases corresponding to our component API through stories. With these use cases we can browse component styles and test component interactions&lt;br&gt;
For example, in the image below, we create a story for the primary type of the Button component, and manually check whether the background color and font color are correct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const Primary = () =&amp;gt; &amp;lt;Button type="primary"&amp;gt;UI semi&amp;lt;/Button&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A8YvBUXx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c29yjeut598vdfdc5u9i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A8YvBUXx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c29yjeut598vdfdc5u9i.png" alt="Storybook For The Button Component" width="880" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2.2 Automatic testing
&lt;/h2&gt;

&lt;p&gt;Manual testing is only suitable for the development phase, and there is no guarantee that the component will maintain the consistency of UI and interaction during the iteration process. Therefore, we need to introduce test tools to help test. Semi Teams generally writes test cases after component development is complete. We test the functionality of the component by writing test cases, and then check whether the UI display and interaction behavior of the component meet expectations according to the pass rate and code coverage of the test cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  2.3 Code coverage
&lt;/h2&gt;

&lt;p&gt;In addition to the pass rate of manual and automated tests, code test coverage is also an important criterion for test evaluation. According to Wikipedia's definition,"Code coverage is a measure in software testing that describes the proportion and extent to which the source code in a program is tested. The resulting proportion is called code coverage". Code coverage includes function coverage, statement coverage, condition coverage, judgment coverage, and line coverage.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function coverage&lt;/strong&gt;: are there calls to every function in the program? Whether the function has been called.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Statement coverage&lt;/strong&gt;: is there a call to each statement? In JS, values, operators, expressions, keywords, and comments are all statements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Condition Coverage&lt;/strong&gt;: is each condition in each logical expression (logical expression that cannot be decomposed any more)  executed?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Judgment coverage&lt;/strong&gt;: is there a call to every branch in the logical expression? The "if instruction" is true or not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Line Coverage&lt;/strong&gt;:has this line been executed? A line may contain multiple statements and branches.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  3 Semi Component Library Test Method
&lt;/h1&gt;

&lt;p&gt;There are three testing methods for Semi component library, namely &lt;strong&gt;unit testing&lt;/strong&gt;, &lt;strong&gt;E2E testing&lt;/strong&gt; and &lt;strong&gt;Visual testing&lt;/strong&gt;. The following describes the test scenarios of these three and how to use them to test components.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.1 Unit testing by Jest
&lt;/h2&gt;

&lt;p&gt;What is unit testing? According to the definition of Wikipedia, "In computer programming, unit testing, also known as module testing, is a test work for correctness verification of program modules (the smallest unit of software design)." From the perspective of the Semi component library, unit testing is the testing of components.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1.1 Technical solutions
&lt;/h3&gt;

&lt;p&gt;Semi unit testing technical solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test framework: Jest, which provides functions such as runnable environment, test structure, test report, assertion, mocking, etc.&lt;/li&gt;
&lt;li&gt;Auxiliary testing library: Enzyme, mainly used for React component render&lt;/li&gt;
&lt;li&gt;Auxiliary test library: JSDOM, which provides a DOM operation environment in the Node environment and is used in conjunction with Enzyme's Full DOM Rendering scene&lt;/li&gt;
&lt;li&gt;Auxiliary test library: Sinon, provides spy, stub, mock for event testing and callback function testing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.1.2 Test content
&lt;/h3&gt;

&lt;p&gt;Semi unit tests mainly include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the correct DOM tree the component should be rendered in&lt;/li&gt;
&lt;li&gt;Whether the properties of the component are passed correctly (whether the method is called correctly, etc.)&lt;/li&gt;
&lt;li&gt;Whether the individual behaviors within the component respond correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.1.3 Common API Examples
&lt;/h3&gt;

&lt;p&gt;🌰 For example, we want to test whether the className or style of the Button component is correctly rendered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button with custom className &amp;amp; style&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Mount a Button with className and style&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;getDOMNode&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🌰 Or test that a Button with an icon renders the icon correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`button with icon`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iconType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`semi-icon-edit`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IconEdit&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;iconType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🌰 Test that the component's properties are passed correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Input&lt;/span&gt;  &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// read state directly&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// read props&lt;/span&gt;
 &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🌰 Modify the state and props to test whether the component UI state has changed correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simulate the external state changes of the component through the setState and setProps interfaces&lt;/span&gt;
&lt;span class="c1"&gt;// test whether the UI responds correctly when the component state changes dynamically&lt;/span&gt;
&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change props &amp;amp; state&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Input&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🌰 Test that the component's event callback is called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input should call onChange when value change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;inputValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;semi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inputValue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
     &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
     &lt;span class="c1"&gt;// Use sinon.spy to encapsulate callback functions&lt;/span&gt;
     &lt;span class="c1"&gt;// Function call information can be collected after spy&lt;/span&gt;
     &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;spyOnChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Input&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;spyOnChange&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;     &lt;span class="c1"&gt;// Find the native input element, trigger the simulation event&lt;/span&gt;
     &lt;span class="c1"&gt;// simulate the value change of the input&lt;/span&gt;
     &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.semi-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;simulate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spyOnChange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;calledOnce&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// onChange callback is executed once&lt;/span&gt;
 &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.1.4 Some Tips of jest / enzyme
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;3.1.4.1 Mount a component with a popup layer, but can't find the DOM corresponding to the popup layer?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enzyme's default mount will only mount the component itself to a div container, not to the document. The pop-up layer is inserted into document.body by means of appendChild, so the container of the portal cannot be found. If it is not inserted, there will be no pop-up layer.&lt;/p&gt;

&lt;p&gt;Use attachTo to mount the container to a specific div in the body. Note that you also need to create and destroy in beforeEach and afterEach.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// mount(component, { attachTo: container })&lt;/span&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AutoComplete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Avoid `attachTo: document.body` Warning&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom className &amp;amp; style&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AutoComplete&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;, { attachTo: document.getElementById&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;'container'&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt; }&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;3.1.4.2 JSDOM does not contain a Layout engine, so when calling the getBoundingClientRect function and getting the offsetWidth, it always returns 0?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can use getComputedStyle to get CSS properties, &lt;a href="https://github.com/jsdom/jsdom/issues/135"&gt;#135&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3.1.4.3  Error when import es module in jest&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, when we import { get } from lodash-es. Error reporting By default, modules in node_modules will not go to babel-jest, while lodash-es exports ES modules, and CommonJS is needed in the Node environment.&lt;/p&gt;

&lt;p&gt;Solution：Put all packages under node_modules that need to be compiled with babel-jest, Configure the corresponding module path in &lt;a href="https://jestjs.io/docs/configuration#transformignorepatterns-arraystring"&gt;transformIgnorePatterns&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The modules in node_modules that need to be processed by babel-jest are declared here&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ModuleNeedCompile2Cjs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash-es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dnd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dnd-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dnd-html5-backend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;jestConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transformIgnorePatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;rootDir&amp;gt;/node_modules/(?!(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ModuleNeedCompile2Cjs&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;))`&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.1.5 Advantage &amp;amp; disadvantage
&lt;/h3&gt;

&lt;p&gt;✅ Unit testing is a test from the programmer's point of view. When writing, it is more to test whether the return result of each function in the component is the same as the expected value. It is suitable for testing the state of React components, the invocation of callback functions, and the transfer of parameters and properties. Mounting and rendering of components, etc. &lt;/p&gt;

&lt;p&gt;❌ However, this test method has some flaws. It relies more on the trust in the state of the component, rather than testing the real interaction of the user. For some interaction operations that are strongly related to the user, such as scrolling, delay or page jumping, therefore, we also need a test method that can simulate user behavior - E2E test.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.2 E2E testing by Cypress
&lt;/h2&gt;

&lt;p&gt;E2E is the abbreviation of "End to End". It tests whether the component behaves as expected by simulating the user's actions in the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2.1 Plan selection
&lt;/h3&gt;

&lt;p&gt;Semi has conducted research on various test platform tools in the industry (such as TestCafe, Testim.io, Cypress, CrossBrowserTesting, Webdriver.io, Nightwatch...), after comprehensive comparison of multiple dimensions such as ecological perfection, feature richness, GUI ease of use, the feasibility of secondary development of plug-ins, we finally adopted &lt;a href="https://www.cypress.io/"&gt;Cypress&lt;/a&gt; as our E2E testing tool and Chromatic as our UI testing tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2.2 Test content
&lt;/h3&gt;

&lt;p&gt;In the early stage of the Semi component library, we used unit testing, which can satisfy most test scenarios, but with the iteration of components, we found that there are some untestable scenarios in unit testing, such as the calculation of Tooltip pop-up position, Slider dragging move a distance, etc. We urgently need a means to supplement these untested and more complex scenarios. After research, we introduced Cypress for E2E testing. Cypress complements existing unit tests, and its implementation cost will be much less than unit tests in the following two scenarios:   &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The first one is to use unit tests to write tedious test cases for long operation paths;
&lt;/li&gt;
&lt;li&gt;The second is some operations that are not easy to implement through unit testing.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It mainly includes (but is not limited to) the following operating scenarios:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scrolling behavior：Anchor、BackTop、Table、ScrollList、DatePicker、TimePicker&lt;/li&gt;
&lt;li&gt;DOM location：Tooltip（The location of the popup is currently untestable with Enzyme + JSDOM）&lt;/li&gt;
&lt;li&gt;form submission：Form&lt;/li&gt;
&lt;li&gt;async delay behavior：Notification、Toast&lt;/li&gt;
&lt;li&gt;link jump：Navigation&lt;/li&gt;
&lt;li&gt;complex use case：Tree、TreeSelect、Table&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/jsdom/jsdom"&gt;JSDOM&lt;/a&gt; is a pure JS implementation of web standards like DOM and HTML for Node.js. When used with Enzyme, JSDOM emulates the DOM manipulation environment for Enzyme.&lt;br&gt;
But there are some &lt;a href="https://github.com/jsdom/jsdom#unimplemented-parts-of-the-web-platform"&gt;limitations&lt;/a&gt; like no navigation and layout. JSDOM will return 0 when using layout related functions like getBoundingClientRects and properties like offsetTop .&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.2.3 Common API examples
&lt;/h3&gt;

&lt;p&gt;🌰 Test the scrolling behavior of the Anchor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// visit storybook&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://127.0.0.1:6006/iframe.html?id=anchor--target-offset&amp;amp;args=&amp;amp;viewMode=story&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#box&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;scrollTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bottom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// Check whether the anchor title of the currently selected state after scrolling is doc1&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.semi-anchor-link-title-active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doc1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#box&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;scrollTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// See if the top header whatever appears in the page, i.e. successfully scrolled to the top&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;whatever&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🌰 Test the Tooltip's popup behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;leftTopOver autoAdjustOverflow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;viewportWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;viewportHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;660&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;triggerWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;triggerHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;leftTopPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; 
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rightBottomPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;viewportHeight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;triggerHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;viewportWidth&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;triggerWidth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://127.0.0.1:6006/iframe.html?id=tooltip--left-top-over-auto-adjust-overflow&amp;amp;args=&amp;amp;viewMode=story&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Control viewport size&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewportWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewportHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`[data-cy=leftTopOver]`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataSelector&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;scrollIntoView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leftTopPosition&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataSelector&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;force&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Determine whether the tooltip pop-up position is correct&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[x-placement="leftTopOver"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.length&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// After changing the offset position of the scroll bar, determine whether the pop-up position of the tooltip is correct&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataSelector&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;scrollIntoView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rightBottomPosition&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[x-placement="rightBottomOver"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.length&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🌰 Test that the autoplay of the carousel is as expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto play interval&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://127.0.0.1:6006/iframe.html?id=carousel--auto-play-example&amp;amp;args=&amp;amp;viewMode=story&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.semi-carousel-content-item-active h3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Autoplay interval is 1300ms&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.semi-carousel-content-item-active h3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.semi-carousel-content-item-active h3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2.4 Advantage &amp;amp; disadvantage
&lt;/h3&gt;

&lt;p&gt;✅ Cypress used by Semi makes up for the inadequacy of the Jest unit test and is suitable for testing the real API of the browser. We can use the browser's &lt;code&gt;getBoundingClientRects&lt;/code&gt; to obtain the DOM position information, or pass in pageX and pageY in the mouseover event to achieve Drag to the specified location. &lt;br&gt;
❌ But it is precisely because the test is a real browser, its test case execution time will be significantly greater than the execution time of Jest + Enzyme.&lt;/p&gt;
&lt;h2&gt;
  
  
  3.3 Visual testing by Chromatic
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.chromatic.com/"&gt;Chromatic&lt;/a&gt; is a static component visual comparison test tool that detects the visual differences of stories by comparing snapshots (images rendered by components, or snapshots). Snapshot tests are run in parallel and can run 2000+ tests in 1 minute.&lt;br&gt;
Chromatic can provide us with the following services to ensure the consistency of our UI library:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shared workspace. With each code push action, Chromatic publishes a Storybook of the code to its CDN, while providing the team with a shared workspace to comment on and review UI changes together. It works with the Github action used by Semi.&lt;/li&gt;
&lt;li&gt;Provides visual regression in test components. Chromatic turns stories into benchmarks. Each story is simultaneously rendered in Chrome, Firefox and Internet Explorer 11, then compared to a "last known good" snapshot to detect bugs.&lt;/li&gt;
&lt;li&gt;View visual changesets for components affected by each PR. Chromatic compares new and updated components on a given branch to the target branch to generate UI changesets.
Use Chromatic for UI testing, no need to write additional test case code, each story is its snapshot use case.
The following is a brief introduction to the use process of Chromatic.
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3.3.1 Process of UI diff
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: Build Storybook, publish to Chromatic Cloud
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--98xk1PUW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z0w58uh7ewjbq3j6tac8.png" alt="chromatic workflow" width="880" height="245"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Chromatic testing, each PR (rebuild after update) is a build process. Currently Semi Design has created 807 stories for 63 components, each story contains use cases of one or more components, and each build will compare the snapshots created by these 807 stories.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8MlhFHSj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xrxl59qs5hvctxow72ly.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8MlhFHSj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xrxl59qs5hvctxow72ly.png" alt="storybook demo" width="880" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Step 2: UI regression testing, comparing changes, updating baseline
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ievPxZrX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ohihwp665ipmgn7pwevv.png" alt="chromatic workflow:accept or deny" width="880" height="219"&gt;
On the Chromatic platform,  we can click the build details to see if the changes are as expected. Accept for conforming changes and deny for non conforming changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Accept&lt;/strong&gt;：Update the baseline of the story. When a snapshot is accepted, unless it changes, it does not need to be re-accepted (even via git branch or git merge)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deny&lt;/strong&gt;：Marked as "rejected", this build is immediately updated to fail status. A build can reject multiple changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Accept Example&lt;/strong&gt;: The Input password button has changed. The left is the baseline, and the right is the new build change. You can see that the password button in the non-disabled state on the right side of the picture has become a permanent display, which was displayed when the hover or focus input box was used before. However, this update was expected, so we chose to accept this change.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Eac9YIT4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rf5oecqm7wt71h2x3lc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Eac9YIT4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rf5oecqm7wt71h2x3lc2.png" alt="Accept Example" width="880" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deny Example&lt;/strong&gt;: Breadcrumb provides the ability to display ellipses when the text exceeds the set width. The new build on the right below does not show ellipsis, here we expect no change, so choose to reject this change.。&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ppbkDcCB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/13iv4uxkkfimynme05s8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ppbkDcCB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/13iv4uxkkfimynme05s8.png" alt="Deny Example" width="880" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we find an unexpected change in the style, we first need to reject the change, and then modify our style code. After pushing the code to the Github repository, Chromatic will rebuild it, and finally we review the change again.&lt;/p&gt;
&lt;h3&gt;
  
  
  3.3.2 Advantage &amp;amp; disadvantage
&lt;/h3&gt;

&lt;p&gt;✅ Chromatic is suitable for UI regression testing of static components. It can compare snapshots of components between different builds, reducing the cost of manual regression testing of component styles. It provides event-triggered hooks that can be fired at different stages of the test.&lt;br&gt;
Through this hook, a message can be sent to the developer after the test is completed to inform the status of the component test.&lt;/p&gt;

&lt;p&gt;❌ Of course, Chromatic also has some limitations. Currently, it can only test static components. The number of snapshot tests varies according to different services. The open source free plan only provides 35,000 snapshot tests per month. So we need to choose the timing to trigger the diff comparison more precisely. ( This is reasonable, because we cannot occupy the test resources infinitely)&lt;/p&gt;
&lt;h1&gt;
  
  
  4 Code coverage statistics
&lt;/h1&gt;

&lt;p&gt;We wrote relevant unit test and E2E test code for the component API. And which line or function in the source code has not been tested, we need to check the code coverage.&lt;br&gt;
Semi uses Jest and Cypress to write our test code, both of them can get corresponding code coverage.  &lt;/p&gt;

&lt;p&gt;For example, in Jest, we wrote test code such as component callback functions, and in Cypress, we wrote test code for scroll events, and the test report generated by them only includes the code coverage corresponding to the test code.  &lt;/p&gt;

&lt;p&gt;However, for the component library, what we need is to obtain the overall code coverage of the repository, which can be tested in any way, so we need to combine the test reports of these two parts together when counting the code coverage.&lt;/p&gt;
&lt;h2&gt;
  
  
  4.1 The principle of coverage statistics
&lt;/h2&gt;

&lt;p&gt;The statistics of code coverage includes two core steps. The first step is to insert a counter in each line of the source code. The second step is to run the test code, and count the execution of the source code during the running process, and accumulate the counters.&lt;br&gt;&lt;br&gt;
There are corresponding tools for these two steps, and we briefly show the process below.&lt;br&gt;&lt;br&gt;
Take a simple count function as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first step is to insert a counter into the source code. We use nyc to process the count function. After nyc processing, the function will become:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;nyc is a package for statistical code coverage.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// cov_1mo7xf3ci8 is a function name randomly generated &lt;/span&gt;
&lt;span class="c1"&gt;// by nyc&lt;/span&gt;
&lt;span class="c1"&gt;// Calling it returns a global object that holds a reference // to the counter&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cov_1mo7xf3ci8&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// When the code executes to this line&lt;/span&gt;
  &lt;span class="c1"&gt;// the count function is called once,&lt;/span&gt;
  &lt;span class="c1"&gt;// and the counter f[1] will be incremented by 1&lt;/span&gt;
  &lt;span class="nx"&gt;counters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// When the code executes to this line&lt;/span&gt;
  &lt;span class="c1"&gt;// it means that return a+b is called once&lt;/span&gt;
  &lt;span class="c1"&gt;// and the counter s[1] will be incremented by 1&lt;/span&gt;
  &lt;span class="nx"&gt;counters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;our test code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../src/index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// A very simple test code that checks count(1,2)=3&lt;/span&gt;
&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running, nyc will generate a code coverage report of the count function according to the statistics of the counter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;======= Coverage summary ===============
Statements   : 100% ( 2/2 )
Branches     : 100% ( 0/0 )
Functions    : 100% ( 1/1 )
Lines        : 100% ( 2/2 )
========================================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Corresponding to the statistics of Semi Design code coverage, in the first step, we need to convert the &lt;code&gt;semi-ui&lt;/code&gt; and &lt;code&gt;semi-foundation&lt;/code&gt; source code and insert the counter;&lt;br&gt;&lt;br&gt;
The second step runs our &lt;code&gt;Enzyme&lt;/code&gt; and &lt;code&gt;Cypress&lt;/code&gt; test code to generate a test coverage report for the source code. Because &lt;code&gt;Enzyme&lt;/code&gt; and &lt;code&gt;Cypress&lt;/code&gt; test frameworks are different, we need to generate two test reports and merge the test reports.&lt;/p&gt;
&lt;h2&gt;
  
  
  4.2 Testing report
&lt;/h2&gt;
&lt;h3&gt;
  
  
  4.2.1 Jest + Enzyme
&lt;/h3&gt;

&lt;p&gt;Jest provides the &lt;code&gt;--coverage&lt;/code&gt; parameter. When running a unit test, we can generate a test report for the unit test by passing this parameter on the command line.&lt;br&gt;&lt;br&gt;
We put Jest's code coverage report in the &lt;code&gt;test/coverage&lt;/code&gt; directory under the root directory by setting the Jest configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NODE_ENV=test type=unit ./node_modules/.bin/jest --coverage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.2.2 Cypress
&lt;/h3&gt;

&lt;p&gt;Generating code coverage for Cypress is a little trickier.&lt;br&gt;&lt;br&gt;
We need to customize insert counters and generate coverage reports.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cypress provides &lt;a href="https://docs.cypress.io/guides/tooling/code-coverage"&gt;documentation for getting code coverage&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: Insert the counter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need to first transform the Semi source code and insert the counters needed to generate code coverage. The source code can be transformed using the nyc or babel plugins.  &lt;/p&gt;

&lt;p&gt;Semi chose the babel plugin 😉. The reason is that the babel plugin connects seamlessly with Storybook's Webpack configuration. If using nyc needs to generate a temporary directory, we also need to change the source directory referenced by Storybook, so this solution is not adopted.  &lt;/p&gt;

&lt;p&gt;We insert the &lt;a href="https://www.npmjs.com/package/babel-plugin-istanbul"&gt;babel-plugin-istanbul&lt;/a&gt; plugin into the Storybook Webpack configuration, and the corresponding configuration is as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nycConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../nyc.config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// storybook webpack config&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...,&lt;/span&gt;
  &lt;span class="na"&gt;babel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;istanbulPluginOption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;babel-plugin-istanbul&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nycConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exclude&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nycConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exclude&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// If it is a test environment, insert the istanbul babel plugin&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isTest&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;istanbulPluginOption&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;babel-plugin-istanbul&lt;/code&gt; set exclude to filter out Semi source code that does not need to be tested, such as story files and packaging related files.&lt;br&gt;
We create a new &lt;code&gt;nyc.config.js&lt;/code&gt; in the root directory, configure the variables related to code coverage statistics, and refer to the relevant configuration in the Storybook above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// cypress's code coverage statistics file is here&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;report-dir&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cypress/coverage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reporter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lcov&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;packages/semi-ui/**/*.{js,jsx,ts,tsx}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;packages/semi-foundation/**/*.{js,jsx,ts,tsx}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exclude&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;**/*.test.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;**/*.stories.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;packages/**/scripts/**&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;packages/**/types/**&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;packages/**/__test__/**&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;packages/**/_story/**&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;packages/**/getBabelConfig.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;packages/**/gulpfile.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;packages/**/webpack.config.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Step 2: Collect code coverage reports
We follow the &lt;a href="https://docs.cypress.io/guides/tooling/code-coverage#E2E-code-coverage"&gt;Cypress documentation&lt;/a&gt; to configure the Semi source code coverage when running Cypress test cases.
First, install &lt;code&gt;@cypress/code-coverage&lt;/code&gt; as the dev dependency of the project and introduce the dependency in &lt;code&gt;plugin/index.js&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@cypress/code-coverage/task&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, add a reference to support/index.js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@cypress/code-coverage/support&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@cypress/code-coverage&lt;/code&gt; merges Cypress's individual tests and produces merged test results. (Actually, it also calls nyc to generate the corresponding test report)&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2.3 Combined testing report
&lt;/h3&gt;

&lt;p&gt;After generating the two code coverages, we use the &lt;a href="https://www.npmjs.com/package/istanbul-combine"&gt;instanbul-combine&lt;/a&gt; package to merge the code coverage reports of Enzyme and Cypress, and generate a merged report. These files stored in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enzyme: test/coverage/coverage-final.json&lt;/li&gt;
&lt;li&gt;Cypress: cypress/coverage/coverage-final.json&lt;/li&gt;
&lt;li&gt;Combined: test/merged&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run the command to merge the code coverage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx istanbul-combine &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;/merged &lt;span class="nt"&gt;-p&lt;/span&gt; detail &lt;span class="nt"&gt;-r&lt;/span&gt; lcov &lt;span class="nt"&gt;-r&lt;/span&gt; json cypress/coverage/coverage-final.json &lt;span class="nb"&gt;test&lt;/span&gt;/coverage/coverage-final.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that the combined code coverage is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Statement Coverages：86.5%&lt;/li&gt;
&lt;li&gt;Branches Coverages：74.9%&lt;/li&gt;
&lt;li&gt;Functions Coverages：84%&lt;/li&gt;
&lt;li&gt;Line Coverages：86.7%
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dzo1KIu3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lyczobrs1obak18s753r.png" alt="combined code coverage" width="880" height="503"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  5 Continuous Integration (CI)
&lt;/h1&gt;

&lt;p&gt;Running test commands manually and getting test reports is too tedious.&lt;br&gt;
We are now automating this process with CI (Continuous Integration) tools.&lt;/p&gt;
&lt;h2&gt;
  
  
  5.1 Github action
&lt;/h2&gt;

&lt;p&gt;Github action provides continuous integration capabilities. We hope to automatically run the test process and merge test reports when pushing code to the repository or when there is a pull request to the repository.&lt;br&gt;&lt;br&gt;
Now we add the test.yml file under workflows in the repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;name: &lt;span class="nb"&gt;test

&lt;/span&gt;on:
  push:
    branches: &lt;span class="o"&gt;[&lt;/span&gt; main, release, test-code-coverage &lt;span class="o"&gt;]&lt;/span&gt;
  pull_request:
    branches: &lt;span class="o"&gt;[&lt;/span&gt; main, release &lt;span class="o"&gt;]&lt;/span&gt;

  workflow_dispatch:

&lt;span class="nb"&gt;jobs&lt;/span&gt;:
  jest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: &lt;span class="s1"&gt;'14'&lt;/span&gt;
      - name: Run &lt;span class="nb"&gt;install
        &lt;/span&gt;run: npm i &lt;span class="nt"&gt;-g&lt;/span&gt; lerna &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run bootstrap
      - name: Run Jest &lt;span class="nb"&gt;test
        &lt;/span&gt;run: npm run &lt;span class="nb"&gt;test&lt;/span&gt;:coverage
      - name: Archive Jest coverage
        uses: actions/upload-artifact@v3
        with:
          &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="nt"&gt;-no-files-found&lt;/span&gt;: error
          name: jest
          path: &lt;span class="nb"&gt;test&lt;/span&gt;/coverage/coverage-final.json
  cypress:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: &lt;span class="s1"&gt;'14'&lt;/span&gt;
      - name: Run &lt;span class="nb"&gt;install
        &lt;/span&gt;run: |
          npm i &lt;span class="nt"&gt;-g&lt;/span&gt; lerna
          npm run bootstrap
      - name: Build storybook
        run: |
          npm run pre-story
          &lt;span class="nv"&gt;TEST_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test &lt;/span&gt;npm run build-storybook
      - name: Serve storybook
        run: &lt;span class="nb"&gt;nohup &lt;/span&gt;npx http-server &lt;span class="nt"&gt;-p&lt;/span&gt; 6006 storybook-static &amp;amp;
      - name: Run Cypress &lt;span class="nb"&gt;test
        &lt;/span&gt;run: npx wait-on http://127.0.0.1:6006 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./node_modules/.bin/cypress run
      - name: Archive Cypress coverage
        uses: actions/upload-artifact@v3
        with:
          &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="nt"&gt;-no-files-found&lt;/span&gt;: error
          name: cypress
          path: cypress/coverage/coverage-final.json
  coverage:
    runs-on: ubuntu-latest
    needs: &lt;span class="o"&gt;[&lt;/span&gt;jest, cypress]
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: &lt;span class="s1"&gt;'14'&lt;/span&gt;
      - name: Download Jest coverage
        uses: actions/download-artifact@v3
        with:
          &lt;span class="c"&gt;# upload jest and cypress coverage to output dir&lt;/span&gt;
          path: output
      - name: Code coverage merge
        run: |
          tree output
          npx istanbul-combine &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;/merged &lt;span class="nt"&gt;-p&lt;/span&gt; detail &lt;span class="nt"&gt;-r&lt;/span&gt; lcov &lt;span class="nt"&gt;-r&lt;/span&gt; json output/cypress/coverage-final.json output/cypress/coverage-final.json
          tree &lt;span class="nb"&gt;test&lt;/span&gt;/merged
      - name: Run codecov
        run: npx codecov &lt;span class="nt"&gt;--token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;{ secrets.CODECOV_TOKEN &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;/merged/coverage-final.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This workflow first installs the project's dependencies, then runs the test cases, merges the test reports, and finally uploads the test results to Codecov.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--twuUFhE3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z61c3tehrco37jdon143.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--twuUFhE3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z61c3tehrco37jdon143.png" alt="test process of CI" width="880" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5.2 Codecov
&lt;/h2&gt;

&lt;p&gt;In the workflow above, we finally uploaded the code coverage report to the &lt;a href="https://about.codecov.io/"&gt;Codecov platform&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
Codecov.io provides coverage online viewing, PR comment test coverage reporting, and badge generation.&lt;br&gt;
On the Codecov platform we can view the code coverage of each file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9UekR9T7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pr801phml9fn7t32sjg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9UekR9T7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pr801phml9fn7t32sjg1.png" alt="Codecov page" width="880" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the Github PR page, after running the test workflow, Codecov will comment on the code coverage changes for the current PR.&lt;br&gt;&lt;br&gt;
The comments will show which file's coverage has changed by how much.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---UQHvnT5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2mhz1ryef5po48sjv28q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---UQHvnT5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2mhz1ryef5po48sjv28q.png" alt="codecov comment" width="880" height="1036"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Codecov.io can also generate a badge showing the current code coverage of the repository. &lt;br&gt;
We open Codecov's settings and copy the badge's link to the repository's README.md.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FqJFXByg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gedih71n2rninva6b7ca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FqJFXByg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gedih71n2rninva6b7ca.png" alt="badge url" width="880" height="451"&gt;&lt;/a&gt;&lt;br&gt;
Finally, we get a badge like this. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nKGLuT3p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wpr99bk03t5egcstxy58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nKGLuT3p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wpr99bk03t5egcstxy58.png" alt="coverage badge" width="880" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  6 Summary
&lt;/h1&gt;

&lt;p&gt;The testing solution of the Semi Design used Jest + Enzyme in the early stage. As the project iterated, we found that it could not meet our testing needs. After comparing the popular E2E testing frameworks in the community, we chose Cypress, which can supplement the scenarios that Jest cannot test and further improve our testing scope and code coverage.  &lt;/p&gt;

&lt;p&gt;The two tools have their own usage scenarios, and they can be used in combination to test the component library in the project.  &lt;/p&gt;

&lt;p&gt;Ultimately, Semi achieved ~90% line coverage with Jest and Cypress. In addition to this, we also visual testing the UI with Chromatic to avoid unexpected UI changes to components.&lt;br&gt;
Going forward, in addition to testing the interaction and UI of the component, the performance of the component is also an issue that we need to pay attention to.  &lt;/p&gt;

&lt;p&gt;In the future, Semi will also add tests related to component performance, so that maintainers can perceive the performance loss caused by component changes and avoid major performance problems during component iteration.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webtesting</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Accessibility in Semi Design</title>
      <dc:creator>Semi Design</dc:creator>
      <pubDate>Wed, 22 Jun 2022 09:07:43 +0000</pubDate>
      <link>https://dev.to/semidesign/accessibility-in-semi-design-pff</link>
      <guid>https://dev.to/semidesign/accessibility-in-semi-design-pff</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TL;DR&lt;br&gt;
This article mainly introduces &lt;strong&gt;how to support accessibility as a website designer&lt;/strong&gt; and &lt;strong&gt;the accessibility design done by Semi Design&lt;/strong&gt;, the estimated reading time is 15–20 min&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://semi.design"&gt;Semi Design&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;em&gt;An open source, modern, comprehensive, flexible design system and React UI library. Connect design &amp;amp; dev workflow.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;🎨 &lt;em&gt;Provide more than 2,300+ Design Tokens and &lt;a href="https://semi.design/dsm"&gt;powerful DSM tools&lt;/a&gt; help you build your own design system. 🌟 You can make Semi Design to Any Design.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;Any front-end open source UI component library that does not support accessibility is a disaster.   -- From a Zhihu user&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Semi design has received extensive attention and comments from industry insiders since it was open-sourced, and the comment above caught our attention.&lt;/p&gt;

&lt;p&gt;Based on the population reported by the Sixth National Population Census, the proportion of disabled persons and the proportion of different types of disabled persons reported by the Second National Sample Survey on Disability, it is estimated that there are 85.02 million disabled persons in China by the end of 2010, of which about 12.63 million are visually impaired. It means that if accessible design is not considered part of the design system, our design system may exclude this large group of population.&lt;/p&gt;

&lt;p&gt;As designers, we have the responsibility to create products easily accessible to everyone. That's why Semi launched its A11y project to remove barriers and create inclusive product experiences that work for everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is A11y?
&lt;/h2&gt;

&lt;p&gt;A11y stands for Accessibility, or accessible design. Although it is generally considered for people with disabilities, accessible design is actually about providing an equal experience for all. Because each of us may have moments of disability. For example, you cannot use your eyes 👀 temporarily because you just have myopia surgery. Or you cannot see clearly when being exposed to strong light. These are situational disabilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--luI_JaFz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l03cihemy94szer0ldi0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--luI_JaFz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l03cihemy94szer0ldi0.png" alt="Types of Disability (image source: Microsoft Inclusive Design)" width="733" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Therefore, the ultimate goal of accessible design is to make the Internet accessible to all regardless of their background, ability, or situation. Here's something you might want to know: The second Thursday of November is &lt;a href="https://worldusabilityday.org/"&gt;World Usability Day&lt;/a&gt;. On this day, events occur around the world that bring together different communities to celebrate how people from all walks of life can make our world easy for all.&lt;br&gt;
Now that we understand what A11y is, what should we do next?&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand user needs
&lt;/h2&gt;

&lt;p&gt;Designing and developing inclusive products starts with understanding the different needs of different users and their assistive devices. At present, the main types of impairment are visual impairment, hearing impairment, physical impairment and cognitive impairment. Below is what we need to pay attention to when people with each type of impairment use products.&lt;/p&gt;

&lt;h3&gt;
  
  
  👩‍🦯 Visual impairment
&lt;/h3&gt;

&lt;p&gt;There are three categories of visually impaired people: blind people, people with low vision, and colorblind people. Colorblind users and people with low vision experience similar barriers while accessing websites. You can see the world in the eyes of people with color vision impairments through &lt;a href="https://www.color-blindness.com/coblis-color-blindness-simulator/"&gt;Coblis, the color-blind emulator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z_DCsrmV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o19yh5weo1silipm2yek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z_DCsrmV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o19yh5weo1silipm2yek.png" alt="Special keyboard for blind users (image source: Baidu)" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blind users&lt;/strong&gt; rely on screen readers to access websites, often navigating pages by browsing specific elements such as titles, links, or forms. It should be noted that such users generally do not use a mouse. Instead, they use a specially designed keyboard to access websites.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Low vision users&lt;/strong&gt; have different needs depending on the nature of their visual impairment. Some users cannot distinguish text or other content without magnification, especially small text; some users have difficulty distinguishing text and images with low color contrast; some users may not be able to distinguish colors (the same problem is encountered by colorblind users). These users typically use screen amplifiers, contrast enhancement or color inversion software, and assistive tools such as screen readers to access websites.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧏 Hearing impairment
&lt;/h3&gt;

&lt;p&gt;Hearing-impaired users can access websites with the help of technological products such as hearing aids or cochlear implants. As a result, accessible design does not pay much attention to hearing-impaired users compared with other types of users with disabilities, but it should be noted that when we use media tools such as videos, subtitles should be added to improve the access experience of hearing-impaired users. Subtitles can also help non-native speakers.&lt;br&gt;
In addition, we need to avoid unnecessary autoplay, and give users the option to turn off the sound in case that it causes disturbance.&lt;/p&gt;

&lt;h3&gt;
  
  
  👩‍🦽Physical impairment
&lt;/h3&gt;

&lt;p&gt;Users whose mouse no longer works or who suffer a broken arm, and those with chronic medical conditions such as repetitive stress injury (RSI) should limit or avoid mouse use,&lt;br&gt;
They use only the keyboard to navigate websites, usually relying on the Tab key and other keys to navigate between interactive elements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cP5UIC27--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eoyt56dxlbl1e94rk4tv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cP5UIC27--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eoyt56dxlbl1e94rk4tv.png" alt="A man in a wheelchair is typing with a mouth stick (image source: W3C)" width="880" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Cognitive impairment
&lt;/h3&gt;

&lt;p&gt;Users with cognitive impairment have difficulty concentrating on long, dense texts and understanding complex structures.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Yqf9Tl9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d29rm14z2e1t6zswn9f4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Yqf9Tl9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d29rm14z2e1t6zswn9f4.png" alt="Dense texts (image source: W3C)" width="880" height="494"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8rzt9bZh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/by29xw843flr5ge4b0f4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8rzt9bZh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/by29xw843flr5ge4b0f4.png" alt="Users reading painfully (image source: W3C)" width="880" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;After understanding the needs of users, we need to discuss what to do. After reading the Web Content Accessibility Guidelines (WCAG), we decided to complete Semi Design's A11y from the following aspects.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎨 Color
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Contrast
&lt;/h4&gt;

&lt;p&gt;Contrast is a very important aspect of accessibility. High contrast makes visual elements stand out from the background. The contrast we are talking about here mainly refers to the contrast of text elements and the contrast at the component level.&lt;/p&gt;

&lt;h5&gt;
  
  
  1.1 Contrast of text elements
&lt;/h5&gt;

&lt;p&gt;According to the WCAG, the contrast ratio between texts (including texts within components) and background color should be at least 4.5:1. The requirement can be lowered to 3:1 for texts of 18px or larger. &lt;strong&gt;Disabled text is not subject to the contrast requirements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KPOQ_elu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hmits8u49m2zlmogkk48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KPOQ_elu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hmits8u49m2zlmogkk48.png" alt="good case &amp;amp; bad case" width="880" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  1.2 Component status and comparison
&lt;/h5&gt;

&lt;p&gt;All operable components need to have a focus . The active, hover, and focus status of components all need to have a contrast ratio of at least 3:1 with adjacent colors. But there is no requirement for contrast ratio between different status.&lt;br&gt;
For components with strokes, only a 3:1 contrast ratio  is required between the stroke color and the base color. There is no requirement for contrast ratio between the fill color and the stroke color.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e69G1qiB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ui6e64aylvua7j76b6pn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e69G1qiB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ui6e64aylvua7j76b6pn.png" alt="Component status and comparison" width="880" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  1.3 Exceptions
&lt;/h5&gt;

&lt;p&gt;Reading-oriented components can be accessed by disabled users through screen readers such as Message, Banner, etc., and therefore do not need to strictly follow the AA standard. But in order to ensure operability, the actionable items within the components still need to meet the contrast ratio requirements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jbkp7Jn5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z65kbnzhi2k501m3tbhl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jbkp7Jn5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z65kbnzhi2k501m3tbhl.png" alt="Exceptions" width="880" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  1.4 A11y theme package and related plugins
&lt;/h5&gt;

&lt;p&gt;In order to better support accessibility, we launched the &lt;strong&gt;A11y theme package&lt;/strong&gt;. It should be noted that the contrast ratio of the orange hue has always been a complicated issue under the WACG 2.0 standard. Because any color with more than 50% yellow will naturally reflect more light. High-risk colors such as yellow, orange, green, and teal should be used with caution when accessibility is considered.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We support changing themes, and in order to ensure that all colors can be covered when changing themes, this version of theme package has not removed the orange hue.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eqEYtuAu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pg9kz03eo1mv395g3air.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eqEYtuAu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pg9kz03eo1mv395g3air.png" alt="Theme store illustration" width="880" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For dark mode, &lt;strong&gt;avoid using solid black&lt;/strong&gt; as the primary background color. This is because placing white text on a solid black background will create halos that make reading difficult. It can be more difficult when it comes to large text or when the user suffers from astigmatism.&lt;br&gt;
Therefore, although the contrast of solid black is high, &lt;code&gt;text-0&lt;/code&gt; and &lt;code&gt;bg-0&lt;/code&gt; in Semi Dark mode are light gray text on a dark gray background, which also increases readability while ensuring high contrast.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Halo: The effect caused by light propagating beyond its boundaries, forming fog around the edges of a bright image&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uVBkFviF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/efbii7gf9goyvpi6y2n2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uVBkFviF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/efbii7gf9goyvpi6y2n2.png" alt="Black and gray contrast" width="880" height="866"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition to the A11y theme pack, visually impaired users are also recommended to use the chrome extension &lt;a href="https://chrome.google.com/webstore/detail/high-contrast/djcfdncoelnlbldjfhinnjlhdjlikmph"&gt;High Contrast Mode&lt;/a&gt; to access websites. This extension provides a variety of modes that support increasing contrast, gray scale, inverting colors, etc. to meet the needs of all types of visually impaired users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E9I_9Wqr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zc3cch643iqhstefnyjj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E9I_9Wqr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zc3cch643iqhstefnyjj.gif" alt="high contrast mode" width="880" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Don't use color as the only visual cue
&lt;/h4&gt;

&lt;p&gt;When you're sending important message or feedback information, don't use color as the only way to convey information. Users with low vision or color blindness may have difficulty understanding what you are trying to say.&lt;br&gt;
Try adding icons, text, underscores, etc. to make sure everyone receives the same information. A typical example is error message prompts for forms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k466R52w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jzi1mai4z3xaenu7whha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k466R52w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jzi1mai4z3xaenu7whha.png" alt="Error message for forms" width="880" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding textures and labels to charts can also help users distinguish elements within them. Despite that they add distractions to data analysts and therefore do not meet the data analysis standard, textures help improve accessibility.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---z80duSL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3z2jl9cez3fjis9119ui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---z80duSL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3z2jl9cez3fjis9119ui.png" alt="Textures and labels in charts" width="880" height="750"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ⌨️keyboard and focus
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Keyboard shortcuts
&lt;/h4&gt;

&lt;p&gt;This type of users can browse websites through keyboard shortcuts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Switch focus with the Tab key&lt;/strong&gt; : The focus-switching order by pressing the Tab key should be predictable and follow a particular pattern, such as: top to bottom, left to right. When some key elements are focused, the prompt information should be displayed; after losing focus, the prompt should disappear;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arrows&lt;/strong&gt;: Navigate between radio buttons, menu items, or widget items;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enter&lt;/strong&gt;: activate button, submit form, etc.;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Space&lt;/strong&gt;: Activate button, select switch, etc.;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Esc&lt;/strong&gt;: Exit pop-up layers;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2.Focus
&lt;/h4&gt;

&lt;p&gt;The most important thing about using the keyboard tab to navigate through web interactive elements is their focus state, which allows keyboard users to know where the focus is. Therefore, &lt;strong&gt;Semi Design has designed focus states for each interoperable component&lt;/strong&gt;. In order to ensure that users who rely solely on keyboard can have the same browsing experience as ordinary users,&lt;br&gt;
The focus of Semi components needs to follow the following principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Initial Focus&lt;/strong&gt;: To enable users to complete tasks, always set an initial focus for tasks. When the focus is switched, if the current focus control is covered, the focus needs to automatically switch to the first focus area of the new page. When setting the initial focus, note:

&lt;ul&gt;
&lt;li&gt;The initial focus is normally set to the first logical interaction element or the first element in the task;&lt;/li&gt;
&lt;li&gt;When the task involves the last step of an irreversible process such as deleting data, it's best to set the initial focus on the least destructive interactive element, e.g., the close button;&lt;/li&gt;
&lt;li&gt;If the task is reading or continuing the current process, it is recommended to set the initial focus on the most likely used interaction element, e.g., the OK button.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigation reversible&lt;/strong&gt;: if a user can switch to the next focus through the Tab key,  he should be able to switch back to the previous focus through Shift + Tab;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus return to previous position&lt;/strong&gt;: If the currently focused element disappears, the focus should return to the previous position. For example, closing a modal might mean that your focus is on the close button; when the modal is closed, focus should be returned to the button that opens the modal.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3.Example
&lt;/h4&gt;

&lt;p&gt;Detailed keyboard and focus instructions are provided for every component of Semi. You can go to Semi's website to experience it for yourself. The following is an example of Popconfirm keyboard interaction:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oiPl6v_L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://sf16-sg.tiktokcdn.com/obj/eden-sg/slepweh7nupqpognuhbo/p14.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oiPl6v_L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://sf16-sg.tiktokcdn.com/obj/eden-sg/slepweh7nupqpognuhbo/p14.gif" alt="popconfirm example" width="880" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🗝 Semantics
&lt;/h3&gt;

&lt;p&gt;Native elements have built-in semantics in the browser (e.g., buttons have a built-in role of button). But for custom components, &lt;strong&gt;Semi uses ARIA (Accessible Rich Internet Applications) to add semantic information&lt;/strong&gt; to help screen reader users better interact with the component.&lt;br&gt;
&lt;a href="https://www.w3.org/TR/wai-aria-1.1/#introstates"&gt;ARIA&lt;/a&gt; supports the following auxiliary features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let users know what the focused element is and what it does.&lt;/li&gt;
&lt;li&gt;Let users know its current status; whether it can be selected; whether it is disabled; whether there is a pop-up layer, etc.;&lt;/li&gt;
&lt;li&gt;Let users know the number of items in the list, and the whereabouts of the focused element.
Semi provides semantic information for all of its components. Coupled with keyboard interaction, it allows people with disabilities to access websites more smoothly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🎬 Media
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Image alternative text
&lt;/h4&gt;

&lt;p&gt;People with low vision access websites with the help of screen readers. When screen readers read an image that is not processed, a visually impaired user can only hear that there is an image here. But there is no way for him to know the message conveyed by it. What's worse, he might only be able to hear the file name of this image.&lt;br&gt;
To avoid the above-mentioned situation, we provide alternative text for image elements. Semi uses the &lt;code&gt;alt&lt;/code&gt; attribute to interpret  Image-type components such as avatars.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vTFE2BKJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pzsnhxb2nkcfi49ifvlz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vTFE2BKJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pzsnhxb2nkcfi49ifvlz.png" alt="alt text usage" width="880" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Video and audio
&lt;/h4&gt;

&lt;p&gt;Provide subtitles for audio or video content, and screen readers can read the text aloud, so that hearing-impaired users can have good access to such information.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎇 Animation
&lt;/h3&gt;

&lt;p&gt;When producing animations or videos, flashing content should be avoided. &lt;strong&gt;Nothing on the screen page should flash more than 3 times per second&lt;/strong&gt;, as it may trigger seizures in some users. Unless the flashing area is small and the contrast of the content is low and does not contain too much redness.&lt;br&gt;
I wonder if you have heard of such a case. In the 38th episode Electric Soldier Porygon of the Japanese cartoon Pokémon (1997), Pikachu used electric shocks to destroy missiles. After the missile exploded, there were unusually bright and flashing red and blue alternating images, inducing epileptic seizures in nearly 700 children.&lt;br&gt;
As content designers, we are obligated to ensure the security of the content we produce.&lt;/p&gt;

&lt;h3&gt;
  
  
  📐 Layout
&lt;/h3&gt;

&lt;p&gt;Reasonable and concise layout allows users to easily explore the content. When designing the interface, designers should ensure that&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Information is clear, concise and easy to navigate;&lt;/li&gt;
&lt;li&gt;Content is divided into short, relevant sections, and long paragraphs are avoided for the sake of visual hierarchies;&lt;/li&gt;
&lt;li&gt;The browsing order of the overall layout is linear and consistent;&lt;/li&gt;
&lt;li&gt;It's still readable at a 200% amplifier.
They are helpful for users who rely on screen readers, screen amplifiers, and users with cognitive impairments without losing information or functionality.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use
&lt;/h2&gt;

&lt;p&gt;The above introduces some of Semi's efforts in accessible design. What else can designers do for accessibility in daily work?&lt;br&gt;
Designers and engineers can compare whether their products meet the accessibility standards aforementioned. Here are a few tips and precautions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verifying that color is not the only information
&lt;/h3&gt;

&lt;p&gt;If you want to verify whether your design meets the standard of "color is not the only visual cue", you can use the chrome plugin - &lt;a href="https://chrome.google.com/webstore/detail/high-contrast/djcfdncoelnlbldjfhinnjlhdjlikmph?hl=zh-CN"&gt;High Contrast Mode&lt;/a&gt;, select the mode to [Inverse Gray] to change the interface to a de-colored one, and then test whether you can accurately obtain all the information in that case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Page focus order
&lt;/h3&gt;

&lt;p&gt;Page-level focus order is important for the overall keyboard interaction to work smoothly. You can use Figma to insert plugin &lt;a href="https://www.figma.com/community/plugin/731310036968334777/A11y---Focus-Orderer"&gt;A11y - Focus Orderer&lt;/a&gt; to mark the focus order. After your marking, engineers can realize your demands through tabIndex.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zgMhhRQv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fxf4an3ci04t3bjgyp95.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zgMhhRQv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fxf4an3ci04t3bjgyp95.png" alt="focus orderer usage" width="880" height="723"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Focus processing of controlled components
&lt;/h3&gt;

&lt;p&gt;For some Semi controlled components like Popover, the focus will be lost after the user closes Popover through &lt;code&gt;esc&lt;/code&gt; as Semi cannot confirm the trigger mode of the trigger of the controlled component. It requires the developer to manually handle it and switch the focus to the next interactive element.&lt;/p&gt;

&lt;h3&gt;
  
  
  Image and icon semantic annotation
&lt;/h3&gt;

&lt;p&gt;When your design includes images or icons, use the &lt;code&gt;alt&lt;/code&gt; tag to replace the text. Of course, if your designed buttons contain icons and labels, you do not need to add the alt attribute, otherwise the screen reader will broadcast the same information twice, which will disturb concentration and do harm to user experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RczxdY7v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kd5uardh00a1ysdeqirm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RczxdY7v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kd5uardh00a1ysdeqirm.png" alt="use alt attribute" width="880" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Use of the screen reader
&lt;/h3&gt;

&lt;p&gt;When you are using some screen readers, do you find them a little difficult to get started?Here are some tips for using the narrator of the screen reader that comes with your Mac computer.&lt;br&gt;
Open [System Preferences], click [Accessibility], select [Voice], and check [Enable Voiceover] . You can experience this function, and you can also click [Open Voiceover Training...] to learn how to use it initially.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0YQbg5YY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qixwqu8rqhu8ndks8pwi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0YQbg5YY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qixwqu8rqhu8ndks8pwi.gif" alt="screen reader on MacOS" width="880" height="812"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click [VoiceOver Utility] can also customize some narration preferences, such as the voice of the broadcast, etc.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YsrPbWTL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0srcjakxygc6iu3qkchd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YsrPbWTL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0srcjakxygc6iu3qkchd.png" alt="VoiceOver Utility" width="880" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some shortcut keys for everyone to use quickly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;operation&lt;/th&gt;
&lt;th&gt;shortcut keys&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Open / Close VoiceOver&lt;/td&gt;
&lt;td&gt;Command + F5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read the title and have the narration jump through the title text of the page&lt;/td&gt;
&lt;td&gt;Control + Option + Command + H&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigating a web page (eg: when you want to read the text under the title after focusing on the title)&lt;/td&gt;
&lt;td&gt;Control + Option + Right Arrow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Adjust the narration reading rate&lt;/td&gt;
&lt;td&gt;Control + Option + Command + Down Arrow / Up Arrow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Start narration on the current page&lt;/td&gt;
&lt;td&gt;Control + Option + Shift + Down Arrow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browse through all the navigation items on the web page and open the rotor&lt;/td&gt;
&lt;td&gt;Control + Option + U&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  In closing
&lt;/h2&gt;

&lt;p&gt;There are some limitations of WCAG 2.0, such as the orange hue mentioned above. &lt;strong&gt;We will continue the iterative optimization to WCAG 3.0&lt;/strong&gt;, and simultaneously update the contrast standard in the DSM editor and documents.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The new color contrast standard &lt;a href="https://github.com/Myndex/SAPC-APCA/blob/master/documentation/WhyAPCA.md#why-the-new-contrast-method-apca"&gt;APCA&lt;/a&gt; optimizes the ratio of the 2.0 version to a numerical value, and the higher the value, the higher the contrast. The new standard takes into account font-size and font-weight, contrast changes after text and background colors are exchanged, and most importantly, addresses the issue of tonal perception, so that the long-standing orange hue problem is solved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, I would like to say that we still have a lot to learn about accessible design. And the Semi team will continue to work on design for accessibility. At the same time, I hope we could work together and bring accessible design to more users, providing them a better work and life experience while maximizing the benefit of technology.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>designsystem</category>
      <category>ux</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
