こんにちは、mimiです。
Next.js + TypeScript な環境にPlaywrightとnext-i18nextを導入して、多言語化できてるか簡単なe2eテストをするまでのメモです。
Playwrightとは
Playwrightは、Webテストと自動化のためのフレームワークです。Chromium、Firefox、WebKitを一つのAPIでテストすることができます。Playwrightは、クロスブラウザのWeb自動化を可能にするために構築されており、常に環境に優しく、高機能で、信頼性が高く、高速なWeb自動化を実現します。
https://github.com/microsoft/playwright
Microsoft製(といってもオープンソース)の自動化ツール。puppeteerとかCypressとか仲間ですね。ここではe2eテストツールとして導入しますが、ビジュアルリグレッションテストとかも出来ます。Safariとかでもテストできるのが素敵。且つpuppeteerより早いと思いますしdebugモードもめっちゃいいです。
最近、WordPressもpuppeteerからPlaywrightへの移行計画を発表しました。
Migrating WordPress E2E tests to Playwright – Make WordPress Core
といってもライブラリが膨大なので移行作業なかなか進んでないみたいです。Playwrightに慣れたらPR送ってみようと思います。
そんな訳で、今e2eやるならPlaywrightだな!ということでNext.js + TypeScriptなプロジェクトに導入してみました。
インストール
Getting started | Playwright
に全部書いてあります。
マニュアルでやるなら
yarn add -D @playwright/test
// または
npm i -D @playwright/test
したあとで、
npx playwright install
するとサポートブラウザがインストールされます。テストするブラウザを限定したい場合はnpx playwright install webkit
のように指定することもできます。
設定
インストールしただけですぐに使えますが、テストファイルの置き場所とかいくつか設定をカスタムしたくなると思います。その場合はplaywright.config.ts
を作ります。
testMatch
でテストするファイルを指定します。テストに使うブラウザも設定できたりします。
ex. chromiumとiPhone12の場合
import { PlaywrightTestConfig, devices } from '@playwright/test'
const config: PlaywrightTestConfig = {
testMatch: '/e2e/**/*.spec.ts',
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
headless: true,
ignoreHTTPSErrors: true,
actionTimeout: 10_000
},
projects: [
{
name: 'chromium',
use: {...devices['Desktop Chrome']},
},
{
name: 'iphone12',
use: {...devices["iPhone 12"]}
}
]
}
export default config;
これでe2eフォルダ以下にテストを書けます。
VS Codeのプラグイン Playwright Test for VSCode が神ってるので入れましょう
Playwright Test for VSCode – Visual Studio Marketplace
Playwright使うなら、ノールックで入れたほうがいいプラグインです。
e2eテストの下準備はとりあえずここまで。
Next.js で多言語化 – next-i18nextとは
e2eテストの例として多言語化できてるかの簡単なテストを書いてみましょう。
ついでに、多言語化の話もメモしておこうと思います。
Next.jsの多言語化はnext-i18nextを使うのが良さそうです。
next-i18nextはi18nextとreact-i18nextを利用していますが、next-i18nextのユーザーは翻訳コンテンツをJSONファイルとして含めるだけで、他のことはあまり気にする必要がありません。
https://github.com/isaachinman/next-i18next
と書いてあるように導入めっっちゃ簡単です。
多言語化のあれこれは基本的に
NextJS i18n/Internationalization – DEV Community
こちらの↑記事を読んだら出来ます。
Next.jsで多言語化するときの基本設定
next-i18next
を使わなくても、Next.jsでは、next/router
のuseRouter
でlocale
を取ることが出来ますし、configでlocale
を設定することで簡単にRoutingの設定ができます。
ここの部分はこの記事が日本語だし分かりやすいと思います。
サブディレクトリでルーティング出来るように設定
上記の記事にもあるように、ドメイン切り替えもできますが、今回はサブディレクトリで英語をデフォルトとして言語を切り替えさせたいのでnext.config.js
で以下のように設定しておきます。
module.exports = {
i18n: {
locales: ['en', 'ja'],
defaultLocale: 'en',
},
}
これを書くだけで、ブラウザの言語情報からenかjaかを自動判別して表示を切り替えてくれます。この場合日本語環境でサイトのトップページにアクセスすると、https://example.com/ja
に飛びます。
例えばpages/index.tsx
に
import type { NextPage } from 'next'
import { useRouter } from "next/router"
const HomePage: NextPage = () => {
const { locale } = useRouter()
return <main>Hello world: {locale}</main>
}
export default HomePage
という風に書いているとHello world:
の後の表示がja/enと切り替わるでしょう。
ちなみにNext.jsはhtmlタグにlang情報付与されませんが、これを設定するとちゃんと勝手に付与してくれます。多言語化しなくてもjaを付与したかったらlocales: ['ja']
にすれば良いだけです。
自動判別については
When a user visits the application root (generally /), Next.js will try to automatically detect which locale the user prefers based on the Accept-Language header and the current domain.
ユーザーがアプリケーションのルート(一般に/)にアクセスすると、Next.jsはAccept-Languageヘッダーと現在のドメインに基づいて、ユーザーが好むロケールを自動的に検出しようとします。
Automatic Locale Detection
という風にNext.jsの公式サイトに説明があります。もし自動判別をオフしたかったらlocaleDetection: false
を書くだけでオフしてルーティングをカスタマイズできます。詳しくは上記のリンクのドキュメントにあります。
英日切り替え表示とかは上記に紹介したNextJS i18n/Internationalization – DEV Communityに詳しいので参照してみてください。
さて、基本のルーティング設定が出来たので、next-i18nextを使って翻訳情報を追加していきましょう。
Hello world:
の部分を書き換えて、日本語のアクセスだったらこんにちは:
と切り替わるようにします。
インストール
yarn add next-i18next
// または
npm i next-i18next
設定
さっき書いたconfigをちょっと移動して一行加えます。
next-i18next.config.js
というファイルを作成して、以下のように記述します。
module.exports = {
i18n: {
defaultLocale: "en",
locales: ["en", "ja"],
localePath: "./locales",
}
}
そしてnext.config.js
を書き換えます。
const { i18n } = require("./next-i18next.config");
module.exports = {
i18n
}
翻訳が当たるように_app.tsx
をappWithTranslationでラップします。
import type { AppProps } from 'next/app'
import { appWithTranslation } from 'next-i18next'
function MyApp({ Component, pageProps }: AppProps) {
return (
<Component {...pageProps} />
)
}
export default appWithTranslation(MyApp)
さらにページ単位で getStaticProps または getServerSideProps を使ってコンポーネントに非同期関数を含める必要があります。
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
// 中略。export default HomePageの後に以下のように記述します
export async function getStaticProps({ locale }: any) {
return {
props: {
...(await serverSideTranslations(locale, ["common","home"])),
}
}
}
以上で設定完了。
翻訳を当てる
次に翻訳を書きましょう。localesディレクトリに以下のようにファイルを作ります。
今回の記事では出てきませんが、共通の翻訳はcommon.jsonに記述してください。
.
└── locales
├── en
| └── common.json
| └── home.json
└── ja
└── common.json
└── home.json
2つのhome.jsonに以下のように記述して保存します。
./locales/en/home.json
{
"greeting": "Hello"
}
./locales/ja/home.json
{
"greeting": "こんにちは"
}
pages/index.jsx
を以下のように書き換えます。
import type { NextPage } from 'next'
import { useTranslation } from 'next-i18next'
const HomePage: NextPage = () => {
const { t } = useTranslation("home")
return <main><{t("greeting")}</main>
}
export default HomePage
これで日本語環境でトップページにアクセスするとこんにちは
と表示されるはずです。
ちなみに、翻訳が見当たらない時はgreeting
が代わりに表示されます。とりあえず{t("Something String")}
な形式で原文を書いてjson化するのが良いのか、最初からjson化したほうが良いのか知りたい。あと、jsonのファイルサイズどのぐらいまで問題ないのかとか。ご存知の方は教えてください。
e2eテストを書いてみる
さて、残りの多言語化の諸々は参照した記事に任せるとして、本題のe2eテストを書いてみましょう。
すごく愚直な感じですが、chromiumでlocaleをen-USとjaに切り替えてそれぞれのトップページでHello
とこんにちは
が表示されるか、というテストを書いてみます。このテストの場合はブラウザ毎のテストは要らない気がするのでchromiumだけで良いかなと思います。
./e2e/i18n.spec.ts
に以下のように書いてみます。
import { test, chromium, expect } from '@playwright/test'
test('App should be displayed in English if the locale is en', async () => {
const browser = await chromium.launch()
const context = await browser.newContext({
locale: 'en-US'
})
const page = await context.newPage();
await page.goto('http://localhost:3000');
const login = page.locator('main');
expect(login).toHaveText('Hello');
});
test('App should be displayed in Japanese if the locale is ja', async () => {
const browser = await chromium.launch()
const context = await browser.newContext({
locale: 'ja'
})
const page = await context.newPage();
await page.goto('http://localhost:3000');
const login = page.locator('main );
expect(login).toHaveText('こんにちは');
});
これでnpx playwright test
を実行すればテストが走ります。デバックしたかったらnpx playwright test --debug
で実際にブラウザを立ち上げて表示を見ながら一つ一つデバックできます。VS Codeのプラグインからだとボタンポチで出来ます。
共通設定などがあれば、global-setup.ts
を作成して、globalSetupを作成したら良いです。envの設定とかはそちらから読み込ませると便利です。詳しくはAdvanced: configuration | Playwrightなどを見てみてください。
というわけで、導入から簡単なテストまでの流れをわーっと書いてみました。特に引っかかりがなく出来てしまったのでこれからが怖いです。