Table of Contents
IntroductionSteve 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 DevelopmentSteve 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 CourseSteve 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 TestsSteve 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 VitestSteve 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 UISteve 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 ExerciseStudents are instructed to complete the test implementations in the exercise.tests.ts file. Use `npm test` to run the tests.
Unit Testing SolutionSteve 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 TestsSteve 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 ExerciseStudents 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 SolutionSteve 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 ActionsSteve 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 UISteve 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 RulesSteve 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 JobsSteve 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 DependenciesSteve 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 ConfigurationSteve 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 OverviewSteve 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 ConfigurationSteve 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 EnvironmentsSteve 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 DOMSteve 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 EventsSteve 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 & SolutionStudents 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 ExerciseStudents 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 FieldSteve 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 ButtonSteve 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 ListSteve 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 IsolationSteve 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 ScopeSteve 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 AxSteve 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 CoverageSteve introduces code coverage and runs a coverage report which displays the percentage of tested code in every source file in the application
Vitest Code Coverage ConfigurationSteve 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 ActionsSteve adds a step to the GitHub action to run a code coverage report.
Generating Coverage Report ArtifactSteve 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 MocksSteve 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 MethodsSteve 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 LibrariesSteve 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 TimeSteve 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 RequestsSteve 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 OverviewSteve introduces Playwright which enables end-to-end testing in web applications.
Playwright SetupSteve 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 ConfigurationSteve briefly overviews the Playwright configuration options in the playwright.config.ts file.
Writing Playwright TestsSteve 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 TestsSteve 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 ScreenshotsSteve 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 PlaywrightSteve 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.
Writing Custom ESLint RulesSteve 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-StagedSteve 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&ASteve answers questions about getting team buy-in for more comprehensive testing, component testing, building API transition layers.