# Javascript automated testing ## Differences Between Unit, Integration, and Automated Testing ### 1. Unit Testing - **Purpose**: Tests individual components or functions in isolation. - **Scope**: Small, focused on one "unit" of code, like a single function or method. - **Goal**: Ensure that specific sections of code behave as expected. - **Tools**: Jest, Mocha, JUnit, etc. - **Example**: Testing if a function correctly calculates a discount based on input values. ### 2. Integration Testing - **Purpose**: Tests how different modules or components of the application work together. - **Scope**: Medium, covering multiple units that interact with each other (e.g., database and API). - **Goal**: Ensure components interact properly when combined. - **Tools**: Mocha, Cypress (with a focus on integration), Postman for API tests, etc. - **Example**: Testing if the user login service works correctly when the frontend sends the request and the backend processes it. ### 3. Automated Testing - **Purpose**: Any test that is executed automatically rather than manually. - **Scope**: Broad, covering both unit and integration tests, but also extending to things like **UI testing**, **end-to-end testing**, and **performance testing**. - **Goal**: Increase testing efficiency by automating repetitive tasks. - **Tools**: Selenium, Cypress, Puppeteer, etc. - **Example**: Running tests to ensure that the whole app’s login flow works properly on different browsers. ### Key Differences - **Unit testing** focuses on small pieces of code. - **Integration testing** ensures multiple components work together. - **Automated testing** refers to how tests are run, not a specific kind of test. <br/> <br/> ## Two Ways to Write Tests When writing tests, there are generally two approaches: **code-first** and **test-first**. Each has its own benefits depending on the situation and development style. Here's a breakdown of both approaches and when they are most suitable. ### 1. **Code-First Approach** In the **code-first** approach, you write the actual code for your application or feature first, and then create tests afterward to validate that the code works as expected. #### **When to Use Code-First:** - **When Exploring New Features**: If you’re still figuring out how a feature should work, starting with the code can be helpful. This allows you to test and iterate quickly. - **For Prototyping**: If you’re building a prototype or proof of concept where speed is essential, writing code first helps move quickly. Tests can be added later once the functionality is more stable. - **Simple or Small Projects**: For small-scale projects where code is straightforward, writing the tests afterward can make more sense because there might be fewer edge cases to consider. #### **Advantages**: - Fast development with a focus on functionality first. - Great when building features where requirements are still evolving. #### **Disadvantages**: - Tests may be biased toward confirming what’s already been written, missing potential edge cases. - Refactoring the code might lead to tests needing to be rewritten or heavily modified. ### 2. **Test-First Approach (TDD - Test-Driven Development)** In the **test-first** approach, also known as **Test-Driven Development (TDD)**, you write the tests before the actual implementation of the code. The idea is to write a failing test, write the code to pass the test, and then refactor the code while keeping tests green (passing). #### **When to Use Test-First (TDD):** - **For Critical Systems**: When building highly reliable, bug-free systems (e.g., banking software, healthcare apps), writing tests first ensures that every part of the code is verified from the start. - **Complex Business Logic**: For code that has intricate logic and many edge cases, writing tests first ensures that you cover various scenarios before implementing the actual functionality. - **When Refactoring Legacy Code**: TDD can help ensure that you don’t introduce bugs during the refactoring process. Writing tests first gives you confidence that the existing functionality remains intact. #### **Advantages**: - Forces you to think through requirements and edge cases upfront. - Leads to better, more robust code with higher test coverage. - Helps prevent over-engineering by making you focus only on the required functionality. #### **Disadvantages**: - Slower initial development since you write tests before the actual code. - Can be difficult to apply if you're still figuring out the system design. --- ### **Which Approach is Best for Which Situation?** - **Use Code-First** when: - The feature or project is small, and you want to focus on fast <br/> <br/> ## Unit testing ### Unit Testing: When It's Useful and When It's Not #### When Unit Testing is Useful 1. **Isolated Functionality Testing**: - Ensures individual functions or components work as expected. - Useful when you need to validate logic in isolation (e.g., utility functions, mathematical calculations). 2. **Catch Bugs Early**: - Since it targets specific units of code, bugs can be identified and fixed early in the development process. 3. **Refactoring and Code Changes**: - Helps ensure that small code changes or refactoring don't break existing functionality. - It provides a safety net when making modifications. 4. **Documentation**: - Unit tests act as documentation by showing how a function or module is supposed to behave with various inputs. 5. **Faster Debugging**: - Unit tests are fast to run and help identify the exact spot in the code where something goes wrong. #### When Unit Testing is Not Useful 1. **Complex Integration Scenarios**: - Unit tests only check isolated pieces of code, so they don't help ensure that different components work well together. - Integration and end-to-end tests are needed to cover interactions between multiple systems. 2. **UI and User Experience**: - Unit tests can't validate user interfaces, user flows, or the overall user experience. - For this, automated **UI testing** or **manual testing** is better suited. 3. **Performance or Load Testing**: - Unit tests don't provide insights into how the application performs under heavy loads or stress. - **Performance testing** tools (like JMeter) should be used for this. 4. **Real-World Scenarios**: - Unit tests focus on individual units, often using mocks or stubs, and don't simulate real-world behavior (e.g., real API responses, actual databases). - **Integration tests** or **end-to-end tests** handle more realistic scenarios. 5. **Time-consuming for Simple Projects**: - For very small or simple projects, the time spent writing unit tests may not be worth the effort if the scope is minimal. ### Vitest vs Jest: Which Should You Use? #### 1. **Jest** - **Pros**: - **Mature and well-documented**: Jest is a widely used testing framework with a large community and excellent documentation. - **Feature-rich**: Built-in features like mocking, snapshots, and code coverage. - **Great for React**: Jest is commonly used with React apps and integrates well with testing libraries like React Testing Library. - **Wide Ecosystem**: Plugins and integrations for a variety of JavaScript/TypeScript tools. - **Parallel Testing**: Jest runs tests in parallel by default, speeding up test execution in larger projects. - **Cons**: - **Performance**: Jest can be slower, especially with large projects or when used in combination with bundlers like Vite. - **Heavy for Simple Projects**: For small, simple projects, the overhead of Jest can be unnecessary. #### 2. **Vitest** - **Pros**: - **Optimized for Vite**: Vitest is designed to work seamlessly with Vite, which results in faster test execution, especially for Vite-based projects. - **Faster**: Vitest’s speed is often superior to Jest, especially in development environments because it uses Vite’s fast module bundling. - **Hot Module Replacement (HMR) Support**: Vitest supports HMR during test runs, meaning you can update your tests in real time without re-running them. - **Lighter Setup**: Vitest is a great option for those who want a lightweight testing framework in a Vite-powered project. - **Cons**: - **Smaller Ecosystem**: Vitest’s ecosystem and documentation are still growing, so you might find fewer resources and integrations compared to Jest. - **Less Mature**: Vitest is newer, so it may not have all the features that Jest offers (though it is catching up fast). #### **When to Use Jest**: - If you’re working on a **large project** with lots of features or multiple testing needs. - If you need built-in support for **snapshot testing**, **mocks**, or **global setups**. - If you’re using **React** and want the more common ecosystem choice. #### **When to Use Vitest**: - If you’re using **Vite** as your build tool, Vitest’s performance advantage makes it a natural choice. - If you want **faster test execution** during development. - If you need a **lightweight** testing solution for a Vite-based project. #### **Conclusion**: - Choose **Jest** for larger, more complex applications or when you need comprehensive testing features and support. - Choose **Vitest** if you’re using Vite and want a faster, more integrated testing experience. ### I chose Vitest I chose **Vitest** to start my testing journey because it integrates seamlessly with **Vite**, which I already use to create React apps. Setting up Jest with **native JavaScript modules (ESM)** can be tricky since ESM support is still experimental in Jest, making configuration more complicated. Vitest, on the other hand, is a modern testing framework designed specifically for today’s JavaScript ecosystem. It supports ESM, TypeScript, and JSX out of the box, which makes it much easier to set up and start testing. Plus, Vitest has an API almost identical to Jest, meaning that what I learn from Vitest will also apply to Jest later on. It offers all the fantastic features of Jest but with better support for modern tooling. ### Example of unitesting with Vitest download vitest into dependency: ``` npm i -d vitest ``` add following to the scripts of package.json: ** "test": "vitest" ** Example functions src/intro.js ```tsx // Lesson: Writing your first tests export function max(a, b) { return a > b ? a : b; } // Exercise export function fizzBuzz(n) { if (n % 3 === 0 && n % 5 === 0) return "FizzBuzz"; if (n % 3 === 0) return "Fizz"; if (n % 5 === 0) return "Buzz"; return n.toString(); } ``` Lets test the functions test/intro.test.js ```tsx import { describe, it, expect } from "vitest"; import { fizzBuzz, max } from "../src/intro"; describe("max", () => { it("should return the first argument if it is greater than the second", () => { expect(max(2, 1)).toBe(2); }); it("should return the second argument if it is greater than the first", () => { expect(max(1, 2)).toBe(2); }); it("should return the first argument if they are equal", () => { expect(max(1, 1)).toBe(1); }); }); describe("fizzBuzz", () => { it("should return 'FizzBuzz' if n is divisible by 3 and 5", () => { expect(fizzBuzz(15)).toBe("FizzBuzz"); }); it("should return 'Fizz' if n is divisible by 3", () => { expect(fizzBuzz(9)).toBe("Fizz"); }); it("should return 'Buzz' if n is divisible by 5", () => { expect(fizzBuzz(20)).toBe("Buzz"); }); }); ``` <br/> <br/> *** <br/> <br/> # React testing using Vitest ## Setting up the testing environment **Testing a single page react application** download vitest into dependency: ``` npm i -d vitest ``` Added this to the script ``` "test": "vitest", "test:ui": "vitest --ui" ``` added this extension for usefull snippets ![image](https://hackmd.io/_uploads/rJ19eMiJyx.png) **Add a test folder and and a file like this *.test.ts ** for example, tests/main.test.ts: ```tsx import { it, expect, describe } from "vitest"; describe("group", () => { it("should", () => { expect(1).toBeTruthy(); }); }); ``` **Start the test with or without UI** without UI ``` npm t ``` 2. With UI ``` npm run test:ui ``` Press yes to install @vitest/ui ![image](https://hackmd.io/_uploads/Sk58IenyJg.png) After installing run **npm run test:ui** again **UI** ![image](https://hackmd.io/_uploads/r1V38ghy1x.png) **Install react testing library** `@testing-library/react` is a popular library for testing React components. It provides utilities to interact with the UI elements in a way that mimics how users would interact with them. ``` npm i -D @testing-library/react@14.2.0 ``` **The next library we need, is jsdom** `jsdom` is a JavaScript library that simulates a web browser's environment, allowing developers to run tests for web applications without needing a real browser. It creates a DOM (Document Object Model) in Node.js, enabling you to manipulate and query HTML elements as you would in a browser. Use `happy-dom` instead of `jsdom` when: 1. **Performance**: You need faster tests for larger applications. 2. **Lightweight**: You want a smaller footprint and minimal setup. 3. **Limited Features**: Your testing doesn't require extensive browser APIs. 4. **Modern Compatibility**: You seek seamless integration with modern frameworks. In short, opt for `happy-dom` for speed and simplicity, while use `jsdom` for complex features. **happy-dom doesent provide all the ui elements we need, so we are going with jsdom and switching to happy-dom if needed** ``` npm i -D jsdom@24.0.0 ``` **Create vitest.config.ts file in the root** ````tsx import {defineConfig} from 'vitest/config' export default defineConfig({ test: { environment: 'jsdom', } }) ```` Whenever we change config we need to start vitest again. **Run again** ```` npm run test:ui ```` **Last library we need** @testing-library/jest-dom `@testing-library/jest-dom` is an extension for Jest that provides custom matchers for asserting on DOM nodes. For example we can check to see if element is in the DOM, or wheather it has the right content and so on. ```` @testing-library/jest-dom ```` ### Make vitest functions global so we dont have to import the **import {it, expect, describe} from "vitest"** every time. 1. go to vitest.config.ts 2. add "globals: true" in test object. 3. go to tsconfig.json, add a new property in compilerOptions: ""types": [vitest/globals]" 4. reload the project again if it doesent work. ### Make Vitest's testing capabilities with additional matchers from the jest-dom library global. 1. Make a file **tests\setup.ts** and paste **import "@testing-library/jest-dom/vitest";** into the file. (use **iv** snippet to get the import { it, expect, describe } from 'vitest') 2. go to vitest.config.ts add "setupFiles: 'tests/setup.ts'" in test object. 4. Run the test again. <br> <br> ## Go to testing-library.com to learn about queries https://testing-library.com/docs/queries/about/ **We need to use react testing library query to find the heading in the dom and make an assertion against it** **assertion** refers to a statement that verifies whether a specific condition is true or false in your tests. Assertions are crucial because they allow you to validate the behavior of your components, ensuring they render correctly and respond as expected to user interactions. ![image](https://hackmd.io/_uploads/BkL3wbhJke.png) <br> <br> ## Code Snippets - Use **gt** to get all the methods for quyring by text ![image](https://hackmd.io/_uploads/HkTpFw6J1x.png) - use **itr** snippet to get the **import { render, screen } from '@testing-library/react'** - use **d** snippet to get the **describe('group', () => {})** <br> <br> ## Testing components ### Testing UserAccount.tsx src\components\UserAccount.tsx ````tsx import { User } from "../entities"; const UserAccount = ({ user }: { user: User }) => { return ( <> <h2>User Profile</h2> {user.isAdmin && <button>Edit</button>} <div> <strong>Name:</strong> {user.name} </div> </> ); }; export default UserAccount; ```` tests\components\UserAccount.test.tsx - use **itr** snippet to get the **import { render, screen } from '@testing-library/react'** - use **d** snippet to get the **describe('group', () => {})** ````tsx import { User } from "@auth0/auth0-react"; import { render, screen } from "@testing-library/react"; import UserAccount from "../../src/components/UserAccount"; // Test suite for the "UserAccount" component describe("UserAccount", () => { // Test case: ensures that the username is rendered in the component it("should render username", () => { // Define a user object with an id and a name const user: User = { id: 1, name: "Mosh", // The name to be displayed }; // Render the "UserAccount" component with the user prop render(<UserAccount user={user} />); // Check that the user's name is rendered in the document expect(screen.getByText(user.name)).toBeInTheDocument(); }); // Test case: ensures the "Edit" button is rendered when the user is an admin it("should render edit button if user is admin", () => { // Render the "UserAccount" component with a user who is an admin render(<UserAccount user={{ id: 1, name: "Mosh", isAdmin: true }} />); // Select the button element by its role (assuming there's a button with the "button" role) const button = screen.getByRole("button"); // Check that the button is present in the document expect(button).toBeInTheDocument(); // Ensure that the button contains the text "Edit" (case-insensitive match using regex) expect(button).toHaveTextContent(/edit/i); }); // Test case: ensures the "Edit" button is NOT rendered when the user is not an admin it("should not render edit button if user is not admin", () => { // Render the "UserAccount" component with a user who is not an admin render(<UserAccount user={{ id: 1, name: "Mosh" }} />); // Try to get the button element (will return null if no button exists) const button = screen.queryByRole("button"); // Check that the button is not present in the document expect(button).not.toBeInTheDocument(); }); }); ```` ### Testing UserList.tsx** src\components\UserList.tsx **Testing UserList.tsx** ```tsx import { User } from "../entities"; const UserList = ({ users }: { users: User[] }) => { if (users.length === 0) return <p>No users available.</p>; return ( <ul> {users.map((user) => ( <li key={user.id}> <a href={`/users/${user.id}`}>{user.name}</a> </li> ))} </ul> ); }; export default UserList; ```` **Create a file:** tests\components\UserList.test.tsx - use **itr** snippet to get the **import { render, screen } from '@testing-library/react'** - use **d** snippet to get the **describe('group', () => {})** ````tsx import { render, screen } from "@testing-library/react"; import UserList from "../../src/components/UserList"; import { User } from "../../src/entities"; describe("UserList", () => { it("should render no users when the users array is empty", () => { render(<UserList users={[]} />); //paragraphs dont have a role by default, so we get the text content by getByText query //use a general keyword that will propably not change given the contexta expect(screen.getByText(/no users available/i)).toBeInTheDocument(); }); it("should render a list of users", () => { const users: User[] = [ { id: 1, name: "Mosh", }, { id: 2, name: "John", }, ]; render(<UserList users={users} />); users.forEach((user) => { // Query the DOM for a <a> (link) element that has an accessible name matching the user's name // The accessible name is usually the text content of the link const link = screen.getByRole("link", { name: user.name }); // Check that the link element has an "href" attribute pointing to the correct URL // The URL should include the user's ID, following the pattern "/users/{user.id}" expect(link).toHaveAttribute("href", `/users/${user.id}`); }); }); }); ```` ### Test ProductImageGallery.tsx src\components\ProductImageGallery.tsx ````tsx const ProductImageGallery = ({ imageUrls }: { imageUrls: string[] }) => { if (imageUrls.length === 0) return null; return ( <ul> {imageUrls.map((url) => ( <li key={url}> <img src={url} /> </li> ))} </ul> ); }; export default ProductImageGallery; ```` - use **itr** snippet to get the **import { render, screen } from '@testing-library/react'** - use **d** snippet to get the **describe('group', () => {})** tests\components\ProductImageGallery.test.tsx ````tsx // Test suite for the "ProductImageGallery" component describe("group", () => { // Test case: ensures the component renders nothing if no images are provided it("should render nothing if there are no images", () => { // Render the "ProductImageGallery" component with an empty array for imageUrls // Destructure the result object to grab the "container" property, which holds the DOM structure const { container } = render(<ProductImageGallery imageUrls={[]} />); // Assert that the "container" (root of the rendered component) is an empty DOM element expect(container).toBeEmptyDOMElement(); }); // Test case: ensures the component renders a list of images when image URLs are provided it("should render a list of images", () => { // Define an array of image URLs const imageUrls = ["url1", "url2", "url3"]; // Render the "ProductImageGallery" component with the imageUrls prop render(<ProductImageGallery imageUrls={imageUrls} />); // Query the DOM for all elements with the role of "img" (image elements) const images = screen.getAllByRole("img"); // Assert that the number of image elements matches the length of the imageUrls array expect(images).toHaveLength(3); // Loop over the imageUrls array and check if each image element has the correct "src" attribute imageUrls.forEach((url, index) => { // Assert that the image at the current index has the expected "src" attribute (matching the URL) expect(images[index]).toHaveAttribute("src", url); ```` ### Testing a component with user interaction src\components\TermsAndConditions.tsx ````tsx import { useState } from "react"; const TermsAndConditions = () => { const [isChecked, setIsChecked] = useState(false); return ( <div> <h1>Terms & Conditions</h1> <p> Lorem ipsum dolor, sit amet consectetur adipisicing elit. Autem, delectus. </p> <div className="pb-3"> <label htmlFor="agree"> <input type="checkbox" id="agree" checked={isChecked} onChange={() => setIsChecked(!isChecked)} className="mr-1" /> I accept the terms and conditions. </label> </div> <button disabled={!isChecked} className="btn"> Submit </button> </div> ); }; export default TermsAndConditions; ```` Note that here we have elements that render based on user interaction **We need to install a special library in order simulate user event.** install ```` npm install --save-dev @testing-library/user-event ```` add this to enable the **userEvent** **import userEvent from "@testing-library/user-event";** Check this website for information about user events. https://testing-library.com/docs/user-event/intro tests\components\TermsAndConditions.test.tsx - use **itr** snippet to get the **import { render, screen } from '@testing-library/react'** - use **d** snippet to get the **describe('group', () => {})** - Use **gt** to get all the methods for quyring by text ````tsx import { render, screen } from "@testing-library/react"; import TermsAndConditions from "../../src/components/TermsAndConditions"; import userEvent from "@testing-library/user-event"; describe("TermsAndConditions", () => { it("should render", () => { render(<TermsAndConditions />); const heading = screen.getByRole("heading"); expect(heading).toBeInTheDocument(); expect(heading).toHaveTextContent(/terms\s*(and|&)\s*conditions/i); const checkbox = screen.getByRole("checkbox"); expect(checkbox).toBeInTheDocument(); expect(checkbox).not.toBeChecked(); // if we have multiple buttons, we can use the filter object to get it // screen.getByRole("button", { name: /submit/i}); const button = screen.getByRole("button"); expect(button).toBeInTheDocument(); //testing the content of the button is not important, because it might change //and it is not important. What is important, is the functionality. //SO LETS NOT TEST THIS expect(button).toHaveTextContent(/submit/i); expect(button).toBeDisabled(); }); it("should enable button when checkbox is checked", async () => { //Arrenge render(<TermsAndConditions />); //Act const checkbox = screen.getByRole("checkbox"); const user = userEvent.setup(); await user.click(checkbox); //Assert expect(screen.getByRole("button")).toBeEnabled(); }); }); ```` ### Testing ExpandableText.tsx src\components\ExpandableText.tsx ````tsx import { useState } from "react"; const ExpandableText = ({ text }: { text: string }) => { const limit = 255; const [isExpanded, setExpanded] = useState(false); if (text.length <= limit) return <article>{text}</article>; return ( <div> {isExpanded ? ( <article>{text}</article> ) : ( <article>{text.substring(0, limit)}...</article> )} <button className="btn" onClick={() => setExpanded(!isExpanded)}> {isExpanded ? "Show Less" : "Show More"} </button> </div> ); }; export default ExpandableText; ```` tests\components\ExpandableText.test.tsx - use **itr** snippet to get the **import { render, screen } from '@testing-library/react'** - use **d** snippet to get the **describe('group', () => {})** tests\components\ExpandableText.test.tsx ````tsx import { render, screen } from "@testing-library/react"; import ExpandableText from "../../src/components/ExpandableText"; import userEvent from "@testing-library/user-event"; import exp from "constants"; describe("ExpandableText", () => { const limit = 255; const text = "Hello World!"; const longText = "a".repeat(limit + 1); const truncatedText = longText.substring(0, limit) + "..."; it("should render the full text without button, when the text is less than the limit characters", () => { //Arrenge render(<ExpandableText text={text} />); //Act const paragraph = screen.getByText(text); //Assert expect(paragraph).toBeInTheDocument(); }); it("should render the full text if the text is more than the limit", () => { //Arrenge render(<ExpandableText text={longText} />); //Act expect(screen.getByText(truncatedText)).toBeInTheDocument(); const button = screen.getByRole("button"); //Checking if the element is in the document is pointless if we are checking weather it has // /more/i in its content. // SO NO NEED FOR CHECKING THIS expect(button).toBeInTheDocument(); expect(button).toHaveTextContent(/more/i); }); it("should expand text when SHOW MORE button is clicked", async () => { //Arrenge render(<ExpandableText text={longText} />); const button = screen.getByRole("button"); const user = userEvent.setup(); //Act await user.click(button); //Assert expect(screen.getByText(longText)).toBeInTheDocument(); expect(button).toHaveTextContent(/less/i); }); it("should collapse text when SHOW LESS button is clicked", async () => { //Arrenge render(<ExpandableText text={longText} />); const showMoreButton = screen.getByRole("button", { name: /more/i }); const user = userEvent.setup(); await user.click(showMoreButton); //Act const showLessButton = screen.getByRole("button", { name: /less/i }); await user.click(showLessButton); //Assert expect(screen.getByText(truncatedText)).toBeInTheDocument(); expect(showMoreButton).toHaveTextContent(/more/i); }); }); ```` <br/> <br/> ### Simplifying tests **Changed the TermsAndConditions.tsx file from this:** ````tsx import { render, screen } from "@testing-library/react"; import TermsAndConditions from "../../src/components/TermsAndConditions"; import userEvent from "@testing-library/user-event"; describe("TermsAndConditions", () => { it("should render", () => { render(<TermsAndConditions />); const heading = screen.getByRole("heading"); expect(heading).toBeInTheDocument(); expect(heading).toHaveTextContent(/terms\s*(and|&)\s*conditions/i); const checkbox = screen.getByRole("checkbox"); expect(checkbox).toBeInTheDocument(); expect(checkbox).not.toBeChecked(); // if we have multiple buttons, we can use the filter object to get it // screen.getByRole("button", { name: /submit/i}); const button = screen.getByRole("button"); expect(button).toBeInTheDocument(); //testing the content of the button is not important, because it might change //and it is not important. What is important, is the functionality. //SO LETS NOT TEST THIS expect(button).toHaveTextContent(/submit/i); expect(button).toBeDisabled(); }); it("should enable button when checkbox is checked", async () => { //Arrenge render(<TermsAndConditions />); //Act const checkbox = screen.getByRole("checkbox"); const user = userEvent.setup(); await user.click(checkbox); //Assert expect(screen.getByRole("button")).toBeEnabled(); }); }); ```` We refactored the test to: 1. **Extract common logic**: Created a `renderComponent` function to avoid duplicating rendering code and querying for the main elements. 2. **Remove unnecessary checks**: Removed `toBeInTheDocument()` since `getByRole()` already verifies the element exists. 3. **Focus on key assertions**: Kept only relevant checks, like heading text, checkbox state, and button functionality, without testing irrelevant details like button text. This makes the test more readable and maintainable. **Changed to this** ````tsx import { render, screen } from "@testing-library/react"; import TermsAndConditions from "../../src/components/TermsAndConditions"; import userEvent from "@testing-library/user-event"; describe("TermsAndConditions", () => { const renderComponent = () => { render(<TermsAndConditions />); return { heading: screen.getByRole("heading"), checkbox: screen.getByRole("checkbox"), button: screen.getByRole("button"), }; }; it("should render", () => { // If the element is not in the document, we will get an error //We no longer need to use toBeInTheDocument() function to see weather they are in the DOM const { heading, checkbox, button } = renderComponent(); expect(heading).toHaveTextContent(/terms\s*(and|&)\s*conditions/i); expect(checkbox).not.toBeChecked(); expect(button).toBeDisabled(); }); it("should enable button when checkbox is checked", async () => { const { checkbox, button } = renderComponent(); //Act const user = userEvent.setup(); await user.click(checkbox); //Assert expect(button).toBeEnabled(); }); }); ```` <br/> <br/> *** <br/> <br/> # ESLint https://eslint.org/ **ESLint is a popular open-source linting tool for JavaScript and TypeScript that helps developers find and fix problems in their code, such as syntax errors, potential bugs, and deviations from coding standards. It enforces consistent coding practices by analyzing the code based on configurable rules.** ### Key features: 1. **Error Detection**: Identifies issues like undefined variables, unused variables, and incorrect types. 2. **Code Style Enforcement**: Helps enforce code style preferences (like indentation, spacing, etc.) based on custom or predefined configurations (e.g., Airbnb, Google style guides). 3. **Customizable**: You can define your own set of rules or extend popular configurations. 4. **Pluggable**: Supports plugins for frameworks and libraries (like React, Vue, etc.) to handle specific use cases. ### Prerequisites for Using ESLint: 1. **Node.js and npm**: - ESLint requires Node.js (usually version 12 or higher) and npm (Node's package manager) to be installed on your machine. You’ll use npm to install ESLint. 2. **JavaScript/TypeScript project**: - ESLint is designed to work with JavaScript and TypeScript codebases, so you'll need an existing project or be ready to create one. 3. **Basic knowledge of JavaScript/TypeScript**: - Understanding the language is necessary for configuring ESLint rules and resolving the issues it highlights. 4. **Package manager**: - You’ll need a package manager like npm or Yarn to install ESLint and its dependencies. 5. **Editor or IDE support** (optional but useful): - Most modern editors (like VS Code) support ESLint via plugins, making it easier to see linting errors directly in the code editor. Once these prerequisites are in place, you can initialize and configure ESLint in your project. ### Steps to Install ESLint: 1. **Install Node.js and npm**: - Make sure you have Node.js and npm installed on your system. You can download and install them from the [official Node.js website](https://nodejs.org/). 2. **Navigate to your project directory**: - Open your terminal and navigate to your project's root directory. ```bash cd /path/to/your/project ``` 3. **Download and configure the ESLint in the project** ``` npm init @eslint/config@latest ``` 4. **ESLint is going to give as** ![image](https://hackmd.io/_uploads/HkCF9Aiy1g.png) ### Recommendation - **Choose "Syntax and Problem Finding"**: It improves code quality and reduces bugs, making it ideal for most development scenarios. 5. **What type of modules does your project use** ![image](https://hackmd.io/_uploads/BJkgRCjkkx.png) #### Recommendation - Always choose **JavaScript Modules (ES Modules)** for React development to ensure compatibility, cleaner code, and better performance. Choose CommonJS if you use older version of node. Choose None of these if you are using simple javascript files. 6. **Choose other setups** ![image](https://hackmd.io/_uploads/B1diJ13Jkx.png) ![image](https://hackmd.io/_uploads/SyUaJ1nyJe.png) use a space bar to select the following ![image](https://hackmd.io/_uploads/ryByxJhyJl.png) ![image](https://hackmd.io/_uploads/ryEel1h1Jg.png) ![image](https://hackmd.io/_uploads/SkRbxJ2k1e.png) 7. **Add this to the package.json** ![image](https://hackmd.io/_uploads/ryUpxkh1ye.png) 8. **npm run lint** ``` npm run lint ``` ![image](https://hackmd.io/_uploads/H1hvP1n11g.png) <br> <br>