【脱create-react-app】pnpm + vite + React + vitest を動かす！

# create-react-app is dead
以下の記事が凄くわかりやすいです。

https://zenn.dev/nekoya/articles/dd0f0e8a2fa35f

一部引用

>Releasesを見ていくと、最後のリリースは去年の4月です。近年のリリース日をまとめるとこんな感じです。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/18709251-9a0b-45b5-ade8-c5ca5c48cfe0.png)

viteに移行しましょう。ついでにpnpmにも入門してみます。

今回は、以下をやってみます！

**① pnpmインストール**
**② vite + react 環境作成**
**③ vitest インストール**
**④ vitest + React Testing Library 環境作成**

# 0⃣ ver確認
```bash
node -v # v20.9.0
pnpm -v # 9.11.0
```

# ① pnpmのインストール
公式ドキュメントを見つつ、お好みの方法でインストールします
nodeはデフォルトでnpmもおまけにインストールしてくれているはずなので、これがシンプルで楽なはず

```bash
npm install -g pnpm
```

https://pnpm.io/ja/installation

## そもそもpnpmって？
npmのお兄ちゃん

https://zenn.dev/cloud_ace/articles/articlejs-package-manager-pnpm

特に
- npmに比べ動作が速い
- node_modulesをプロジェクト間で共有する

の2点が趣味開発してるマシン的には嬉しいと思いました

# ② vite + react環境の作成
ドキュメントに従い、 ```create vite``` コマンドでプロジェクトを作成します

https://ja.vite.dev/guide/#%E6%9C%80%E5%88%9D%E3%81%AE-vite-%E3%83%95%E3%82%9A%E3%83%AD%E3%82%B7%E3%82%99%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B

## create viteコマンドでプロジェクトを作成する
```bash
pnpm create vite my-react-ts-app --template react-ts
```

一瞬で終わる！
昔create-react-appしたときより早い気がする。きのせい？

```bash
c/work/react$ pnpm create vite my-react-ts-app --template react-ts

Scaffolding project in c/work/react/my-react-ts-app...

Done. Now run:

  cd my-react-ts-app
  pnpm install
  pnpm run dev
```

コマンドラインに従い、パッケージのインストールとreact devサーバーの起動を行ってみます

```bash
  cd my-react-ts-app
  pnpm install
  pnpm run dev
```

## pnpmコマンドでパッケージをインストール
```bash
pnpm install
```
実行します。

```
c/work/react/my-react-ts-app$   pnpm install       

   ╭──────────────────────────────────────────────────────────────────╮
   │                                                                  │
   │                Update available! 9.11.0 → 9.12.0.                │
   │   Changelog: https://github.com/pnpm/pnpm/releases/tag/v9.12.0   │
   │                Run "pnpm add -g pnpm" to update.                 │
   │                                                                  │
   │         Follow @pnpmjs for updates: https://x.com/pnpmjs         │
   │                                                                  │
   ╰──────────────────────────────────────────────────────────────────╯

Packages: +192
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
Progress: resolved 229, reused 140, downloaded 52, added 192, done


dependencies:     
+ react 18.3.1    
+ react-dom 18.3.1

devDependencies:
+ @eslint/js 9.12.0
+ @types/react 18.3.11
+ @types/react-dom 18.3.0
+ @vitejs/plugin-react 4.3.2
+ eslint 9.12.0
+ eslint-plugin-react-hooks 5.1.0-rc-fb9a90fa48-20240614
+ eslint-plugin-react-refresh 0.4.12
+ globals 15.10.0
+ typescript 5.6.2
+ typescript-eslint 8.8.0
+ vite 5.4.8

Done in 1m 31.6s
```

>Done in 1m 31.6s

早い！気がします

## react devサーバーを実行
```bash
pnpm run dev
```

実行します

```bash
c/work/react/my-react-ts-app$   pnpm run dev

> my-react-ts-app@0.0.0 dev c/work/react/my-react-ts-app
> vite


  VITE v5.4.8  ready in 4632 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

```

>  VITE v5.4.8  ready in 4632 ms

早い！気がします

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/76c3c093-a9db-14be-72dc-48b1cca473b2.png)

```http://localhost:5173/``` で問題なく表示されていることを確認します

とにかく早いですねー

# ③ vitest インストール
テストライブラリもインストールしていきます。

## vitestのインストール
ドキュメントに従ってインストールしていきます。

https://vitest.dev/guide/

```bash
pnpm add -D vitest
```

実行します

