Skip to content

Commit

Permalink
E2E テストと VRT の安定性を向上 (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
nodaguti authored Mar 29, 2024
1 parent 534d38a commit ab92d96
Show file tree
Hide file tree
Showing 16 changed files with 120 additions and 62 deletions.
8 changes: 4 additions & 4 deletions workspaces/testing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
"private": true,
"scripts": {
"start": "run-s start:client start:admin",
"start:client": "playwright test ./src/client --update-snapshots",
"start:client": "playwright test ./src/client",
"start:admin": "playwright test --workers=1 ./src/admin",
"start:debug": "DEBUG=pw:api run-s start:client:debug start:admin:debug",
"start:client:debug": "playwright test --debug ./src/client",
"start:admin:debug": "playwright test --workers=1 --debug ./src/admin"
"start:debug": "run-s start:client:debug start:admin:debug",
"start:client:debug": "DEBUG=pw:api playwright test --debug ./src/client",
"start:admin:debug": "DEBUG=pw:api playwright test --workers=1 --debug ./src/admin"
},
"devDependencies": {
"@playwright/test": "1.42.1",
Expand Down
4 changes: 2 additions & 2 deletions workspaces/testing/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ export default defineConfig({
reporter: 'list',
retries: 0,
testDir: './src',
timeout: 120_000,
timeout: 300_000,
use: {
baseURL: BASE_URL,
headless: false,
headless: true,
trace: 'off',
},
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions workspaces/testing/src/admin/author.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import path from 'node:path';

import { expect, test } from '@playwright/test';

import { waitForAllImagesToLoad, waitForImageToLoad } from '../utils';

const AUTHOR_ID = '2ab0aca5-7dc2-4543-ac98-e23fdaca0739';
const AUTHOR_NAME = 'サトウ リコ';
const AUTHOR_NAME_HIRAGANA = 'さとう りこ';
Expand Down Expand Up @@ -93,8 +95,7 @@ test.describe('作者一覧', () => {
test('作者編集モーダルが表示されていること', async ({ page }) => {
// Then
const authorDetailSection = page.getByRole('dialog').getByRole('region', { name: '作者詳細' });
const img = authorDetailSection.getByRole('img', { name: AUTHOR_NAME });
await expect(img).toBeVisible();
await waitForAllImagesToLoad(authorDetailSection, 1);
await expect(authorDetailSection).toContainText(AUTHOR_NAME);
await expect(authorDetailSection).toContainText(AUTHOR_DESC);
await expect(authorDetailSection).toHaveScreenshot('vrt-author-detail-section.png');
Expand Down Expand Up @@ -164,7 +165,7 @@ test.describe('作者一覧', () => {
await page.getByRole('dialog').getByRole('button', { name: '決定' }).click();

// Then
await page.getByRole('dialog').getByRole('img', { name: AUTHOR_NAME }).waitFor();
await waitForImageToLoad(page.getByRole('dialog').getByRole('img', { name: AUTHOR_NAME }));
});

test('編集ボタンをクリックして編集モードになっている状態で、作者名、プロフィール、画像に不正な値を入力すると、エラーメッセージが表示されること', async ({
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions workspaces/testing/src/admin/book.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import path from 'node:path';

import { expect, test } from '@playwright/test';

import { waitForAllImagesToLoad, waitForImageToLoad } from '../utils';

const BOOK_ID = '29eeee80-c22e-4af7-83dd-d4de883f3d57';
const BOOK_NAME = '夫婦様、デザートの時間です';
const BOOK_DESC =
Expand Down Expand Up @@ -169,8 +171,7 @@ test.describe('作品一覧', () => {
test('作品編集モーダルが表示されていること', async ({ page }) => {
// Then
const details = page.getByRole('dialog').getByRole('region', { name: '作品詳細' });
const img = details.getByRole('img', { name: BOOK_NAME });
await expect(img).toBeVisible();
await waitForAllImagesToLoad(details, 1);
await expect(details).toContainText(BOOK_NAME);
await expect(details).toContainText(BOOK_DESC);
await expect(details).toHaveScreenshot('vrt-book-detail-section.png');
Expand Down Expand Up @@ -258,8 +259,7 @@ test.describe('作品一覧', () => {
await page.getByRole('dialog').getByRole('button', { name: '決定' }).click();

// Then
const img = page.getByRole('dialog').getByRole('img', { name: BOOK_NAME });
await expect(img).toBeVisible();
await waitForImageToLoad(page.getByRole('dialog').getByRole('img', { name: BOOK_NAME }));
});

test('編集ボタンをクリックして編集モードになっている状態で、作品名(ふりがな)、作品名、概要、画像に不正な値を入力すると、エラーメッセージが表示されること', async ({
Expand Down
18 changes: 10 additions & 8 deletions workspaces/testing/src/admin/episode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import path from 'node:path';

import { expect, test } from '@playwright/test';

import { waitForImageToLoad } from '../utils';

const BOOK_ID = '29eeee80-c22e-4af7-83dd-d4de883f3d57';
const EPISODE_ID = '2915ff54-5ddb-4eb7-99a2-f415045622be';

Expand Down Expand Up @@ -40,7 +42,7 @@ test.describe('エピソード', () => {
await page.getByRole('dialog').getByRole('button', { name: 'エピソードを追加' }).click();
});

test.only('必要な情報を入力して作成ボタンを押すと、エピソード編集画面に遷移して入力したデータが表示されること', async ({
test('必要な情報を入力して作成ボタンを押すと、エピソード編集画面に遷移して入力したデータが表示されること', async ({
page,
}) => {
// When
Expand Down Expand Up @@ -130,7 +132,7 @@ test.describe('エピソード', () => {
// Given
const pages = page.getByRole('list', { name: 'ページ一覧' }).getByRole('listitem');
for (let i = 0; i < 5; i++) {
await pages.nth(i).getByRole('img').waitFor();
await waitForImageToLoad(pages.nth(i).getByRole('img'));
}
});

Expand Down Expand Up @@ -166,13 +168,13 @@ test.describe('エピソード', () => {
await fileChooser.setFiles(path.join(__dirname, 'image.jpg'));

// Then
await expect(pages.last().getByRole('img')).toBeVisible();
await waitForImageToLoad(pages.last().getByRole('img'));
await expect(pages.last().getByRole('img')).not.toHaveAttribute('alt', lastPageId!);
});
});

test.describe('エピソード情報の編集', () => {
test.only('エピソード情報がフォームとして表示されること', async ({ page }) => {
test('エピソード情報がフォームとして表示されること', async ({ page }) => {
// Then
const bookId = page.url().match(/\/books\/([a-z0-9-]+)\//)![1];
await expect(page.getByRole('form', { name: 'エピソード情報' })).toContainText(bookId!);
Expand Down Expand Up @@ -202,7 +204,7 @@ test.describe('エピソード', () => {
await page.getByRole('button', { name: '更新' }).click();

// Then
page.getByRole('button', { name: '更新' }).waitFor();
await page.getByRole('button', { name: '更新' }).waitFor();
await expect(page.getByRole('textbox', { exact: true, name: 'エピソード名' })).toHaveValue(
'私は夜空に届かない',
);
Expand All @@ -215,7 +217,7 @@ test.describe('エピソード', () => {
await page.getByRole('button', { name: '更新' }).click();

// Then
page.getByRole('button', { name: '更新' }).waitFor();
await page.getByRole('button', { name: '更新' }).waitFor();
await expect(page.getByRole('textbox', { name: 'エピソード名(ふりがな)' })).toHaveValue(
'わたしはよぞらにとどかない',
);
Expand All @@ -230,7 +232,7 @@ test.describe('エピソード', () => {
await page.getByRole('button', { name: '更新' }).click();

// Then
page.getByRole('button', { name: '更新' }).waitFor();
await page.getByRole('button', { name: '更新' }).waitFor();
await expect(page.getByRole('textbox', { name: 'あらすじ' })).toHaveValue(replacement);
});

Expand All @@ -241,7 +243,7 @@ test.describe('エピソード', () => {
await page.getByRole('button', { name: '更新' }).click();

// Then
page.getByRole('button', { name: '更新' }).waitFor();
await page.getByRole('button', { name: '更新' }).waitFor();
await expect(page.getByRole('spinbutton', { name: 'エピソードの章' })).toHaveValue('2');
});

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions workspaces/testing/src/client/author.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { expect, test } from '@playwright/test';

import { waitForAllImagesToLoad } from '../utils';

const AUTHOR_ID = '2ab0aca5-7dc2-4543-ac98-e23fdaca0739';
const AUTHOR_NAME = 'サトウ リコ';
const AUTHOR_DESC =
Expand All @@ -15,8 +17,7 @@ test.describe('作者', () => {
test('作者情報にカバー画像、名前、説明が表示されていること', async ({ page }) => {
// Then
const section = page.getByRole('region', { name: '作者情報' });
const img = section.getByRole('img', { name: AUTHOR_NAME });
await expect(img).toBeVisible();
await waitForAllImagesToLoad(section, 1);
await expect(section).toContainText(AUTHOR_NAME);
await expect(section).toContainText(AUTHOR_DESC);
await expect(section).toHaveScreenshot('vrt-author-info.png');
Expand All @@ -32,8 +33,7 @@ test.describe('作者', () => {
// Then
const section = page.getByRole('region', { name: '作品一覧' });
const firstBook = section.getByRole('listitem').first();
const img = firstBook.getByRole('img');
await expect(img).toBeVisible();
await waitForAllImagesToLoad(firstBook, 2);
await expect(firstBook).toContainText(BOOK_TITLE);
await expect(firstBook).toHaveScreenshot('vrt-book.png');
});
Expand Down
8 changes: 4 additions & 4 deletions workspaces/testing/src/client/book.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { expect, test } from '@playwright/test';

import { waitForAllImagesToLoad } from '../utils';

const BOOK_ID = 'af7583e6-e52e-4f28-86dd-04f0af9d4868';
const BOOK_TITLE = '異世界教え子';
const BOOK_DESC =
Expand All @@ -15,8 +17,7 @@ test.describe('作品', () => {
test('作品情報にカバー画像、タイトル、説明、作者が表示されていること', async ({ page }) => {
// Then
const section = page.getByRole('region', { name: '作品情報' });
const img = section.getByRole('img', { name: BOOK_TITLE });
await expect(img).toBeVisible();
await waitForAllImagesToLoad(section, 2);
await expect(section).toContainText(BOOK_TITLE);
await expect(section).toContainText(BOOK_DESC);
await expect(section).toContainText(BOOK_AUTHOR);
Expand All @@ -43,8 +44,7 @@ test.describe('作品', () => {
// Then
const section = page.getByRole('region', { name: 'エピソード一覧' });
const firstEpisode = section.getByRole('listitem').first();
const img = firstEpisode.getByRole('img');
await expect(img).toBeVisible();
await waitForAllImagesToLoad(firstEpisode, 2);
await expect(firstEpisode).toContainText('第1話');
await expect(firstEpisode).toHaveScreenshot('vrt-episode.png');
});
Expand Down
5 changes: 3 additions & 2 deletions workspaces/testing/src/client/episode.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { devices, expect, test } from '@playwright/test';

import { waitForAllImagesToLoad } from '../utils';

const BOOK_ID = '670abeed-7c82-4d7a-a997-cd86c362b9b8';
const EPISODE_ID = 'fe2c26de-6bf7-4564-a4dc-d0b88cba8b22';

Expand Down Expand Up @@ -94,8 +96,7 @@ test.describe('エピソード', () => {
const section = page.getByRole('region', { name: 'エピソード一覧' });
await expect(section).toBeVisible();
const firstEpisode = section.getByRole('listitem').first();
const firstEpisodeImg = firstEpisode.getByRole('img');
await expect(firstEpisodeImg).toBeVisible();
await waitForAllImagesToLoad(firstEpisode, 2);
await expect(firstEpisode).toContainText('第1話');
await expect(firstEpisode).toHaveScreenshot('vrt-episode.png');
});
Expand Down
6 changes: 3 additions & 3 deletions workspaces/testing/src/client/search.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { expect, test } from '@playwright/test';

import { waitForAllImagesToLoad } from '../utils';

test.describe('検索', () => {
test.beforeEach(async ({ page }) => {
// Given
Expand Down Expand Up @@ -100,9 +102,7 @@ test.describe('検索', () => {
const section = page.getByRole('region', { name: '検索結果' });
const list = section.getByRole('list');
const items = list.getByRole('listitem');
const img = items.nth(0).getByRole('img', { name: // });
await expect(img).toBeVisible();
await expect(items.nth(0)).toContainText('日常');
await waitForAllImagesToLoad(items.nth(0), 2);
await expect(items.nth(0)).toHaveScreenshot('vrt-search-result.png');
});

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
76 changes: 48 additions & 28 deletions workspaces/testing/src/client/top.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import path from 'node:path';

import { expect, test } from '@playwright/test';

import { waitForAllImagesToLoad, waitForImageToLoad } from '../utils';

test.describe('サービストップ', () => {
test.beforeEach(async ({ context, page }) => {
await context.addInitScript({
Expand All @@ -20,7 +22,7 @@ test.describe('サービストップ', () => {
// Then
const header = page.getByRole('banner');
const heroImg = header.getByRole('img', { name: 'Cyber TOON' });
await expect(heroImg).toBeVisible();
await waitForImageToLoad(heroImg);
await expect(heroImg).toHaveScreenshot('vrt-hero-img.png');
});

Expand All @@ -34,35 +36,36 @@ test.describe('サービストップ', () => {
});

test('ピックアップセクションが表示されていること', async ({ page }) => {
// Then
// When
const section = page.getByRole('region', { name: 'ピックアップ' });
await expect(section).toBeVisible();
const firstCard = section.getByRole('link').first();
const firstCardImg = firstCard.getByRole('img').first();
await firstCardImg.waitFor();
await waitForAllImagesToLoad(firstCard, 2);

// Then
await expect(firstCard).toHaveScreenshot('vrt-pickup-card.png');
});

test('ピックアップセクションで横スクロールバーが表示されていること', async ({ page }) => {
// Then
// When
const section = page.getByRole('region', { name: 'ピックアップ' });
const firstCard = section.getByRole('link').first();
const firstCardImg = firstCard.getByRole('img').first();
await section.scrollIntoViewIfNeeded();
await expect(firstCard).toBeVisible();
await section.scrollIntoViewIfNeeded();
await expect(firstCardImg).toBeVisible();
await firstCard.evaluate((card) => {
await waitForAllImagesToLoad(firstCard, 2);
await waitForAllImagesToLoad(section, 3);

// Then
const hasScroll = firstCard.evaluate((card) => {
const wrapper = card.parentNode as HTMLDivElement;
return wrapper.scrollWidth > wrapper.clientWidth;
});
await expect(hasScroll).resolves.toBeTruthy();
});

test('ピックアップセクションでタブキーを10回押すと、11番目のカードが表示されること', async ({ page }) => {
// Given
const section = page.getByRole('region', { name: 'ピックアップ' });
const firstCard = section.getByRole('link').first();
await expect(firstCard).toBeVisible();
await waitForAllImagesToLoad(firstCard, 2);
await waitForAllImagesToLoad(section, 3);
await firstCard.focus();

// When
Expand Down Expand Up @@ -91,12 +94,12 @@ test.describe('サービストップ', () => {
});

test('ランキングセクションが表示されていること', async ({ page }) => {
// Then
// When
const section = page.getByRole('region', { name: 'ランキング' });
await expect(section).toBeVisible();
const firstCard = section.getByRole('listitem').first();
const firstCardImg = firstCard.getByRole('img').first();
await firstCardImg.waitFor();
await waitForAllImagesToLoad(firstCard, 2);

// Then
await expect(firstCard).toContainText('この漫画を読む');
await expect(firstCard).toHaveScreenshot('vrt-ranking-card.png');
});
Expand All @@ -112,28 +115,45 @@ test.describe('サービストップ', () => {
});

test('本日更新セクションが表示されていること', async ({ page }) => {
// Then
// When
const section = page.getByRole('region', { name: '本日更新' });
await expect(section).toBeVisible();
const firstCard = section.getByRole('link').first();
const firstCardImg = firstCard.getByRole('img').first();
await firstCardImg.waitFor();
await waitForAllImagesToLoad(firstCard, 2);
await expect(async () => {
expect(
await (
await firstCard.evaluateHandle((element, prop) => {
return element[prop as keyof typeof element];
}, 'clientHeight')
).jsonValue(),
).toBeLessThan(245);
}).toPass();

// Then
await expect(firstCard).toHaveScreenshot('vrt-today-updates-card.png');
});

test('本日更新セクションで横スクロールバーが表示されていること', async ({ page }) => {
// Then
// When
const section = page.getByRole('region', { name: '本日更新' });
const firstCard = section.getByRole('link').first();
const firstCardImg = firstCard.getByRole('img').first();
await section.scrollIntoViewIfNeeded();
await expect(firstCard).toBeVisible();
await section.scrollIntoViewIfNeeded();
await expect(firstCardImg).toBeVisible();
await firstCard.evaluate((card) => {
await waitForAllImagesToLoad(section, 4);
await expect(async () => {
expect(
await (
await firstCard.evaluateHandle((element, prop) => {
return element[prop as keyof typeof element];
}, 'clientHeight')
).jsonValue(),
).toBeLessThan(245);
}).toPass();

// Then
const hasScroll = firstCard.evaluate((card) => {
const wrapper = card.parentNode as HTMLDivElement;
return wrapper.scrollWidth > wrapper.clientWidth;
});
await expect(hasScroll).resolves.toBeTruthy();
});

test('本日更新セクションの先頭のカードをクリックすると、作品詳細ページに遷移すること', async ({ page }) => {
Expand Down
Loading

0 comments on commit ab92d96

Please sign in to comment.