iT邦幫忙

2024 iThome 鐵人賽

DAY 12
1

第一步老樣子就是來新增測試檔案。◝( •ω• )◟

e2e\components\text-characters-transition.spec.ts

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

test.beforeEach(async ({ page }) => {
  await page.goto('http://localhost:5173/components/text-characters-transition/');
});

test('頁面必須存在(title 不可出現 404)', async ({ page }) => {
  const title = await page.title();
  expect(title).not.toContain('404');
});

先將元件加上 title,方便定位。

docs\components\text-characters-transition\index.md

...

<basic-usage title="basic-usage" />
...

<custom-splitter title="custom-splitter" />

...

<transition-type title="transition-type" />

...

<custom-transition title="custom-transition" />

...

執行腳本開始運行 e2e 測試。

npm run test:e2e-ui

測試:基本用法

目前可以已知:

  • 有一個 checkbox
  • 兩段文字

讓我們新增測試吧。

e2e\components\text-characters-transition.spec.ts

...

test.describe('基本用法', () => {
  test('必須有文字為「基本用法」的 h3', async ({ page }) => {
    const h3Els = page.locator('h3');
    const target = h3Els.getByText('基本用法');
    await expect(target).toBeVisible();
  });

  test('必須包含一個 checkbox', async ({ page }) => {
    const section = page.getByTitle('basic-usage');
    await expect(section).toBeVisible();

    const checkbox = section.getByRole('checkbox');
    await expect(checkbox).toBeVisible();
  });

  test('必須包含兩段文字', async ({ page }) => {
    const section = page.getByTitle('basic-usage');
    await expect(section).toBeVisible();

    const paragraphs = section.locator('p');
    await expect(paragraphs).toHaveCount(2);
  });
})

