From a4a3f5e55724643bcf9c55992f1da78e45a00ed6 Mon Sep 17 00:00:00 2001 From: Adrien Guinet Date: Wed, 6 Nov 2024 16:32:41 +0100 Subject: [PATCH 1/4] Add post linaria jest --- _data/authors.yml | 4 + _posts/2024-10-25-linaria-and-jest.md | 392 ++++++++++++++++++++++++++ images/avatar/a_guinet.jpg | Bin 0 -> 9318 bytes 3 files changed, 396 insertions(+) create mode 100644 _posts/2024-10-25-linaria-and-jest.md create mode 100644 images/avatar/a_guinet.jpg diff --git a/_data/authors.yml b/_data/authors.yml index 1a902466b..b4e70f477 100644 --- a/_data/authors.yml +++ b/_data/authors.yml @@ -21,6 +21,10 @@ a_caron: a_ferez: name: Amélie Ferez avatar: /images/avatar/a_ferez.jpg +a_guinet: + name: Adrien Guinet + avatar: /images/avatar/a_guinet.jpg + url: https://github.com/Esquimor a_martins: name: Alexis Martins a_pitel: diff --git a/_posts/2024-10-25-linaria-and-jest.md b/_posts/2024-10-25-linaria-and-jest.md new file mode 100644 index 000000000..c6c84cb17 --- /dev/null +++ b/_posts/2024-10-25-linaria-and-jest.md @@ -0,0 +1,392 @@ +--- +layout: post +title: Use Jest with Linaria in a React App +description: How do we make Linaria and Wyw-in-js coexist with Jest +author: [a_guinet] +tags: [test, css, frontend, react, linaria, jest] +color: rgb(251,87,66) +language: en +--- + +## Introduction +At our company, we are in the process of migrating to Linaria (v6.2.0) for managing CSS in our React application. Our codebase is over 10 years old and features a significant number of tests created by various developers over the years. + +Throughout this migration, we encountered a few challenges, particularly with our testing framework. In this article, I will outline our experience and the solutions we implemented. + +## What is Linaria? +Linaria is a zero-runtime CSS-in-JS library designed for JavaScript applications, specifically React in our case. It allows us to write CSS code alongside our JavaScript, effectively managing component styling and ensuring everything works smoothly within our large application. + +## Introducing Wyw-in-js +Starting with Linaria version 6.0.0, a new tool called Wyw-in-js has been introduced to manage the build process. This tool centralizes all configuration settings, making it easier to manage styles. + +## Why Jest? +Jest is a popular testing framework within the React community, widely used for its simplicity and powerful features. + +## The Challenge +We decided to migrate from styled-components to Linaria to reduce our bundle size. By compiling CSS, we can generate a more lightweight version of our bundle. However, this approach presents a significant challenge: if the CSS is not generated to the client, we cannot use it in our tests. + +This becomes problematic when we need to test the visibility of an element using .not.toBeVisible(). For example, we have a CSS class that sets an element's display to none. While Jest can recognize that the element is not visible, the absence of the compiled CSS means that it only knows the class name without any associated CSS code. + +## Understanding Wyw-in-js +Wyw-in-js operates during the compilation process, specifically with Webpack, to generate CSS. It parses JavaScript files that contain styles and transforms them into CSS files. + +To achieve this, Wyw-in-js stores all files in your computer's memory, then reads and processes them accordingly. It also provides flexibility for developers to modify this behavior. Our solution is to create intermediate files that we can use to read and manage the CSS for our application, enabling us to utilize it in our tests. + +## Practical Setup +### Setting Up a React Project with Linaria +Create a New Directory: + +```bash +mkdir react-linaria-jest +cd react-linaria-jest +``` +Add package.json for Dependency Management: Create a package.json file with the following content: + +```json +{ + "name": "wyw-js-test", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "webpack serve --mode development --open", + "build": "webpack --mode production", + "test": "jest" + }, + "dependencies": { + "@linaria/core": "^6.2.0", + "@linaria/react": "^6.2.1", + "@wyw-in-js/babel-preset": "^0.5.4", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@babel/core": "^7.25.8", + "@babel/preset-env": "^7.25.8", + "@babel/preset-react": "^7.25.7", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.5.0", + "@testing-library/react": "^16.0.1", + "@wyw-in-js/webpack-loader": "^0.5.4", + "babel-jest": "^29.7.0", + "babel-loader": "^9.2.1", + "css-loader": "^7.1.2", + "html-webpack-plugin": "^5.6.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "jest-transform-css": "^6.0.1", + "mini-css-extract-plugin": "^2.9.1", + "style-loader": "^4.0.0", + "webpack": "^5.95.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.1.0" + } +} +``` +Install Dependencies: Run the following command to install the dependencies: + +```bash +pnpm install +``` +Create the Application: Create a file named src/index.js with the following content: + +```javascript +// src/index.js +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import App from './App'; + +const domNode = document.getElementById('root'); +const root = createRoot(domNode); +root.render(); +``` +Create the App Component: Create the main component in src/App.jsx: + +```javascript +// src/App.jsx +import React from 'react'; +import { styled } from '@linaria/react'; + +const Title = styled.h1` + color: red; +`; + +const Link = styled.a` + color: blue; + display: none; // This element is not visible +`; + +const App = () => { + return ( +
+ Hello! + Link +
+ ); +}; + +export default App; +``` +Create an HTML File: Create a basic HTML file for your application in public/index.html: + +```html + + + + + + Linaria and Jest + + +
+ + +``` +Configure Webpack: Create a webpack.config.js file with the following content: + +```javascript +// webpack.config.js +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); + +module.exports = { + entry: path.join(__dirname, "src", "index.js"), + output: { + path: path.resolve(__dirname, "dist"), + }, + resolve: { + extensions: ['.js', '.jsx'] + }, + plugins: [ + new HtmlWebpackPlugin({ + template: path.join(__dirname, "public", "index.html"), + }), + new MiniCssExtractPlugin(), + ], + module: { + rules: [ + { + test: /\.?(js|jsx)$/, + exclude: /node_modules/, + use: [ + { + loader: "babel-loader", + options: { + presets: ['@babel/preset-env', '@babel/preset-react'], + } + }, + { + loader: '@wyw-in-js/webpack-loader' + } + ] + }, + { + test: /\.css$/, + use: [ + MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { + esModule: true, + modules: { + namedExport: true, + localIdentName: '[local]' + }, + } + } + ], + }, + ] + }, + devServer: { + port: 3000, + }, +}; +``` +Add Wyw-in-js Configuration: Create a configuration file named wyw-in-js.config.js: + +```javascript +// wyw-in-js.config.js +module.exports = { + evaluate: true, + displayName: process.env.APP_ENV !== 'prod', + classNameSlug: '[title]', +}; +``` +Add Babel Configuration: Create a configuration file named babel.config.js: + +```javascript +// babel.config.js +module.exports = { + "presets": [["@babel/preset-env", {targets: {node: "current"}}], "@babel/preset-react", "@wyw-in-js"] +} +``` +And add the last config, Jest Configuration: Create a configuration file named babel.config.js: + +```javascript +// jest.config.js +module.exports = { + "testEnvironment": "jsdom", + transform: { + '^.+\\.(jsx?|tsx?|js)$': 'babel-jest', + }, +}; +``` +### Running Your Application +Launch your application with the command: + +```bash +pnpm start +``` +You should see a page displaying just the title. + +### The Test +We need to ensure that Linaria and Wyw-in-js cannot handle styles on their own. Let's write a simple test. + +Create a new file named src/App.test.js with the following content: + +```javascript +// src/App.test.js +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import App from './App'; +import '@testing-library/jest-dom'; + +test('should render app', () => { + render(); + expect(screen.getByRole('heading')).toHaveTextContent('Hello!'); + expect(screen.getByTestId('link')).not.toBeVisible(); +}); +``` +You can run this test with: + +```bash +pnpm test +``` +You may encounter an issue where the ‘link’ element is visible. + +## Solution +In your webpack.config.js, ensure you have added the Wyw-in-js loader: + +```javascript +{ + loader: '@wyw-in-js/webpack-loader' +} +``` +This loader can accept options, one of which is cache-provider. This option allows you to modify how files are cached for reuse in the CSS generation. + +Now, let's create a custom cache provider to generate temporary files. + +### Implementing the Cache Provider +Create a new file named cacheProvider.js in your project directory: + +```javascript +// cacheProvider.js +const fs = require('fs'); // to read and write on file +const { hash } = require('node:crypto'); + +const BASE_PATH = '.cache/wyw-in-js'; // path to store temporary files + +class CacheFileForWyw { + // Generate a file name to store CSS + static getPathFromKey(key) { + return hash('md5', key, 'hex'); + } + + get(key) { + const keyPath = CacheFileForWyw.getPathFromKey(key); + if (fs.existsSync(`${BASE_PATH}/${keyPath}.css`)) { + return fs.readFileSync(`${BASE_PATH}/${keyPath}.css`, { encoding: 'utf-8' }); + } + return Promise.reject(); + } + + set(key, value) { + if (!fs.existsSync(BASE_PATH)) { + fs.mkdirSync(BASE_PATH, { recursive: true }); + } + const keyPath = CacheFileForWyw.getPathFromKey(key); + fs.writeFileSync(`${BASE_PATH}/${keyPath}.css`, value); + return Promise.resolve(); + } +} + +const cacheProvider = new CacheFileForWyw(); +module.exports = cacheProvider; +``` +Next, integrate the custom cache provider with Webpack: + +```javascript +{ + loader: '@wyw-in-js/webpack-loader', + options: { cacheProvider: path.resolve(__dirname, 'cacheProvider.js') } +} +``` +### Custom Render Function for Tests +To ensure Jest utilizes the generated CSS, we will create a custom render function. + +Create src/render.js with the following content: + +```javascript +// src/render.js +import { render as rtlRender } from '@testing-library/react'; +const fs = require('node:fs'); + +const BASE_PATH = '.cache/wyw-in-js'; + +const getCSS = () => { + const filesStyle = fs.readdirSync(`./${BASE_PATH}/`); + return filesStyle.reduce((acc, file) => acc + fs.readFileSync(`./${BASE_PATH}/${file}`, { encoding: 'utf-8' }), ''); +}; + +function render(ui, options = {}) { + const view = rtlRender(ui, options); + const concatFiles = getCSS(); + + const styleElement = document.createElement('style'); + styleElement.innerHTML = concatFiles.toString(); + document.body.appendChild(styleElement); + document.body.appendChild(view.container); + return view; +} + +export * from '@testing-library/react'; +export { render }; +``` +Update your test file (src/App.test.js) to import the custom render function: + +```javascript +import { render, screen } from './render.js'; +``` +Finally, run a build with: + +```bash +pnpm build +``` +Rerun your tests: + +```bash +pnpm test +``` +The tests should now pass successfully. + +## A Note on Potential Issues +As mentioned, our setup relies on temporary files generated by Webpack. This introduces the risk of discrepancies between the actual styles and the CSS in these files if a build is not performed. Currently, we have no solution for this issue, and it remains an area for future improvement. + +## Final Note about Linaria +Do you need to use this trick to make Linaria work with Jest? +Sadly, yes. + +This workaround, along with a few other issues, made us reconsider Linaria’s fit for our project. Here are the main challenges we faced: + +- Outdated Documentation: Key parts of Linaria's documentation were outdated or incomplete, requiring us to dig through code and experiment extensively to find solutions. +- Small Community and Limited Support: The community around Linaria is still relatively small, with limited articles, resources, and tutorials available. The Discord server is inactive, making it difficult to find support or share experiences with other developers. +- Unresolved GitHub Issues: We observed several issues on Linaria’s GitHub repository that had gone months without responses, which raised concerns about maintenance and support going forward. + +Given these red flags, we concluded that Linaria might not be the best fit for our needs. While it offers potential advantages, the lack of support and the necessity of workarounds have prompted us to explore alternative solutions for managing styles in our React application. + +## Conclusion +I hope this article helps you understand how to set up Linaria with Jest for testing in a React application. By creating a custom cache provider and render function, we can ensure that our tests accurately reflect the styles in our components. + +Thank you for reading, and best of luck with your migration! \ No newline at end of file diff --git a/images/avatar/a_guinet.jpg b/images/avatar/a_guinet.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a42e666e90b14cbfe21aff551f632527061e1901 GIT binary patch literal 9318 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 z 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 Date: Thu, 7 Nov 2024 10:52:18 +0100 Subject: [PATCH 2/4] add links --- _posts/2024-10-25-linaria-and-jest.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/_posts/2024-10-25-linaria-and-jest.md b/_posts/2024-10-25-linaria-and-jest.md index c6c84cb17..c9940b6e5 100644 --- a/_posts/2024-10-25-linaria-and-jest.md +++ b/_posts/2024-10-25-linaria-and-jest.md @@ -9,26 +9,26 @@ language: en --- ## Introduction -At our company, we are in the process of migrating to Linaria (v6.2.0) for managing CSS in our React application. Our codebase is over 10 years old and features a significant number of tests created by various developers over the years. +At our company, we are in the process of migrating to [Linaria](https://linaria.dev/) (v6.2.0) for managing CSS in our [React](https://react.dev/) application. Our codebase is over 10 years old and features a significant number of tests created by various developers over the years. Throughout this migration, we encountered a few challenges, particularly with our testing framework. In this article, I will outline our experience and the solutions we implemented. ## What is Linaria? -Linaria is a zero-runtime CSS-in-JS library designed for JavaScript applications, specifically React in our case. It allows us to write CSS code alongside our JavaScript, effectively managing component styling and ensuring everything works smoothly within our large application. +[Linaria](https://linaria.dev/) is a zero-runtime CSS-in-JS library designed for JavaScript applications, specifically React in our case. It allows us to write CSS code alongside our JavaScript, effectively managing component styling and ensuring everything works smoothly within our large application. ## Introducing Wyw-in-js -Starting with Linaria version 6.0.0, a new tool called Wyw-in-js has been introduced to manage the build process. This tool centralizes all configuration settings, making it easier to manage styles. +Starting with Linaria version 6.0.0, a new tool called [Wyw-in-js](https://wyw-in-js.dev/) has been introduced to manage the build process. This tool centralizes all configuration settings, making it easier to manage styles. ## Why Jest? -Jest is a popular testing framework within the React community, widely used for its simplicity and powerful features. +[Jest](https://jestjs.io/) is a popular testing framework within the React community, widely used for its simplicity and powerful features. ## The Challenge -We decided to migrate from styled-components to Linaria to reduce our bundle size. By compiling CSS, we can generate a more lightweight version of our bundle. However, this approach presents a significant challenge: if the CSS is not generated to the client, we cannot use it in our tests. +We decided to migrate from [styled-components](https://styled-components.com/) to Linaria to reduce our bundle size. By compiling CSS, we can generate a more lightweight version of our bundle. However, this approach presents a significant challenge: if the CSS is not generated to the client, we cannot use it in our tests. -This becomes problematic when we need to test the visibility of an element using .not.toBeVisible(). For example, we have a CSS class that sets an element's display to none. While Jest can recognize that the element is not visible, the absence of the compiled CSS means that it only knows the class name without any associated CSS code. +This becomes problematic when we need to test the visibility of an element using `.not.toBeVisible()`. For example, we have a CSS class that sets an element's display to none. While Jest can recognize that the element is not visible, the absence of the compiled CSS means that it only knows the class name without any associated CSS code. ## Understanding Wyw-in-js -Wyw-in-js operates during the compilation process, specifically with Webpack, to generate CSS. It parses JavaScript files that contain styles and transforms them into CSS files. +Wyw-in-js operates during the compilation process, specifically with [Webpack](https://webpack.js.org/), to generate CSS. It parses JavaScript files that contain styles and transforms them into CSS files. To achieve this, Wyw-in-js stores all files in your computer's memory, then reads and processes them accordingly. It also provides flexibility for developers to modify this behavior. Our solution is to create intermediate files that we can use to read and manage the CSS for our application, enabling us to utilize it in our tests. From 203662f1d4f890aacd087fc72010c6dcf9440fbb Mon Sep 17 00:00:00 2001 From: Adrien Guinet Date: Thu, 7 Nov 2024 12:42:58 +0000 Subject: [PATCH 3/4] improve article --- _posts/2024-10-25-linaria-and-jest.md | 49 +++++++++++++++++++-------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/_posts/2024-10-25-linaria-and-jest.md b/_posts/2024-10-25-linaria-and-jest.md index c9940b6e5..4748c0c9f 100644 --- a/_posts/2024-10-25-linaria-and-jest.md +++ b/_posts/2024-10-25-linaria-and-jest.md @@ -8,26 +8,29 @@ color: rgb(251,87,66) language: en --- -## Introduction -At our company, we are in the process of migrating to [Linaria](https://linaria.dev/) (v6.2.0) for managing CSS in our [React](https://react.dev/) application. Our codebase is over 10 years old and features a significant number of tests created by various developers over the years. +## Overview +At [Bedrock](https://bedrockstreaming.com/), we constantly seek to enhance our technical stack to better serve our clients. One of our ongoing challenges is managing CSS effectively in our web applications. -Throughout this migration, we encountered a few challenges, particularly with our testing framework. In this article, I will outline our experience and the solutions we implemented. +Currently, we use [styled-components](https://styled-components.com/) for CSS-in-JS. While it has served us well, we’ve encountered several limitations: +- **Client-Side Generation**: Styled-components generates CSS on the client side, impacting performance. +- **SSR Limitations**: Server-side rendering (SSR) is limited, which affects our ability to deliver optimized content quickly. +- **Bundle Size**: The tool adds significantly to our bundle size, which we aim to reduce. -## What is Linaria? -[Linaria](https://linaria.dev/) is a zero-runtime CSS-in-JS library designed for JavaScript applications, specifically React in our case. It allows us to write CSS code alongside our JavaScript, effectively managing component styling and ensuring everything works smoothly within our large application. +We explored multiple alternatives to replace styled-components, ultimately selecting [Linaria](https://linaria.dev/) as our preferred solution. -## Introducing Wyw-in-js -Starting with Linaria version 6.0.0, a new tool called [Wyw-in-js](https://wyw-in-js.dev/) has been introduced to manage the build process. This tool centralizes all configuration settings, making it easier to manage styles. +Migrating to Linaria has posed several challenges, especially given our codebase spans over a decade. Testing was one of the major areas impacted. In this article, we’ll share our approach, the challenges we faced, and the solutions we implemented. + +## Tool for testing +For our tests, we use [Jest](https://jestjs.io/). This popular testing framework within the React community is well-regarded for its simplicity and powerful features, making it an ideal choice for our test suite. -## Why Jest? -[Jest](https://jestjs.io/) is a popular testing framework within the React community, widely used for its simplicity and powerful features. +## What is Linaria? +[Linaria](https://linaria.dev/) is a zero-runtime CSS-in-JS library, ideal for JavaScript applications, particularly with [React](https://react.dev/). Linaria allows us to write CSS directly in our JavaScript, providing an efficient way to manage component styling within a large application while aiming to enhance performance. -## The Challenge -We decided to migrate from [styled-components](https://styled-components.com/) to Linaria to reduce our bundle size. By compiling CSS, we can generate a more lightweight version of our bundle. However, this approach presents a significant challenge: if the CSS is not generated to the client, we cannot use it in our tests. +In the sections that follow, we’ll delve into how we set up Linaria with Jest for testing and the workarounds we used to overcome specific integration challenges. -This becomes problematic when we need to test the visibility of an element using `.not.toBeVisible()`. For example, we have a CSS class that sets an element's display to none. While Jest can recognize that the element is not visible, the absence of the compiled CSS means that it only knows the class name without any associated CSS code. +## Introducing Wyw-in-js +Starting with Linaria version 6.0.0, a new tool called [Wyw-in-js](https://wyw-in-js.dev/) has been introduced to manage the build process. This tool centralizes all configuration settings, making it easier to manage styles. -## Understanding Wyw-in-js Wyw-in-js operates during the compilation process, specifically with [Webpack](https://webpack.js.org/), to generate CSS. It parses JavaScript files that contain styles and transforms them into CSS files. To achieve this, Wyw-in-js stores all files in your computer's memory, then reads and processes them accordingly. It also provides flexibility for developers to modify this behavior. Our solution is to create intermediate files that we can use to read and manage the CSS for our application, enabling us to utilize it in our tests. @@ -233,6 +236,9 @@ module.exports = { }, }; ``` + +🎉🎉 Congratulation, our application is configured. 🎉🎉 + ### Running Your Application Launch your application with the command: @@ -241,6 +247,8 @@ pnpm start ``` You should see a page displaying just the title. +🎊 Nice, we have step up all our tools and we are ready to start testing. 🎊 + ### The Test We need to ensure that Linaria and Wyw-in-js cannot handle styles on their own. Let's write a simple test. @@ -264,7 +272,13 @@ You can run this test with: ```bash pnpm test ``` -You may encounter an issue where the ‘link’ element is visible. +Oops, you got a 🔴 message. + +By compiling CSS, we can generate a more lightweight version of our bundle. However, this approach presents a significant challenge: if the CSS is not generated to the client, we cannot use it in our tests. + +This becomes problematic when we need to test the visibility of an element using `.not.toBeVisible()`. For example, we have a CSS class that sets an element's display to none. While Jest can recognize that the element is not visible, the absence of the compiled CSS means that it only knows the class name without any associated CSS code. + +Let's try a solution to handle it. ## Solution In your webpack.config.js, ensure you have added the Wyw-in-js loader: @@ -369,11 +383,16 @@ Rerun your tests: ```bash pnpm test ``` -The tests should now pass successfully. +Houra 🎊🎊 ! Finally, the test works ! ## A Note on Potential Issues As mentioned, our setup relies on temporary files generated by Webpack. This introduces the risk of discrepancies between the actual styles and the CSS in these files if a build is not performed. Currently, we have no solution for this issue, and it remains an area for future improvement. +We have think to others solutions: +- Create a custom [Jest transformer](https://jestjs.io/docs/code-transformation) like in [CSS Module](https://www.npmjs.com/package/jest-css-modules-transform) or [Vanilla Extract](https://www.npmjs.com/package/@vanilla-extract/jest-transform) +- Create a custom class for visibility in global and use it in our component (like that you can check if the class is in our test or not). +- Test the visibility via a functional test + ## Final Note about Linaria Do you need to use this trick to make Linaria work with Jest? Sadly, yes. From d27ebc76b94676e7a003319bb9806d854497b292 Mon Sep 17 00:00:00 2001 From: Adrien Guinet Date: Wed, 13 Nov 2024 13:08:27 +0000 Subject: [PATCH 4/4] review florent --- _posts/2024-10-25-linaria-and-jest.md | 73 +++++++++++++++++++++------ 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/_posts/2024-10-25-linaria-and-jest.md b/_posts/2024-10-25-linaria-and-jest.md index 4748c0c9f..44db56ead 100644 --- a/_posts/2024-10-25-linaria-and-jest.md +++ b/_posts/2024-10-25-linaria-and-jest.md @@ -16,9 +16,9 @@ Currently, we use [styled-components](https://styled-components.com/) for CSS-in - **SSR Limitations**: Server-side rendering (SSR) is limited, which affects our ability to deliver optimized content quickly. - **Bundle Size**: The tool adds significantly to our bundle size, which we aim to reduce. -We explored multiple alternatives to replace styled-components, ultimately selecting [Linaria](https://linaria.dev/) as our preferred solution. +We explored multiple alternatives to replace styled-components, ultimately selecting [Linaria](https://linaria.dev/) as our best candidate as replacement. -Migrating to Linaria has posed several challenges, especially given our codebase spans over a decade. Testing was one of the major areas impacted. In this article, we’ll share our approach, the challenges we faced, and the solutions we implemented. +Our on going migrating to Linaria pose several challenges, especially given our codebase spans over a decade. Testing is one of the major areas impacted. In this article, we’ll share our approach, the challenges we face, and the solutions we implement. ## Tool for testing For our tests, we use [Jest](https://jestjs.io/). This popular testing framework within the React community is well-regarded for its simplicity and powerful features, making it an ideal choice for our test suite. @@ -36,7 +36,11 @@ Wyw-in-js operates during the compilation process, specifically with [Webpack](h To achieve this, Wyw-in-js stores all files in your computer's memory, then reads and processes them accordingly. It also provides flexibility for developers to modify this behavior. Our solution is to create intermediate files that we can use to read and manage the CSS for our application, enabling us to utilize it in our tests. ## Practical Setup -### Setting Up a React Project with Linaria + +In the next part, I setup a basic react application. +So, if you want to focus on Linaria go directly to: [Add Linaria](#add-linaria). + +### Setting Up a React Project Create a New Directory: ```bash @@ -145,6 +149,7 @@ Create an HTML File: Create a basic HTML file for your application in public/ind ``` + Configure Webpack: Create a webpack.config.js file with the following content: ```javascript @@ -178,9 +183,6 @@ module.exports = { options: { presets: ['@babel/preset-env', '@babel/preset-react'], } - }, - { - loader: '@wyw-in-js/webpack-loader' } ] }, @@ -192,10 +194,6 @@ module.exports = { loader: 'css-loader', options: { esModule: true, - modules: { - namedExport: true, - localIdentName: '[local]' - }, } } ], @@ -207,6 +205,51 @@ module.exports = { }, }; ``` + +### Add Linaria + +We need to change a little bit our webpack config: + +```javascript +// webpack.config.js +[...] +module: { + rules: [ + { + test: /\.?(js|jsx)$/, + exclude: /node_modules/, + use: [ + { + loader: "babel-loader", + options: { + presets: ['@babel/preset-env', '@babel/preset-react'], + } + }, + { + loader: '@wyw-in-js/webpack-loader' + } + ] + }, + { + test: /\.css$/, + use: [ + MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { + esModule: true, + modules: { + namedExport: true, + localIdentName: '[local]' // Need to match name generate by wyw-in-js + }, + } + } + ], + }, + ] + }, +[...] +``` Add Wyw-in-js Configuration: Create a configuration file named wyw-in-js.config.js: ```javascript @@ -250,7 +293,7 @@ You should see a page displaying just the title. 🎊 Nice, we have step up all our tools and we are ready to start testing. 🎊 ### The Test -We need to ensure that Linaria and Wyw-in-js cannot handle styles on their own. Let's write a simple test. +So, let check that we got an issue by launching a test with any modification. Create a new file named src/App.test.js with the following content: @@ -389,9 +432,9 @@ Houra 🎊🎊 ! Finally, the test works ! As mentioned, our setup relies on temporary files generated by Webpack. This introduces the risk of discrepancies between the actual styles and the CSS in these files if a build is not performed. Currently, we have no solution for this issue, and it remains an area for future improvement. We have think to others solutions: -- Create a custom [Jest transformer](https://jestjs.io/docs/code-transformation) like in [CSS Module](https://www.npmjs.com/package/jest-css-modules-transform) or [Vanilla Extract](https://www.npmjs.com/package/@vanilla-extract/jest-transform) -- Create a custom class for visibility in global and use it in our component (like that you can check if the class is in our test or not). -- Test the visibility via a functional test +- Create a custom [Jest transformer](https://jestjs.io/docs/code-transformation) like in [CSS Module](https://www.npmjs.com/package/jest-css-modules-transform) or [Vanilla Extract](https://www.npmjs.com/package/@vanilla-extract/jest-transform). But no solution existing yet. +- Create a custom class for visibility in global and use it in our component (like that you can check if the class is in our test or not). Not a very good choice. +- Test the visibility via a functional test. Sometimes it's not possible. ## Final Note about Linaria Do you need to use this trick to make Linaria work with Jest? @@ -408,4 +451,4 @@ Given these red flags, we concluded that Linaria might not be the best fit for o ## Conclusion I hope this article helps you understand how to set up Linaria with Jest for testing in a React application. By creating a custom cache provider and render function, we can ensure that our tests accurately reflect the styles in our components. -Thank you for reading, and best of luck with your migration! \ No newline at end of file +Thank you for reading! \ No newline at end of file