Skip to content

chore: fix more typos #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion exercises/01.sunsetting-jsdom/FINISHED.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Sunsetting JSDOM

Great job! 🎉 Take some rest and let's continue.
Great job! 🎉 Have some rest and let's continue.
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ Once you do all this, you will spot that the `*.css` imports fail to resolve in
Cannot find module './src/index.css' or its corresponding type declarations.ts(2307)
```

🐨 To fix this, we need to pull some of the Vite built-in type definitions to help us. At the top of the `vitest.browser.setup.ts` file, add the following type reference comment:
🐨 To fix this, we need to pull in some of the Vite built-in type definitions to help us. At the top of the `vitest.browser.setup.ts` file, add the following type reference comment:

```ts nonumber
/// <reference path="./src/vite-env.d.ts" />
```

Lastly, provide the path to the setup file in <InlineFile file="vite.config.ts">`vite.config.ts`</InlineFile> as the `setupFiles` value of a conrete browser instance (follow the instructions in the configuration file for more details).
Lastly, provide the path to the setup file in <InlineFile file="vite.config.ts">`vite.config.ts`</InlineFile> as the `setupFiles` value of a concrete browser instance (follow the instructions in the configuration file for more details).

Once you have it ready, run the tests again to see the `<FilePreview />` component reclaim its striking visuals ✨.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ There are two types of test setup you can have: _global_ and _local_. The global

I will start by creating a `vitest.browser.setup.ts` file. The naming here doesn't matter that much, but I like to keep Vitest-related files starting with `vitest.*`.

In that file, I will import the assets I want to apply to all rendered components:
In that file, I will import the assets I want to include when rendering any component:

```ts filename=vitest.browser.setup.ts add=1
import './src/index.css'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Multiple workspaces

The way you have Vitest configured right now will run _all tests_ using the Browser Mode. This may be not what you want, especially if you are using Vitest for unit testing or integration testing in Node.js in the same project that needs in-browser component tests as well.
Our current Vitest configuration runs _all tests_ in Browser Mode. This isn't ideal when you need to run different types of tests in the same project - like unit and integration tests running in a Node.js environment alongside browser-based component tests.

You can fix this by introducing _different workspaces_ for differnt types of tests. In fact, I think this is just the right task for you...
You can fix this by introducing _different workspaces_ for different types of tests. In fact, I think this is just the right task for you...

👨‍💼 In this one, you will expand on the Vitest configuration to support running multiple types of tests in the same project. This will be a multi-step process to make sure you have the Vitest and TypeScript configured correctly for your tests.
👨‍💼 In this one, you will expand the Vitest configuration to support running multiple types of tests in the same project. This will be a multi-step process to make sure you have Vitest and TypeScript configured correctly for your tests.