相當順利。ヾ(◍'౪`◍)ノ゙

image.png

接著來測試元件行為,切換 checkbox 會讓文字開始過場。

e2e\components\text-characters-transition.spec.ts

...

test.describe('基本用法', () => {
  ...
  
  test('切換 checkbox 會讓文字開始過場', async ({ page }) => {
    const section = page.getByTitle('basic-usage');
    await expect(section).toBeVisible();

    /** 所有的 p 內的 span 的 opacity 都該為 0 */
    const spans = section.locator('p span');

    let spanStyles = await spans.evaluateAll((els) =>
      els.map((el) => window.getComputedStyle(el))
    )
    spanStyles.forEach((style) => {
      expect(style.opacity).toBe('0');
    });

    /** 切換 checkbox */
    const checkbox = section.getByRole('checkbox');
    await checkbox.check();
    await page.waitForTimeout(2000);

    /** 現在應該要變為 1 */
    spanStyles = await spans.evaluateAll((els) =>
      els.map((el) => window.getComputedStyle(el))
    )
    spanStyles.forEach((style) => {
      expect(style.opacity).toBe('1');
    });
  });
})

成功!( ‧ω‧)ノ╰(‧ω‧ )

image.png

測試:切分文字

這個測試相對單純,判斷每個 p 標籤內的 span 是否符合數量即可。

e2e\components\text-characters-transition.spec.ts

...

test.describe('切分文字', () => {
  test('必須有文字為「切分文字」的 h3', async ({ page }) => {
    const h3Els = page.locator('h3');
    const target = h3Els.getByText('切分文字');
    await expect(target).toBeVisible();
  });

  test('必須包含一個 checkbox', async ({ page }) => {
    const section = page.getByTitle('custom-splitter');
    await expect(section).toBeVisible();

    const checkbox = section.getByRole('checkbox');
    await expect(checkbox).toBeVisible();
  });

  test('第一段文字有 5 個 span,第二段文字有 6 個 span', async ({ page }) => {
    const section = page.getByTitle('custom-splitter');
    await expect(section).toBeVisible();

    const firstP = section.locator('p').first();
    const secondP = section.locator('p').nth(1);

    const firstSpans = firstP.locator('span');
    const secondSpans = secondP.locator('span');

    await expect(firstSpans).toHaveCount(5);
    await expect(secondSpans).toHaveCount(6);
  });
})

成功!◝( •ω• )◟

image.png

測試:轉場類型

這個部分需要針對每個不同的轉場區塊進行測試。

先加入基本測試,必須有 2 個 clickable-box。( •̀ ω •́ )✧

e2e\components\text-characters-transition.spec.ts

...

test.describe('轉場類型', () => {
  test('必須有文字為「轉場類型」的 h3', async ({ page }) => {
    const h3Els = page.locator('h3');
    const target = h3Els.getByText('轉場類型');
    await expect(target).toBeVisible();
  });

  test('必須包含 2 個 clickable-box', async ({ page }) => {
    const section = page.getByTitle('transition-type');
    await expect(section).toBeVisible();

    const list = section.locator('.clickable-box');
    await expect(list).toHaveCount(2);
  });
})

接著依序測試各種過場,第一個是 blur。

e2e\components\text-characters-transition.spec.ts

...

test.describe('轉場類型', () => {
  ...
  
  test('測試 blur 過場', async ({ page }) => {
    const section = page.getByTitle('transition-type');
    await expect(section).toBeVisible();

    /** 取得包含 blur 文字的 .clickable-box */
    const box = page.locator('.clickable-box:has-text("blur")');

    /**  一開始所有的 p 內的 span 的 opacity 都該為 0 */
    const spans = box.locator('p span');

    let spanStyles = await spans.evaluateAll((els) =>
      els.map((el) => window.getComputedStyle(el))
    )
    spanStyles.forEach((style) => {
      expect(style.opacity).toBe('0');
    });

    await box.click();
    await page.waitForTimeout(2000);

    /** 現在應該要變為 1 */
    spanStyles = await spans.evaluateAll((els) =>
      els.map((el) => window.getComputedStyle(el))
    )
    spanStyles.forEach((style) => {
      expect(style.opacity).toBe('1');
    });
  });
})

最後是 clip-right。

e2e\components\text-characters-transition.spec.ts

...

test.describe('轉場類型', () => {
  ...
  
  test('測試 clip-right 過場', async ({ page }) => {
    const section = page.getByTitle('transition-type');
    await expect(section).toBeVisible();

    /** 取得包含 clip-right 文字的 .clickable-box */
    const box = page.locator('.clickable-box:has-text("clip-right")');

    /** 所有的 span 的 clipPath 都該為 polygon(100% 0px, 100% 0px, 100% 100%, 100% 100%) */
    const spans = box.locator('p span');

    let spanStyles = await spans.evaluateAll((els) =>
      els.map((el) => window.getComputedStyle(el))
    )
    spanStyles.forEach((style) => {
      expect(style.clipPath).toBe('polygon(100% 0px, 100% 0px, 100% 100%, 100% 100%)');
    });

    await box.click();
    await page.waitForTimeout(2000);

    /** 現在應該要變為 polygon(0px 0px, 100% 0px, 100% 100%, 0% 100%) */
    spanStyles = await spans.evaluateAll((els) =>
      els.map((el) => window.getComputedStyle(el))
    )
    spanStyles.forEach((style) => {
      expect(style.clipPath).toBe('polygon(0px 0px, 100% 0px, 100% 100%, 0% 100%)');
    });
  });
})

成功!◝( •ω• )◟

image.png

測試:自定義轉場

此部分也很單純,依樣確認 opacity 即可。

e2e\components\text-characters-transition.spec.ts

...

test.describe('自定義轉場', () => {
  test('必須有文字為「自定義轉場」的 h3', async ({ page }) => {
    const h3Els = page.locator('h3');
    const target = h3Els.getByText('自定義轉場');
    await expect(target).toBeVisible();
  });

  test('確認所有轉場', async ({ page }) => {
    const section = page.getByTitle('custom-transition');
    await expect(section).toBeVisible();

    const boxes = await page.locator('.clickable-box').all();

    /** 所有的 p 內的 span 的 opacity 都該為 0 */
    const spans = section.locator('p span');

    let spanStyles = await spans.evaluateAll((els) =>
      els.map((el) => window.getComputedStyle(el))
    )
    spanStyles.forEach((style) => {
      expect(style.opacity).toBe('0');
    });

    /** 切換 */
    for (const box of boxes) {
      await box.click();
    }
    await page.waitForTimeout(3000);

    /** 現在應該要變為 1 */
    spanStyles = await spans.evaluateAll((els) =>
      els.map((el) => window.getComputedStyle(el))
    )
    spanStyles.forEach((style) => {
      expect(style.opacity).toBe('1');
    });
  });
})

簡單直接。◝(≧∀≦)◟

image.png

以上我們完成基本 e2e 測試了,大家可以想想還有甚麼更細緻的案例。

歡迎大家自由發揮!( ´ ▽ ` )ノ

總結

  • 完成「逐字轉場」介紹頁面 e2e 測試

以上程式碼已同步至 GitLab,大家可以前往下載:

GitLab - D12


上一篇
D11 - 逐字轉場:更多範例
下一篇
D13 - 拉炮:分析需求
系列文
要不要 Vue 點酷酷的元件?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言