Skip to content
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

test: ドラッグで長いノートを追加するテストと、複数ノートを一度に歌詞編集するテストを追加 #2561

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
88 changes: 38 additions & 50 deletions tests/e2e/browser/song/ソング.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { test, expect, Page } from "@playwright/test";

import { gotoHome, navigateToMain } from "../../navigators";
import { gotoHome, navigateToSong } from "../../navigators";
import { ensureNotNullish } from "@/helpers/errorHelper";

test.beforeEach(gotoHome);

async function navigateToSong(page: Page) {
await navigateToMain(page);
await expect(page.getByText("ソング")).toBeVisible();
await page.getByText("ソング").click();
}

async function getCurrentPlayhead(page: Page) {
const boundingBox = await page
.getByTestId("sequencer-playhead")
Expand All @@ -24,7 +19,7 @@ test("再生ボタンを押して再生できる", async ({ page }) => {

const sequencer = page.getByLabel("シーケンサ");

await sequencer.click({ position: { x: 107, y: 171 } }); // ノートを追加
await sequencer.click({ position: { x: 100, y: 171 } }); // ノートを追加
const beforePosition = await getCurrentPlayhead(page); // 再生ヘッドの初期位置
await page.getByText("play_arrow").click(); // 再生ボタンを押す
await page.waitForTimeout(3000);
Expand All @@ -42,56 +37,49 @@ test("ノートを追加・削除できる", async ({ page }) => {
const getCurrentNoteCount = async () =>
await sequencer.locator(".note").count();

// ノートの追加
expect(await getCurrentNoteCount()).toBe(0);
await sequencer.click({ position: { x: 107, y: 171 } });
expect(await getCurrentNoteCount()).toBe(1);
await sequencer.click({ position: { x: 200, y: 171 } });
expect(await getCurrentNoteCount()).toBe(2);

// ノートの削除
expect(await getCurrentNoteCount()).toBe(2);
await sequencer.click({ position: { x: 107, y: 171 } });
await page.keyboard.press("Delete");
expect(await getCurrentNoteCount()).toBe(1);
await sequencer.click({ position: { x: 200, y: 171 } });
await page.keyboard.press("Delete");
expect(await getCurrentNoteCount()).toBe(0);
await test.step("ノートの追加", async () => {
expect(await getCurrentNoteCount()).toBe(0);
await sequencer.click({ position: { x: 100, y: 171 } });
expect(await getCurrentNoteCount()).toBe(1);
await sequencer.click({ position: { x: 200, y: 171 } });
expect(await getCurrentNoteCount()).toBe(2);
});

await test.step("ノートの削除", async () => {
expect(await getCurrentNoteCount()).toBe(2);
await sequencer.click({ position: { x: 100, y: 171 } });
await page.keyboard.press("Delete");
expect(await getCurrentNoteCount()).toBe(1);
await sequencer.click({ position: { x: 200, y: 171 } });
await page.keyboard.press("Delete");
expect(await getCurrentNoteCount()).toBe(0);
});
});

test("ダブルクリックで歌詞を編集できる", async ({ page }) => {
test("ドラッグで長いノートを追加できる", async ({ page }) => {
await navigateToSong(page);

const sequencer = page.getByLabel("シーケンサ");

const getCurrentNoteLyric = async () =>
await sequencer.locator(".note-lyric").first().textContent();

// ノートを追加し、表示されるまで待つ
await sequencer.click({ position: { x: 107, y: 171 } });
await page.waitForSelector(".note");

// ノートの歌詞を取得
const note = sequencer.locator(".note").first();
const beforeLyric = await getCurrentNoteLyric();
await test.step("クリックで短いノートを追加", async () => {
await sequencer.click({ position: { x: 100, y: 171 } });
});

// ノートをダブルクリックし、入力フィールドが表示されるまで待つ
await note.dblclick();
await page.waitForSelector(".lyric-input");
await test.step("ドラッグで長いノートを追加", async () => {
const startPos = { x: 200, y: 171 };
const endPos = { x: 400, y: 171 };
await sequencer.hover({ position: startPos });
await page.mouse.down();
await page.mouse.move(endPos.x, endPos.y);
await page.mouse.up();
});

// 歌詞を入力し、Enterキーを押す
const lyricInput = sequencer.locator(".lyric-input");
await lyricInput.fill("あ");
await lyricInput.press("Enter");
await test.step("ノートが2つ表示されるのを待ち、2つ目のノートが長いことを確認", async () => {
const notes = sequencer.locator(".note");
await expect(notes).toHaveCount(2);

// 変更が反映されるまで待つ
await page.waitForFunction(() => {
const lyricElement = document.querySelector(".note-lyric");
return lyricElement && lyricElement.textContent === "あ";
const firstNoteBox = ensureNotNullish(await notes.nth(0).boundingBox());
const secondNoteBox = ensureNotNullish(await notes.nth(1).boundingBox());
expect(secondNoteBox.width).toBeGreaterThanOrEqual(firstNoteBox.width * 2);
});

// 歌詞が変更されたことを確認
const afterLyric = await getCurrentNoteLyric();
expect(afterLyric).not.toEqual(beforeLyric);
expect(afterLyric).toEqual("あ");
});
100 changes: 100 additions & 0 deletions tests/e2e/browser/song/歌詞.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { test, expect, Page, Locator } from "@playwright/test";

import { gotoHome, navigateToSong } from "../../navigators";
import { ensureNotNullish } from "@/helpers/errorHelper";

test.beforeEach(gotoHome);

function getSequencer(page: Page) {
return page.getByLabel("シーケンサ");
}

async function addNotes(page: Page, count: number) {
await test.step(`ノートを ${count} つ追加`, async () => {
const sequencer = getSequencer(page);
for (let i = 0; i < count; i++) {
await sequencer.click({ position: { x: (i + 1) * 100, y: 171 } });
}
const notes = sequencer.locator(".note");
await expect(notes).toHaveCount(count);
});
}

async function toSortedLocator(locators: Locator[]): Promise<Locator[]> {
const locatorsWithPosition = await Promise.all(
locators.map(async (locator) => ({
locator,
x: ensureNotNullish(await locator.boundingBox()).x,
})),
);
locatorsWithPosition.sort((a, b) => a.x - b.x);
return locatorsWithPosition.map(({ locator }) => locator);
}

async function getSortedNotes(page: Page): Promise<Locator[]> {
return await test.step("ノートをソートして取得", async () => {
const sequencer = getSequencer(page);
const notes = await sequencer.locator(".note").all();
return toSortedLocator(notes);
});
}

async function getSortedNoteLylics(page: Page): Promise<string[]> {
return await test.step("ノートをソートして歌詞を取得", async () => {
const sequencer = getSequencer(page);
const lyrics = await sequencer.locator(".note-lyric").all();
const sortedLyrics = await toSortedLocator(lyrics);
return Promise.all(
sortedLyrics.map(async (lyric) =>
ensureNotNullish(await lyric.textContent()),
),
);
});
}

async function editNoteLyric(page: Page, note: Locator, lyric: string) {
await test.step("ノートをダブルクリックして歌詞を入力", async () => {
await note.dblclick();

const sequencer = getSequencer(page);
const lyricInput = sequencer.locator(".lyric-input");
await expect(lyricInput).toBeVisible();
await lyricInput.fill(lyric);
await lyricInput.press("Enter");
await expect(lyricInput).not.toBeVisible();
});
}

test("ダブルクリックで歌詞を編集できる", async ({ page }) => {
await navigateToSong(page);

await addNotes(page, 1);
const note = (await getSortedNotes(page))[0];
const beforeLyric = (await getSortedNoteLylics(page))[0];

await editNoteLyric(page, note, "あ");

await test.step("歌詞が変更されていることを確認", async () => {
const afterLyric = await getSortedNoteLylics(page);
expect(afterLyric[0]).not.toEqual(beforeLyric);
expect(afterLyric[0]).toEqual("あ");
});
});

test("複数ノートの歌詞を一度に編集できる", async ({ page }) => {
await navigateToSong(page);

await addNotes(page, 3);

await editNoteLyric(page, (await getSortedNotes(page))[0], "あいう");
await test.step("全てのノートの歌詞が変更されていることを確認", async () => {
const afterLyrics = await getSortedNoteLylics(page);
expect(afterLyrics).toEqual(["あ", "い", "う"]);
});

await editNoteLyric(page, (await getSortedNotes(page))[0], "かきくけこ");
await test.step("最後のノートに残りの文字が入力されていることを確認", async () => {
const afterLyrics = await getSortedNoteLylics(page);
expect(afterLyrics).toEqual(["か", "き", "くけこ"]);
});
});
107 changes: 57 additions & 50 deletions tests/e2e/navigators.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,73 @@
import { expect, Page } from "@playwright/test";
import { expect, Locator, Page, test } from "@playwright/test";
import { getNewestQuasarDialog, getQuasarMenu } from "./locators";

/**
* 最初の画面に移動
*/
export async function gotoHome({ page }: { page: Page }) {
const BASE_URL = "http://localhost:7357/";
await page.setViewportSize({ width: 1024, height: 630 });
await page.goto(BASE_URL);
await test.step("最初の画面に移動", async () => {
const BASE_URL = "http://localhost:7357/";
await page.setViewportSize({ width: 1024, height: 630 });
await page.goto(BASE_URL);
});
}

/**
* 初回起動時の確認を完了してメイン画面に移動
*/
export async function navigateToMain(page: Page) {
await expect(page.getByText("利用規約に関するお知らせ")).toBeVisible({
timeout: 90 * 1000,
await test.step("初回起動時の確認を完了してメイン画面に移動", async () => {
await expect(page.getByText("利用規約に関するお知らせ")).toBeVisible({
timeout: 90 * 1000,
});
await page.waitForTimeout(100);
await page.getByRole("button", { name: "同意して使用開始" }).click();
await page.waitForTimeout(100);
await page.getByRole("button", { name: "完了" }).click();
await page.waitForTimeout(100);
await page.getByRole("button", { name: "許可" }).click();
await page.waitForTimeout(100);
});
await page.waitForTimeout(100);
await page.getByRole("button", { name: "同意して使用開始" }).click();
await page.waitForTimeout(100);
await page.getByRole("button", { name: "完了" }).click();
await page.waitForTimeout(100);
await page.getByRole("button", { name: "許可" }).click();
await page.waitForTimeout(100);
}

/**
* 特定の設定をトグルする
*/
export async function toggleSetting(page: Page, settingName: string) {
await page.getByRole("button", { name: "設定" }).click();
await page.waitForTimeout(100);
await page.getByText("オプション").click();
await page.waitForTimeout(100);
await page
.locator(".row-card", {
has: page.getByText(settingName),
})
.click();
await page.waitForTimeout(100);
await page.getByRole("button", { name: "設定を閉じる" }).click();
await test.step(`設定 ${settingName} をトグルする`, async () => {
await page.getByRole("button", { name: "設定" }).click();
await page.waitForTimeout(100);
await page.getByText("オプション").click();
await page.waitForTimeout(100);
await page
.locator(".row-card", {
has: page.getByText(settingName),
})
.click();
await page.waitForTimeout(100);
await page.getByRole("button", { name: "設定を閉じる" }).click();
});
await page.waitForTimeout(500);
}

/**
* ヘルプダイアログの表示まで移動
*/
export async function navigateToHelpDialog(page: Page) {
await navigateToMain(page);
await page.waitForTimeout(100);
await page.getByRole("button", { name: "ヘルプ" }).click();
return getNewestQuasarDialog(page);
export async function navigateToHelpDialog(page: Page): Promise<Locator> {
return await test.step("ヘルプダイアログの表示まで移動", async () => {
await navigateToMain(page);
await page.waitForTimeout(100);
await page.getByRole("button", { name: "ヘルプ" }).click();
return getNewestQuasarDialog(page);
});
}

export async function navigateToSettingDialog(page: Page): Promise<Locator> {
return await test.step("設定ダイアログの表示まで移動", async () => {
await navigateToMain(page);
await page.waitForTimeout(100);
await page.getByRole("button", { name: "設定" }).click();
await getQuasarMenu(page, "オプション").click();
return getNewestQuasarDialog(page);
});
}

/**
* 設定ダイアログの表示まで移動
*/
export async function navigateToSettingDialog(page: Page) {
await navigateToMain(page);
await page.waitForTimeout(100);
await page.getByRole("button", { name: "設定" }).click();
await getQuasarMenu(page, "オプション").click();
return getNewestQuasarDialog(page);
export async function navigateToSong(page: Page) {
await test.step("ソング画面に移動", async () => {
await navigateToMain(page);
await expect(page.getByText("ソング")).toBeVisible();
await page.getByText("ソング").click();

// 見やすいようにスナップを1/8に変更
await page.getByLabel("スナップ").click();
await page.getByRole("option", { name: "1/8", exact: true }).click();
});
}
Loading