🐨 First, update <InlineFile file="vite.config.ts">`vite.config.ts`</InlineFile> to list multiple [workspaces](https://main.vitest.dev/guide/workspace.html#configuration). Define one for unit tests and the other for component tests.
🐨 First, update <InlineFile file="vite.config.ts">`vite.config.ts`</InlineFile> to list multiple [workspaces](https://main.vitest.dev/guide/workspace.html#configuration). Define one for unit tests and another for component tests.

🐨 Next, rename <InlineFile file="tsconfig.test.json">`tsconfig.test.json`</InlineFile> to `tsconfig.test.browser.json`. This TypeScript configuration will apply only to the component tests now. Update its `include` to target `**/*.browser.test.ts*` files:
🐨 Next, rename <InlineFile file="tsconfig.test.json">`tsconfig.test.json`</InlineFile> to `tsconfig.test.browser.json`. This TypeScript configuration will only apply to the component tests now. Update its `include` setting to target `**/*.browser.test.ts*` files:

```json filename=tsconfig.test.browser.json remove=2 add=3
{
Expand All @@ -17,7 +17,7 @@ You can fix this by introducing _different workspaces_ for differnt types of tes
}
```

🐨 To have proper type-checking in unit tests, create a new <InlineFile file="tsconfig.test.unit.json">`tsconfig.test.unit.json`</InlineFile> file and list there the properties necessary for the unit tests. You can use this as an example:
🐨 To have proper type-checking in unit tests, create a new <InlineFile file="tsconfig.test.unit.json">`tsconfig.test.unit.json`</InlineFile> file and add the necessary properties for unit tests. You can use this as an example:

```json filename=tsconfig.test.unit.json
{
Expand Down Expand Up @@ -49,6 +49,6 @@ You can fix this by introducing _different workspaces_ for differnt types of tes
}
```

🐨 Finally, rename the existing `file-preview.test.tsx` component test to `file-preview.browser.test.tsx` to be picked up by the proper workspace in Vitest.
🐨 Finally, rename the existing `file-preview.test.tsx` component test to `file-preview.browser.test.tsx` to be included in the correct Vitest workspace.

See you on the other side once you're done to go through each step in more detail.
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ The first workspace include the configuration for running unit tests:
- `exclude` does the opposite of `include`, listing the file patterns to _ignore_. Since my browser tests also end with `*.test.ts`, I need to exclude them not to be confused with unit tests;
- `environment` controls the test environment used for this workspace. I want my unit tests to run in Node.js, so I provide `'node'` as the enivornment here.

> :owl: Notice that each workspace lists Vitest configuration starting from the root (e.g. includes the `test` key again). This is handy because this means each workspace can have a different set of Vite options to handle the test files (e.g. list different `plugins`).
> :owl: Notice that each workspace lists Vitest configuration starting from the root by including the `test` key again. This is handy because each workspace can have a different set of Vite options and `plugins` for different test files.

Similarly, here's the workspace for the browser (component) tests:

Expand All @@ -99,17 +99,17 @@ Similarly, here's the workspace for the browser (component) tests:
},
```

Here, I'm naming this workspace `'browser'` and configuring it to include only `*.browser.test.ts(x)` test files. Those will be my component tests. For the rest of it, I simply moved the existing `test.browser` configuration under this workspace and left it as-is.
Here, I'm naming this workspace `'browser'` and configuring it to include only `*.browser.test.ts(x)` test files. These will be my component tests. For the rest of the configuration, I simply moved the existing `test.browser` configuration under this workspace and left it as-is.

## TypeScript

The next step is to deal with TypeScript. One of the most overlooked aspects of using TypeScript is that you often need _multiple configurations_ within the same project. Your source code, unit tests, integration tests, or test utilities are all written in TypeScript but have different concerns that may require different types.
The next step is to deal with TypeScript. One of the most overlooked aspects of using TypeScript is that you often need _multiple configurations_ within the same project. Your source code, unit tests, integration tests, and test utilities are all written in TypeScript but have different concerns that may require different types.

> :scroll: If you want to dive deeper into the reason behing using multiple TypeScript configurations and how to do that properly, read my post called [One Thing Nobody Explained To You About TypeScript](https://kettanaito.com/blog/one-thing-nobody-explained-to-you-about-typescript).

In our project, unit and component tests have a different set of type requirements because they run in different environments. This means I need to introduce two separate configurations: `tsconfig.test.unit.json` and `tsconfig.test.browser.json` to address those differences.
In our project, unit and component tests have a different set of type requirements because they run in different environments. This means I need to introduce two separate configurations to address these differences: `tsconfig.test.unit.json` and `tsconfig.test.browser.json`.

Let's start with the unit tests.
Let's start with the unit tests:

```json filename=tsconfig.test.unit.json
{
Expand All @@ -130,7 +130,7 @@ Similar to how I've configured Vitest workspaces to apply only to specific file
"types": ["node", "vitest/globals"]
```

This include Node.js type definitions (`@types/node`) and Vitest global types (e.g. `test` and `expect`) for my unit tests. Since I don't have the Node.js types installed, I need to add them as a dependency to the project:
This includes Node.js type definitions (`@types/node`) and Vitest global types (e.g. `test` and `expect`) for my unit tests. Since I don't have the Node.js types installed, I need to add them as a dependency to the project:

```sh nonumber
npm i -D @types/node
Expand All @@ -152,7 +152,7 @@ Next, I rename the existing `tsconfig.test.json` to `tsconfig.test.browser.json`

## Test commands

To make it easier to run specific types of tests, I will modify `package.json` to include the new `test:unit` and `test:integration` commands:
To make it easier to run specific types of tests, I will modify `package.json` to add `test:unit` and `test:integration` commands:

```json
{
Expand All @@ -171,4 +171,4 @@ There are multiple ways to group different types of tests in the same project:
- **By file name**. This is the one I'm using this exercise, adopting `*.test.ts` for unit tests and `*.browser.test.tsx` for integration tests;
- **By directory name**. For example, you can keep unit tests in `./src` while integration tests in `./tests`

The only wrong choice here is the one not followed consistently. You can choose whichever approach makes more sense in your circumstance, but I don't recommend mixing them in the same app.
The choice is up to you — I only recommend you stick to one approach and don't mix them in your app.
2 changes: 1 addition & 1 deletion exercises/02.vitest-browser-mode/FINISHED.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Vitest Browser Mode

Great job! 🎉 Take some rest and let's continue.
Great job! 🎉 Have a rest and let's continue.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Queries

One of the primary ways to achieve user-driven tests is to access and interact with the UI elements in the same manner user would. Your users don't know HTML or CSS so they don't locate elements on the page by their class names or `test-id` attributes. Instead, they find the things they need by their _roles_ and _names_ (this is true for visually-impared users as well).
One of the primary ways to achieve user-driven tests is to access and interact with the UI elements in the same way the user would. Your users don't know HTML or CSS so they don't locate elements on the page by their class names or `test-id` attributes. Instead, they find the things they need by their _roles_ and _names_ (this is true for visually-impared users as well).

The way you access different elements in tests matters a lot. It can vary from giving you a ton of value and implicit accessibility assurance to forcing you to change your tests all the time because they are riddled with implementation details.

<callout-info>When testing, do what users do.</callout-info>
<callout-info>When testing, do what your users would do.</callout-info>

👨‍💼 In this exercise, your task is to write a simple integration test for a new React component called <InlineFile file="./src/discount-code-form.tsx">
`<DiscountCodeForm />`</InlineFile>. And what do you know it—this component is a _form_! This means plenty of elements for you to locate. Use locators like `.getByRole()` and `.getByLabelText()`, and write assertions that make sure all the necessary form elements are present on the page.
`<DiscountCodeForm />`</InlineFile>. And what do you know—this component is a _form_! This means plenty of elements for you to locate. Use locators like `.getByRole()` and `.getByLabelText()`, and write assertions that make sure all the necessary form elements are present on the page.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ I'm going to start by locating the discount code input on the page. This one:
/>
```

A user would find this input by its _label text_ because they would see that it says "Discount code" above that input. When creating the `<DiscountCode />` component, I've made sure to have an accessible layout by associating a `label` element with the input by its `id`:
A user would find this input by its _label text_ because they would see that it says "Discount code" above that input. When creating the `<DiscountCode />` component, I've made sure to have accessible markup by associating a `label` element with the input by its `id`:

```tsx filename=discount-code-form.tsx highlight=2,8
<label
Expand Down Expand Up @@ -120,7 +120,7 @@ But there are also elements that don't. Some derive their accessible name from t

And then there are elements that don't need accessible name at all. Those include `<section>`, `<p>`, `<span>`, or images that are purelly illustrational.

<callout-success>Select elements by their accessible name if it has one, otherwise select it by their text content. Do not add ARIA role attributes or accessible names where they are not needed! The goal is always accessible layout first, tests second. Never the other way around. See [Naming techniques](https://www.w3.org/WAI/ARIA/apg/practices/names-and-descriptions/#naming_techniques) for more details.</callout-success>
<callout-success>Select elements by their accessible name if it has one, otherwise select it by their text content. Do not add ARIA role attributes or accessible names where they are not needed! The goal is always accessible markup first, tests second. Never the other way around. See [Naming techniques](https://www.w3.org/WAI/ARIA/apg/practices/names-and-descriptions/#naming_techniques) for more details.</callout-success>

### Text content

Expand Down
10 changes: 5 additions & 5 deletions exercises/03.best-practices/02.problem.user-events/README.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# User events

The way your test interacts with UI elements is just as important as the way it locates them. Naturally, your users don't dispatch events on elements or programmatically access them. They hover, click, type, drag. They do what their input devices allow them to do.
How your tests interact with UI elements is just as important as how they locate them. Your users don't dispatch events or programmatically access elements — they hover, click, type, and drag using their input devices.

So, how do you bring your tests to do the same? For a long while, integration testing revolved around _simulating_ user events. That was mostly due to the environmental limitations as you cannot really interact with a UI element in Node.js.
So, how can we replicate real user interactions in our tests? Historically, integration testing relied on _simulating_ user events, mainly because Node.js can't actually interact with UI elements.

**But your component tests aren't running in Node.js anymore**. Being in the real browser also means utlizing real user events. Actually hovering, clicking, typing, or dragging. One more benefit to reap from testing the code where it's supposed to run.
**But your component tests aren't running in Node.js anymore**. Being in the real browser also means using real user events. Actually hovering, clicking, typing, or dragging. One more benefit to reap from testing the code where it's supposed to run.

👨‍💼 This exercises has the following task for you in store: complete the automated test at <InlineFile file="./src/discount-code-form.browser.test.tsx">`discount-code-form.browser.test.tsx`</InlineFile> to enter a discount code, submit it, and assert that it has actually been applied (i.e. became visible as applied in the UI).
👨‍💼 This exercise has the following task for you: complete the automated test at <InlineFile file="./src/discount-code-form.browser.test.tsx">`discount-code-form.browser.test.tsx`</InlineFile> to enter a discount code, submit it, and verify it appears correctly in the UI.

Give it your best and see you in the solution to this exercise!
Give it your best shot and see you in the solution to this exercise!
12 changes: 6 additions & 6 deletions exercises/03.best-practices/02.solution.user-events/README.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# User events

At its current state, our discount form component test gives you some value, but it can give much more. We don't create UI elements simply for them "to be" on the page. They are there so our users could interact with them, make them do something, help them achieve their tasks.
In its current state, our discount form component test gives you some value, but it can give so much more. We don't create UI elements simply for them "to be" on the page. They are there so our users can interact with them, make them do something, and help them achieve their tasks.

The main purpose of the discount form component is to apply the given discount code. _That_ is what has to be reflected in automated tests.

Expand All @@ -21,7 +21,7 @@ test('applies a discount code', async () => {
await expect.element(discountInput).toBeVisible()
```

I intend to _interact_ with the `discountInput` element so asserting its visibility on the page becomes redundant. Its interactivity is _implied_ by the interaction. If this input is, say, not being rendered correctly, interacting with it will fail. This is called an _implicit assertion_.
I intend to _interact_ with the `discountInput` element so asserting its visibility on the page is redundant. Its interactivity is _implied_ by the interaction. If this input is, say, not being rendered correctly, interacting with it will fail. This is called an _implicit assertion_.

> 🦉 [Implicit assertions](https://www.epicweb.dev/implicit-assertions) are a great way to achieve more in tests while writing less.

Expand All @@ -35,11 +35,11 @@ test('applies a discount code', async () => {
await discountInput.fill('EPIC2025')
```

The `.fill()` method accepts a _value_ to enter and returns a promise that resolves when the browser is done typing it in.
The `.fill()` method accepts a _value_ to enter and returns a promise that resolves when the browser is finished typing it in.

Now that the discount code has been entered, it's time to apply it.

Much the same, I will drop the redundant visibility assertiom from the `applyDiscountCode` button, and replace it with the `.click()` interaction with that button:
As above, I will drop the redundant visibility assertion from the `applyDiscountCode` button, and replace it with the `.click()` interaction with that button:

```tsx filename=discount-code-form.browser.test.tsx remove=10 add=11 nocopy
test('applies a discount code', async () => {
Expand Down Expand Up @@ -70,7 +70,7 @@ My expectation here is derived from _how_ the applied discount code is displayed
)
```

So, in case the code has been successfully applied, I expect this paragraph to be visible on the page:
Finally, when the code has been successfully applied, I expect this paragraph to be visible on the page:

```tsx filename=discount-code-form.browser.test.tsx add=13-15 nocopy
test('applies a discount code', async () => {
Expand All @@ -95,7 +95,7 @@ This completes the test! 🎉

## Locator methods vs `userEvent`

If you used React Testing Library in the past, you've likely been interacting with your components via the `userEvent` object from `@testing-library/user-event`. Vitest provides you a similar object from the `@vitest/browser/context` package as well:
If you used React Testing Library in the past, you've likely been interacting with your components via the `userEvent` object from `@testing-library/user-event`. Vitest provides you a similar object from the `@vitest/browser/context` package:

```tsx nonumber highlight=1,7
import { userEvent } from '@vitest/browser/context',
Expand Down
Loading
Loading