```bash
c/work/react/my-react-ts-app$ pnpm add -D vitest
Packages: +29
+++++++++++++++++++++++++++++
Progress: resolved 258, reused 212, downloaded 9, added 29, done

devDependencies:
+ vitest 2.1.2

Done in 28.3s
```

インストールされました

## vitestを実行してみる
testファイルを作成していきます

```bash
mkdir __test__
touch __test__/sample.test.ts 
```

テストを書いてみます

```__test__/sample.test.ts 
import { describe, expect, it } from "vitest";

describe("sample", () => {
    it("1 eq 1", () => {
        expect(1).toBe(1)
    })
})
```

テストが実行できるよう、package.jsonを編集します

```diff_json
    "dev": "vite",
+   "test":"vitest",
    "build": "tsc -b && vite build",
```

testスクリプトを追記します

```package.json
{
  "name": "my-react-ts-app",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "test":"vitest",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  },
  "devDependencies": {
    "@eslint/js": "^9.11.1",
    "@types/react": "^18.3.10",
    "@types/react-dom": "^18.3.0",
    "@vitejs/plugin-react": "^4.3.2",
    "eslint": "^9.11.1",
    "eslint-plugin-react-hooks": "^5.1.0-rc.0",
    "eslint-plugin-react-refresh": "^0.4.12",
    "globals": "^15.9.0",
    "typescript": "^5.5.3",
    "typescript-eslint": "^8.7.0",
    "vite": "^5.4.8",
    "vitest": "^2.1.2"
  }
}
```

では、テストを実行して1は1なのか確かめます

```bash
pnpm run test
```

コマンドを実行します

```bash
c/work/react/my-react-ts-app$ pnpm run test

> my-react-ts-app@0.0.0 test c/work/react/my-react-ts-app
> vitest


 DEV  v2.1.2 c/work/react/my-react-ts-app

 ✓ __test__/sample.test.ts (1)
   ✓ sample (1)
     ✓ 1 eq 1

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  09:39:24
   Duration  9.43s (transform 268ms, setup 0ms, collect 789ms, tests 7ms, environment 1ms, 
prepare 6.44s)


 PASS  Waiting for file changes...
       press h to show help, press q to quit
```

> ✓ 1 eq 1

良い感じですね

## そもそもvitestって？
jestライクなテスティングライブラリです
 
https://vitest.dev/

特に
-  viteと統合されている（から？HMRがはやい）
- ESM, CJSを意識しなくていい（はず）
- jestライク

の3点が個人的嬉しいところです

# ④ vitest + React Testing Library 環境作成
## 実行環境を提供するライブラリのインストール
node環境上には、デフォルトではDOMを再現する機能が存在しません
DOMを再現するためのライブラリをインストールします

```bash
pnpm add -D jsdom
```

コマンドを実行します

```bash
c/work/react/my-react-ts-app$ pnpm add -D jsdom
Packages: +36
++++++++++++++++++++++++++++++++++++
Progress: resolved 293, reused 227, downloaded 29, added 36, done

devDependencies:
+ jsdom 25.0.1  

Done in 53.5s
```

インストール後、vite.config.ts を編集します

```diff_typescript
+ // Vitestの型を追加する
+ /// <reference types="vitest" />

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
+ test: {
+   // デフォルトのnode環境ではDOMを再現できないので、jsdom環境を使用する
+   environment: "jsdom",
+ },
})


```

準備完了です！！！

```vite.config.ts
// Vitestの型を追加する
/// <reference types="vitest" />

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  test: {
    // デフォルトのnode環境ではDOMを再現できない
    environment: "jsdom",
  },
})
```

## Reactコンポーネントをテストするためのライブラリをインストールする
Reactはjsx、tsxという特殊な形式のjs、tsを利用します
デフォルトのvitest、jsdomだけではこのReactコンポーネントをレンダリングしたり、操作できません

そこで、Reactコンポーネントをレンダリング・操作するためのライブラリを別途追加します

```bash
pnpm add -D @testing-library/react @testing-library/jest-dom @testing-library/user-event
```

コマンドを実行します

```bash
c/work/react/my-react-ts-app$ pnpm add -D @testing-library/react 
@testing-library/jest-dom @testing-library/user-event
Packages: +25
+++++++++++++++++++++++++
Progress: resolved 318, reused 257, downloaded 24, added 25, done

devDependencies:
+ @testing-library/jest-dom 6.5.0
+ @testing-library/react 16.0.1
+ @testing-library/user-event 14.5.2

Done in 1m 2.9s
```

