
Enterprise UI Development: Testing & Code Quality
Learning Paths:
Topics:
Table of Contents
Introduction
Introduction
Steve Kinney explains the pitfalls of large codebases and why infrastructure is needed to manage a large codebase supported by a team of contributors.What is Enterprise UI Development
Steve discusses the core questions around enterprise UI development. How do you create maintainable code? What does it mean to be maintainable? Components of a well-architected UI application are also discussed in this lesson.Tools for the Course
Steve explains the tooling used throughout the course. Most of the code examples use React and TypeScript. Testing Library, Vitest, and Playwright will also be used in the course.
The Basics of Unit Testing
Types of Tests
Steve explains the spectrum of tests used throughout an application. These include unit tests, component tests, end-to-end tests, and integration tests.Setup Unit Testing with Vitest
Steve introduces Vitest and provides a basic unit test example and a description of the Vitest syntax. Instructions for running tests in the course repository are provided. A question about using Happy DOM vs. JS DOM is also covered in this lesson.Vitest UI
Steve demonstrates the reporting options available with Vitest. The --reporter flag customizes the level of reporting. The --ui flag opens a user interface that displays tests and their results.Unit Testing Exercise
Students are instructed to complete the test implementations in the exercise.tests.ts file. Use `npm test` to run the tests.Unit Testing Solution
Steve live codes some of the solutions to the Unit Testing exercise. The remaining solutions can be found in the `exercise.solution.test.ts` file.Async & Asymmetric Tests
Steve demonstrates techniques for handling async behavior in unit tests. Asymmetric tests are also shown and are helpful when a value is generated each time, and the test only needs to know if it exists or is a proper type.Asymmetric Matching Exercise
Students are instructed to implement tests to ensure the randomly generated UUIDs exist and are well formed. Default packed status and removal behavior should also be tested.Asymmetric Matching Solution
Steve live codes the solution to the Asymmetric Matching exercise. The full solution can be found in the `items-slice.solution.test.ts` file.
Building a CI Pipeline with Github Actions
GitHub Actions
Steve introduces GitHub actions and outlines a few use cases, like automatically running tests when commits are pushed to the repo. Actions reside in a YAML file inside a workflows folder.GitHub Actions UI
Steve demonstrates the user interface for GitHub actions. When a PR is opened, the status of the action will change from pending to in-progress. Details about each step can be viewed along with any console output.Build Step & Branch Protection Rules
Steve challenges students to add a build step to the GitHub action. Branch protection rules are also explained, and the configuration settings are explored.Running Multiple Jobs
Steve demonstrates how to run multiple jobs in parallel by creating a separate jobs object in the actions YAML file. If jobs depend on each other, the "needs" property can be used to specify the dependency's name.Caching Dependencies
Steve explains that GitHub actions can store action dependencies with caching or artifacts. Caches are pinned to a specific version and have a unique ID to access them from other jobs, and they can be hashed using the package.lock file to ensure uniqueness. The GitHub UI will indicate when a cache is accessed and provides a way to remove caches manually.Cache Configuration
Steve demonstrates a shortcut for caching npm packages. Conditionals can also be used in job steps to determine if a cache is available. Running GitHub actions locally is also discussed in this lesson.
Component Testing
Component Testing Overview
Steve introduces component testing and describes the value they add to a developer's workflow. These tests ensure application updates do not break UI implementation and test user interactions without requiring browser-based integration testing.Component Testing Configuration
Steve shares a brief overview of the component testing configuration used in the course repo. The testing suite jsdom can be configured for specific files or folders using globs in the vitest.config.ts file. Differences between jsdom and happy-dom are also discussed in this lesson.Using Vitest Environments
Steve configures a component test environment to use happy-dom and runs an initial test to demonstrate the DOM integration by logging results in the console.Interacting with the DOM
Steve explores the screen and fireEvent methods from Testing Library. The screen method provides APIs for querying the DOM and selecting elements based on different conditions. The fireEvent method dispatches common DOM events like click or change.Abstracting Rendering & User Events
Steve introduces the user-event package from Testing Library, which provides more accurate user event behavior. Duplicating the user setup and rendering in each test can become redundant, so an abstracted render method is created in the utilities file, and the test is refactored.Counter Exercise & Solution
Students are instructed to implement component tests to validate the correct initial count is displayed and that the count is reset when the reset button is clicked.Testing Project Exercise
Students are instructed to add component tests to the Packing List application. Tests include verifying there is an input field for adding new items, verifying the "Add New Items" button is disabled or enabled at the correct time, and verifying new items are added to the page.Testing Project: Input Field
Steve codes part of the solution to the Testing Project exercise. The tests written verify there is an input field for adding new items.Testing Project: Disabled & Enabled Button
Steve continues coding the solution to the Testing Project exercise. The tests written verify the "Add New Item" button is disabled when no input is present and is enabled when text is entered into the input.Testing Project: Item in List
Steve continues coding the solution to the Testing Project exercise. The tests written verify items are added to the "Unpacked Items List" when the "Add New Item" button is clicked. Testing the removal of an item is also included in this lesson.Test Isolation
Steve discusses ways to isolate tests so their implementation does not affect other tests in the application. A question about handling async behavior or situations where animations must complete before a test can continue is also discussed in this lesson.Test Scope
Steve refactors the Packing List application to demonstrate further how to scope the resources required to run tests. This creates more modular tests and increases a test's encapsulation.
Accessibility Testing & Code Coverage
Automated Accessibility Testing with Ax
Steve discusses the importance of automating accessibility tests and introduces the aXe testing library. Accessibility violations will cause tests to fail and a descriptive list of ways to solve the violation is presented in the console.Code Coverage
Steve introduces code coverage and runs a coverage report which displays the percentage of tested code in every source file in the applicationVitest Code Coverage Configuration
Steve walks through the coverage configuration options in the vitest.config.ts file. Thresholds and included/excluded file paths can be configured along with the reporter which allows the reports to be imported into other systems.Generating Artifacts Using GitHub Actions
Steve adds a step to the GitHub action to run a code coverage report.Generating Coverage Report Artifact
Steve walks through the GitHub UI for the action-generated coverage report. The output resides as an artifact and can be downloaded as a ZIP file in the summary section.
Mocking & Spying
Creating Mocks
Steve demonstrates how mocks allow developers to test code they don't control without requiring the necessary functions or APIs to be called. This is useful when the external function or API isn't available or would result in increased fees for usage. Mocks can be passed a custom implementation so mocked return values can be used later in the test.Spying on Methods
Steve explains that spies are similar to mocks except they watch a single method of an object. An alias can be used to call the underlying method. The method can also be added to tests to ensure it's called correctly.Mocking Third-Party Libraries
Steve shares a use case for mocking and creating custom implementations for third-party libraries. Questions about mocking server-side APIs are also discussed in this lesson.Mocking Time
Steve uses the useFakeTimers and setSystemTime methods to mock the system time for a test. These methods freeze the system time and allow it to be set to any date-based value to make testing easier and more predictable.Mocking API Requests
Steve introduces the Mock Service Worker library, which allows developers to mock API requests by intercepting requests on the network level with a service worker. The mock definitions can be seamlessly reused for testing, development, and debugging.
Integration Testing with Playwright
Playwright Overview
Steve introduces Playwright which enables end-to-end testing in web applications.Playwright Setup
Steve clones a new repo and installs Playwright. The Playwright tests will run in multiple browser environments and provide test summary reports for each. The Playwright VSCode extension is also introduced in this lesson.Playwright Configuration
Steve briefly overviews the Playwright configuration options in the playwright.config.ts file.Writing Playwright Tests
Steve writes a basic Playwright test that navigates to a URL, types text in an input field, and clicks on one of the results. End-to-end tests like this add confidence in UI behavior and can often eliminate the need for multiple component or unit tests.Recording Tests
Steve demonstrates Playwright's ability to record user interactions in the browser and generate the Playwright test code. This can be done with the VSCode plugin or by launching the Playwright recording UI from the command line.Creating Screenshots
Steve generates screenshots with Playwright. Screenshots can produce failing tests if the visual difference exceeds a threshold. They are helpful when refactoring a large code base because they ensure the UI remains the same.Mocking APIs with Playwright
Steve records API requests with Playwright. The requests are saved to an HTTP archive (HAR) file. The HAR file can be used to mock future responses to network requests.
Enforcing Standards
Writing Custom ESLint Rules
Steve creates custom ESLint rules with AST Explorer. The custom rules can be added to a project to help developers adhere to custom coding standards.Husky & Lint-Staged
Steve introduces Husky, which is a command-line tool for generating pre-commit scripts. These scripts can catch linting or other code issues when code is committed versus waiting for a GitHub action to run.Enforcing Standards Q&A
Steve answers questions about getting team buy-in for more comprehensive testing, component testing, building API transition layers.