テストを書いてみます

```
touch __test__/App.test.tsx
```

こんな感じのテストケースを用意しました。

```__test__/App.test.tsx
import { beforeEach, describe, expect, it } from "vitest";
import { cleanup, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import '@testing-library/jest-dom/vitest';

import React from "react";
import App from "../src/App"

// デフォルトではクリーンアップされないため、Eachで実行する
beforeEach(() => {
    cleanup();
});
// userインスタンスのセットアップ
const user = userEvent.setup();

describe("tsx sample", () => {
    it("viteとReactの画像が表示されていることを確認する", () => {
        // コンポーネントをレンダリングする
        render(<App />)

        // レンダリングされたDOM上から要素を取得する
        const imgElementArray = screen.getAllByRole("img")

        // アサートする
        // 本来はテストケースごとに1アサーションが良いってt-wadaさんが言ってた
        expect(imgElementArray.length).toBe(2)
        expect(imgElementArray[0].getAttribute("alt")).toBe("Vite logo")
        expect(imgElementArray[1].getAttribute("alt")).toBe("React logo")
    })
    it("use jest-dom sample", () => {
        // コンポーネントをレンダリングする
        render(<App />)

        // 拡張されたマッチャーを使用できる
        expect(screen.getByText("count is 0")).toBeInTheDocument()
    })
    it("use user-event sample", async () /* async関数をテストケース内で使用する */ => {
        // コンポーネントをレンダリングする
        render(<App />)
        // レンダリングされたDOM上から要素を取得する
        const countButtonElement = screen.getByRole("button")

        // userの操作を再現する
        // clickはasync関数なので、awaitする
        await user.click(countButtonElement)

        // 拡張されたマッチャーを使用できる
        expect(screen.getByText("count is 1")).toBeInTheDocument()
    })
})

```

先ほどaddした以下のライブラリが何をしてくれているか確認します

```@testing-library/react```
```@testing-library/jest-dom```
```@testing-library/user-event```

### @testing-library/react
```__test__/App.test.tsx
import { cleanup, render, screen } from "@testing-library/react";
```

```@testing-library/react``` は、上述したように、
```render``` 関数や```screen``` 関数で、Reactコンポーネントをレンダリングしてくれています。

```__test__/App.test.tsx
        // コンポーネントをレンダリングする
        render(<App />)

        // レンダリングされたDOM上から要素を取得する
        const imgElementArray = screen.getAllByRole("img")
```

こんなかんじですね

### @testing-library/jest-dom
```__test__/App.test.tsx
import '@testing-library/jest-dom/vitest';
```

jest-domはvitestのマッチャー（比較するメソッド）を拡張してくれています

https://github.com/testing-library/jest-dom#usage

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/5278df42-c3e6-4607-2e15-4ed51963872e.png)

```__test__/App.test.tsx
        // 拡張されたマッチャーを使用できる
        expect(screen.getByText("count is 0")).toBeInTheDocument()
```

今回は ```toBeInTheDocument()``` マッチャーを使ってみてます

### @testing-library/user-event
```__test__/App.test.tsx
import userEvent from "@testing-library/user-event";
```

```@testing-library/user-event``` は、上述したように、
DOMに対するユーザーの操作を疑似的に再現してくれています。

```__test__/App.test.tsx
// userインスタンスのセットアップ
const user = userEvent.setup();
```

```__test__/App.test.tsx
        const countButtonElement = screen.getByRole("button")

        // userの操作を再現する
        // clickはasync関数なので、awaitする
        await user.click(countButtonElement)
```

くりっくしてくれます

テストコードはこんな感じなので、テストを実行してみます



```bash
c/work/react/my-react-ts-app$ pnpm run test

> my-react-ts-app@0.0.0 test c/work/react/my-react-ts-app
> vitest


 DEV  v2.1.2 c/work/react/my-react-ts-app

 ✓ __test__/App.test.tsx (3) 387ms
   ✓ tsx sample (3) 385ms
     ✓ viteとReactの画像が表示されていることを確認する
     ✓ use jest-dom sample
     ✓ use user-event sample

 Test Files  1 passed (1)
      Tests  3 passed (3)
   Start at  10:57:57
   Duration  93.69s (transform 822ms, setup 0ms, collect 36.01s, tests 387ms, environment 47.37s, prepare 7.85s)


 PASS  Waiting for file changes...
       press h to show help, press q to quit

```

やったー

これで超基本の部分の環境構築は完了したかなーと思います！