diff --git a/test1/playwright.config.ts b/test1/playwright.config.ts index a505b69..523748e 100644 --- a/test1/playwright.config.ts +++ b/test1/playwright.config.ts @@ -22,7 +22,7 @@ export default defineConfig({ /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, /* Retry on CI only */ - retries: process.env.CI ? 2 :2, + retries: process.env.CI ? 2 :0, /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ diff --git a/test3/images/image-4.png b/test3/images/image-4.png new file mode 100644 index 0000000..e7ed117 Binary files /dev/null and b/test3/images/image-4.png differ diff --git a/test3/playwright.config.ts b/test3/playwright.config.ts index 81986e6..01568d0 100644 --- a/test3/playwright.config.ts +++ b/test3/playwright.config.ts @@ -12,6 +12,10 @@ import { defineConfig, devices } from '@playwright/test'; */ export default defineConfig({ testDir: './tests', + testMatch:[ + 'basic.spec.ts', + 'llm4.spec.ts' + ], /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ @@ -39,7 +43,7 @@ export default defineConfig({ use: { ...devices['Desktop Chrome'] }, }, - { + /*{ name: 'firefox', use: { ...devices['Desktop Firefox'] }, }, @@ -47,7 +51,7 @@ export default defineConfig({ { name: 'webkit', use: { ...devices['Desktop Safari'] }, - }, + },*/ /* Test against mobile viewports. */ // { diff --git a/test3/prompts_record.md b/test3/prompts_record.md index 4cc3858..f70c8a1 100644 --- a/test3/prompts_record.md +++ b/test3/prompts_record.md @@ -1,4 +1,4 @@ -## P0 Prompt 1 +## P0 Prompt 0 我直接把「飲料訂購系統.pdf」塞給claude ### 成果 ![alt text](./images/image.png) @@ -10,7 +10,7 @@ bug: 飲料訂購那些資訊需要塞成一個橫排的表格,如pdf中圖3-1 不算bug的bug: 題目會給一個csv,但我沒有丟csv給claude,所以他在js那邊模擬了一個csv,很有想法 -## P0 Prompt 2 +## P0 Prompt 1 按照成果1的bug去修,我丟給他以下截圖給的Prompt為「我希望飲料訂購部分的排版跟這個一樣,並重新給我完整的html5,css,js檔案」 ![alt text](./images/image-2.png) @@ -25,3 +25,34 @@ bug: 飲料訂購那些資訊需要塞成一個橫排的表格,如pdf中圖3-1 ### 成果 跟上一個成果沒什麼不一樣 +### p2 Prompt 0 +我把「html5,css,js」丟給Claude,並且給他這樣的Prompt:「現在要用playwright進行端對端測試,針對每一個需求進行最詳盡的測試並且給我,預設使用typerscript 去生成playwright腳本,對於每個測試腳本要註解是測試的需求是哪個,並且盡量用data-testid和aria-label去做selector,給我完整的測試腳本」 + +### 成果 +Claude給了這些測試腳本,有些部分測試會是爛掉的,像是「溫度選項正確」、「甜度選項正確」、「結帳流程」,因此我讓Claude一個一個慢慢改 + +![alt text](./images/image-4.png) + +### p2 Prompt 1 (修改「溫度選項正確」) +Prompt:「溫度選項正確這一個測試是錯的,我要好好的打開combobox,去看裡面的東西是不是為visible的,請幫我修改這個測試」 + +### 成果 +「溫度選項正確」是對的 + +### p2 Prompt 2(修改「甜度選項正確」) +Prompt:「甜度選項正確這一個測試是錯的,我要好好的打開combobox,去看裡面的東西是不是為visible的,請幫我修改這個測試」 + +### 成果 +「甜度選項正確」是錯的,他根本沒有好好的打開combobox接著check裡面的東西 + +### p2 Prompt 3(修改「甜度選項正確」) +在check visible的時候出現問題,請麻煩改正它 + +### 成果 +「溫度選項正確」是對的 + +### p2 Prompt 4修改「結帳流程顯示訂單摘要並清空訂購清單」 +最後一個測試:「結帳流程顯示訂單摘要並清空訂購清單」也是錯的,在checkoutButton.click時出了問題,請修他 + +### 成果 +「結帳流程顯示訂單摘要並清空訂購清單」對了,可喜可賀 diff --git a/test3/tests/llm0.spec.ts b/test3/tests/llm0.spec.ts new file mode 100644 index 0000000..bff0dfa --- /dev/null +++ b/test3/tests/llm0.spec.ts @@ -0,0 +1,140 @@ +import { test, expect } from '@playwright/test'; + +test.describe('飲料訂購系統', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:8080'); // 根據需要調整 URL + }); + + // 需求 1: 使用者界面元素 + test('使用者界面元素存在且正確佈局', async ({ page }) => { + // 檢查主要元素是否存在 + await expect(page.getByRole('heading', { name: '來點什麼...' })).toBeVisible(); + await expect(page.getByTestId('price-display')).toBeVisible(); + await expect(page.getByTestId('total-display')).toBeVisible(); + await expect(page.getByRole('button', { name: '確認' })).toBeVisible(); + + // 檢查下拉式選單元素 + await expect(page.getByRole('combobox', { name: '品名' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '容量' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '溫度' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '甜度' })).toBeVisible(); + + // 檢查輸入元素 + await expect(page.getByRole('spinbutton', { name: '數量' })).toBeVisible(); + await expect(page.getByRole('textbox', { name: '訂購人' })).toBeVisible(); + }); + + // 需求 2a, 2b, 2c: 品名和容量選項 + test('品名和容量選項正確填充', async ({ page }) => { + const itemSelect = page.getByRole('combobox', { name: '品名' }); + await expect(itemSelect).toHaveCount(1); + + const itemOptions = await itemSelect.getByRole('option').all(); + expect(itemOptions.length).toBeGreaterThan(0); + expect(itemOptions.length).toBeLessThanOrEqual(10); + + // 選擇第一個品項並檢查容量選項 + await itemSelect.selectOption({ index: 0 }); + const sizeSelect = page.getByRole('combobox', { name: '容量' }); + const sizeOptions = await sizeSelect.getByRole('option').all(); + expect(sizeOptions.length).toBeGreaterThan(0); + }); + + // 需求 2d, 3: 價格和總金額計算 + test('價格和總金額計算正確', async ({ page }) => { + const itemSelect = page.getByRole('combobox', { name: '品名' }); + const sizeSelect = page.getByRole('combobox', { name: '容量' }); + const quantityInput = page.getByRole('spinbutton', { name: '數量' }); + const priceDisplay = page.getByTestId('price-display'); + const totalDisplay = page.getByTestId('total-display'); + + await itemSelect.selectOption({ index: 0 }); + await sizeSelect.selectOption({ index: 0 }); + await expect(priceDisplay).not.toHaveText('0'); + + await quantityInput.fill('2'); + const price = await priceDisplay.innerText(); + const expectedTotal = (parseInt(price) * 2).toString(); + await expect(totalDisplay).toHaveText(expectedTotal); + }); + + // 需求 4: 溫度選項 + test('溫度選項正確', async ({ page }) => { + const tempSelect = page.getByRole('combobox', { name: '溫度' }); + const options = ['熱', '全冰', '少冰', '去冰']; + for (const option of options) { + await expect(tempSelect.getByText(option)).toBeVisible(); + } + }); + + // 需求 5: 甜度選項 + test('甜度選項正確', async ({ page }) => { + const sweetSelect = page.getByRole('combobox', { name: '甜度' }); + const options = ['全糖', '8分糖', '5分糖', '3分糖', '1分糖', '無糖']; + for (const option of options) { + await expect(sweetSelect.getByText(option)).toBeVisible(); + } + }); + + // 需求 6, 7: 訂單確認和顯示在訂購清單中 + test('確認後訂單添加到訂購清單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const orderList = page.getByTestId('order-list'); + await expect(orderList.getByRole('row')).toHaveCount(2); // 表頭 + 1 個訂單 + await expect(orderList.getByRole('checkbox')).toBeVisible(); + }); + + // 需求 8: 刪除選定的訂單 + test('可以刪除選定的訂單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + await page.getByTestId('order-list').getByRole('checkbox').check(); + await page.getByRole('button', { name: '刪除勾選' }).click(); + await expect(page.getByTestId('order-list').getByRole('row')).toHaveCount(1); // 只剩表頭 + }); + + // 需求 9: 訂單摘要 + test('訂單摘要正確更新', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const summary = page.getByTestId('order-summary'); + await expect(summary).toContainText('總數量: 1'); + await expect(summary).toContainText('總金額: 30'); + }); + + // 需求 10: 自動填充客戶姓名 + test('第一筆訂單後自動填充客戶姓名', async ({ page }) => { + await fillOrderForm(page, '測試客戶'); + await page.getByRole('button', { name: '確認' }).click(); + await fillOrderForm(page); + const customerInput = page.getByRole('textbox', { name: '訂購人' }); + await expect(customerInput).toHaveValue('測試客戶'); + }); + + // 需求 11: 結帳流程 + test('結帳流程顯示訂單摘要並清空訂購清單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const checkoutButton = page.getByRole('button', { name: '結帳' }); + + const dialogPromise = page.waitForEvent('dialog'); + await checkoutButton.click(); + const dialog = await dialogPromise; + + expect(dialog.message()).toContain('訂單摘要'); + expect(dialog.message()).toContain('總金額'); + + await dialog.dismiss(); + await expect(page.getByTestId('order-list').getByRole('row')).toHaveCount(1); // 只剩表頭 + }); +}); + +async function fillOrderForm(page, customerName = '測試客戶') { + await page.getByRole('combobox', { name: '品名' }).selectOption({ index: 0 }); + await page.getByRole('combobox', { name: '容量' }).selectOption({ index: 0 }); + await page.getByRole('spinbutton', { name: '數量' }).fill('1'); + await page.getByRole('combobox', { name: '溫度' }).selectOption('全冰'); + await page.getByRole('combobox', { name: '甜度' }).selectOption('全糖'); + await page.getByRole('textbox', { name: '訂購人' }).fill(customerName); +} \ No newline at end of file diff --git a/test3/tests/llm1.spec.ts b/test3/tests/llm1.spec.ts new file mode 100644 index 0000000..8ae8973 --- /dev/null +++ b/test3/tests/llm1.spec.ts @@ -0,0 +1,155 @@ +import { test, expect } from '@playwright/test'; + +test.describe('飲料訂購系統', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:8080'); // 根據需要調整 URL + }); + + // 需求 1: 使用者界面元素 + test('使用者界面元素存在且正確佈局', async ({ page }) => { + // 檢查主要元素是否存在 + await expect(page.getByRole('heading', { name: '來點什麼...' })).toBeVisible(); + await expect(page.getByTestId('price-display')).toBeVisible(); + await expect(page.getByTestId('total-display')).toBeVisible(); + await expect(page.getByRole('button', { name: '確認' })).toBeVisible(); + + // 檢查下拉式選單元素 + await expect(page.getByRole('combobox', { name: '品名' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '容量' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '溫度' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '甜度' })).toBeVisible(); + + // 檢查輸入元素 + await expect(page.getByRole('spinbutton', { name: '數量' })).toBeVisible(); + await expect(page.getByRole('textbox', { name: '訂購人' })).toBeVisible(); + }); + + // 需求 2a, 2b, 2c: 品名和容量選項 + test('品名和容量選項正確填充', async ({ page }) => { + const itemSelect = page.getByRole('combobox', { name: '品名' }); + await expect(itemSelect).toHaveCount(1); + + const itemOptions = await itemSelect.getByRole('option').all(); + expect(itemOptions.length).toBeGreaterThan(0); + expect(itemOptions.length).toBeLessThanOrEqual(10); + + // 選擇第一個品項並檢查容量選項 + await itemSelect.selectOption({ index: 0 }); + const sizeSelect = page.getByRole('combobox', { name: '容量' }); + const sizeOptions = await sizeSelect.getByRole('option').all(); + expect(sizeOptions.length).toBeGreaterThan(0); + }); + + // 需求 2d, 3: 價格和總金額計算 + test('價格和總金額計算正確', async ({ page }) => { + const itemSelect = page.getByRole('combobox', { name: '品名' }); + const sizeSelect = page.getByRole('combobox', { name: '容量' }); + const quantityInput = page.getByRole('spinbutton', { name: '數量' }); + const priceDisplay = page.getByTestId('price-display'); + const totalDisplay = page.getByTestId('total-display'); + + await itemSelect.selectOption({ index: 0 }); + await sizeSelect.selectOption({ index: 0 }); + await expect(priceDisplay).not.toHaveText('0'); + + await quantityInput.fill('2'); + const price = await priceDisplay.innerText(); + const expectedTotal = (parseInt(price) * 2).toString(); + await expect(totalDisplay).toHaveText(expectedTotal); + }); + + // 需求 4: 溫度選項 + test('溫度選項正確', async ({ page }) => { + const tempSelect = page.getByRole('combobox', { name: '溫度' }); + + // 等待選擇器元素出現並可見 + await expect(tempSelect).toBeVisible({ timeout: 5000 }); + + const expectedOptions = ['熱', '全冰', '少冰', '去冰']; + + // 獲取實際的選項 + const actualOptions = await tempSelect.getByRole('option').allInnerTexts(); + + // 檢查所有預期的選項是否都存在,忽略順序 + for (const option of expectedOptions) { + expect(actualOptions).toContain(option); + } + + // 檢查選項數量是否正確 + expect(actualOptions.length).toBe(expectedOptions.length); + + // 如果需要檢查順序,可以使用以下代碼: + // expect(actualOptions).toEqual(expectedOptions); + }); + + // 需求 5: 甜度選項 + test('甜度選項正確', async ({ page }) => { + const sweetSelect = page.getByRole('combobox', { name: '甜度' }); + const options = ['全糖', '8分糖', '5分糖', '3分糖', '1分糖', '無糖']; + for (const option of options) { + await expect(sweetSelect.getByText(option)).toBeVisible(); + } + }); + + // 需求 6, 7: 訂單確認和顯示在訂購清單中 + test('確認後訂單添加到訂購清單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const orderList = page.getByTestId('order-list'); + await expect(orderList.getByRole('row')).toHaveCount(2); // 表頭 + 1 個訂單 + await expect(orderList.getByRole('checkbox')).toBeVisible(); + }); + + // 需求 8: 刪除選定的訂單 + test('可以刪除選定的訂單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + await page.getByTestId('order-list').getByRole('checkbox').check(); + await page.getByRole('button', { name: '刪除勾選' }).click(); + await expect(page.getByTestId('order-list').getByRole('row')).toHaveCount(1); // 只剩表頭 + }); + + // 需求 9: 訂單摘要 + test('訂單摘要正確更新', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const summary = page.getByTestId('order-summary'); + await expect(summary).toContainText('總數量: 1'); + await expect(summary).toContainText('總金額: 30'); + }); + + // 需求 10: 自動填充客戶姓名 + test('第一筆訂單後自動填充客戶姓名', async ({ page }) => { + await fillOrderForm(page, '測試客戶'); + await page.getByRole('button', { name: '確認' }).click(); + await fillOrderForm(page); + const customerInput = page.getByRole('textbox', { name: '訂購人' }); + await expect(customerInput).toHaveValue('測試客戶'); + }); + + // 需求 11: 結帳流程 + test('結帳流程顯示訂單摘要並清空訂購清單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const checkoutButton = page.getByRole('button', { name: '結帳' }); + + const dialogPromise = page.waitForEvent('dialog'); + await checkoutButton.click(); + const dialog = await dialogPromise; + + expect(dialog.message()).toContain('訂單摘要'); + expect(dialog.message()).toContain('總金額'); + + await dialog.dismiss(); + await expect(page.getByTestId('order-list').getByRole('row')).toHaveCount(1); // 只剩表頭 + }); +}); + +async function fillOrderForm(page, customerName = '測試客戶') { + await page.getByRole('combobox', { name: '品名' }).selectOption({ index: 0 }); + await page.getByRole('combobox', { name: '容量' }).selectOption({ index: 0 }); + await page.getByRole('spinbutton', { name: '數量' }).fill('1'); + await page.getByRole('combobox', { name: '溫度' }).selectOption('全冰'); + await page.getByRole('combobox', { name: '甜度' }).selectOption('全糖'); + await page.getByRole('textbox', { name: '訂購人' }).fill(customerName); +} \ No newline at end of file diff --git a/test3/tests/llm2.spec.ts b/test3/tests/llm2.spec.ts new file mode 100644 index 0000000..f86b346 --- /dev/null +++ b/test3/tests/llm2.spec.ts @@ -0,0 +1,178 @@ +import { test, expect } from '@playwright/test'; + +test.describe('飲料訂購系統', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:8080'); // 根據需要調整 URL + }); + + // 需求 1: 使用者界面元素 + test('使用者界面元素存在且正確佈局', async ({ page }) => { + // 檢查主要元素是否存在 + await expect(page.getByRole('heading', { name: '來點什麼...' })).toBeVisible(); + await expect(page.getByTestId('price-display')).toBeVisible(); + await expect(page.getByTestId('total-display')).toBeVisible(); + await expect(page.getByRole('button', { name: '確認' })).toBeVisible(); + + // 檢查下拉式選單元素 + await expect(page.getByRole('combobox', { name: '品名' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '容量' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '溫度' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '甜度' })).toBeVisible(); + + // 檢查輸入元素 + await expect(page.getByRole('spinbutton', { name: '數量' })).toBeVisible(); + await expect(page.getByRole('textbox', { name: '訂購人' })).toBeVisible(); + }); + + // 需求 2a, 2b, 2c: 品名和容量選項 + test('品名和容量選項正確填充', async ({ page }) => { + const itemSelect = page.getByRole('combobox', { name: '品名' }); + await expect(itemSelect).toHaveCount(1); + + const itemOptions = await itemSelect.getByRole('option').all(); + expect(itemOptions.length).toBeGreaterThan(0); + expect(itemOptions.length).toBeLessThanOrEqual(10); + + // 選擇第一個品項並檢查容量選項 + await itemSelect.selectOption({ index: 0 }); + const sizeSelect = page.getByRole('combobox', { name: '容量' }); + const sizeOptions = await sizeSelect.getByRole('option').all(); + expect(sizeOptions.length).toBeGreaterThan(0); + }); + + // 需求 2d, 3: 價格和總金額計算 + test('價格和總金額計算正確', async ({ page }) => { + const itemSelect = page.getByRole('combobox', { name: '品名' }); + const sizeSelect = page.getByRole('combobox', { name: '容量' }); + const quantityInput = page.getByRole('spinbutton', { name: '數量' }); + const priceDisplay = page.getByTestId('price-display'); + const totalDisplay = page.getByTestId('total-display'); + + await itemSelect.selectOption({ index: 0 }); + await sizeSelect.selectOption({ index: 0 }); + await expect(priceDisplay).not.toHaveText('0'); + + await quantityInput.fill('2'); + const price = await priceDisplay.innerText(); + const expectedTotal = (parseInt(price) * 2).toString(); + await expect(totalDisplay).toHaveText(expectedTotal); + }); + + // 需求 4: 溫度選項 + test('溫度選項正確', async ({ page }) => { + const tempSelect = page.getByRole('combobox', { name: '溫度' }); + + // 等待選擇器元素出現並可見 + await expect(tempSelect).toBeVisible({ timeout: 5000 }); + + const expectedOptions = ['熱', '全冰', '少冰', '去冰']; + + // 獲取實際的選項 + const actualOptions = await tempSelect.getByRole('option').allInnerTexts(); + + // 檢查所有預期的選項是否都存在,忽略順序 + for (const option of expectedOptions) { + expect(actualOptions).toContain(option); + } + + // 檢查選項數量是否正確 + expect(actualOptions.length).toBe(expectedOptions.length); + + // 如果需要檢查順序,可以使用以下代碼: + // expect(actualOptions).toEqual(expectedOptions); + }); + + // 需求 5: 甜度選項 + test('甜度選項正確', async ({ page }) => { + const sweetSelect = page.getByRole('combobox', { name: '甜度' }); + + // 等待選擇器元素出現並可見 + await expect(sweetSelect).toBeVisible({ timeout: 5000 }); + + // 點擊 combobox 以打開選項列表 + await sweetSelect.click(); + + const expectedOptions = ['全糖', '8分糖', '5分糖', '3分糖', '1分糖', '無糖']; + + for (const option of expectedOptions) { + // 使用 getByRole 和 name 選項來定位每個選項 + const optionElement = sweetSelect.getByRole('option', { name: option }); + + // 等待選項可見並檢查 + await expect(optionElement).toBeVisible({ timeout: 2000 }); + + // 額外檢查:確保選項文本正確 + const optionText = await optionElement.innerText(); + expect(optionText.trim()).toBe(option); + } + + // 檢查選項數量是否正確 + const allOptions = await sweetSelect.getByRole('option').all(); + expect(allOptions.length).toBe(expectedOptions.length); + + // 關閉 combobox(可選,取決於您的 UI 行為) + await page.keyboard.press('Escape'); + }); + + // 需求 6, 7: 訂單確認和顯示在訂購清單中 + test('確認後訂單添加到訂購清單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const orderList = page.getByTestId('order-list'); + await expect(orderList.getByRole('row')).toHaveCount(2); // 表頭 + 1 個訂單 + await expect(orderList.getByRole('checkbox')).toBeVisible(); + }); + + // 需求 8: 刪除選定的訂單 + test('可以刪除選定的訂單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + await page.getByTestId('order-list').getByRole('checkbox').check(); + await page.getByRole('button', { name: '刪除勾選' }).click(); + await expect(page.getByTestId('order-list').getByRole('row')).toHaveCount(1); // 只剩表頭 + }); + + // 需求 9: 訂單摘要 + test('訂單摘要正確更新', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const summary = page.getByTestId('order-summary'); + await expect(summary).toContainText('總數量: 1'); + await expect(summary).toContainText('總金額: 30'); + }); + + // 需求 10: 自動填充客戶姓名 + test('第一筆訂單後自動填充客戶姓名', async ({ page }) => { + await fillOrderForm(page, '測試客戶'); + await page.getByRole('button', { name: '確認' }).click(); + await fillOrderForm(page); + const customerInput = page.getByRole('textbox', { name: '訂購人' }); + await expect(customerInput).toHaveValue('測試客戶'); + }); + + // 需求 11: 結帳流程 + test('結帳流程顯示訂單摘要並清空訂購清單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const checkoutButton = page.getByRole('button', { name: '結帳' }); + + const dialogPromise = page.waitForEvent('dialog'); + await checkoutButton.click(); + const dialog = await dialogPromise; + + expect(dialog.message()).toContain('訂單摘要'); + expect(dialog.message()).toContain('總金額'); + + await dialog.dismiss(); + await expect(page.getByTestId('order-list').getByRole('row')).toHaveCount(1); // 只剩表頭 + }); +}); + +async function fillOrderForm(page, customerName = '測試客戶') { + await page.getByRole('combobox', { name: '品名' }).selectOption({ index: 0 }); + await page.getByRole('combobox', { name: '容量' }).selectOption({ index: 0 }); + await page.getByRole('spinbutton', { name: '數量' }).fill('1'); + await page.getByRole('combobox', { name: '溫度' }).selectOption('全冰'); + await page.getByRole('combobox', { name: '甜度' }).selectOption('全糖'); + await page.getByRole('textbox', { name: '訂購人' }).fill(customerName); +} \ No newline at end of file diff --git a/test3/tests/llm3.spec.ts b/test3/tests/llm3.spec.ts new file mode 100644 index 0000000..8c60a88 --- /dev/null +++ b/test3/tests/llm3.spec.ts @@ -0,0 +1,185 @@ +import { test, expect } from '@playwright/test'; + +test.describe('飲料訂購系統', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:8080'); // 根據需要調整 URL + }); + + // 需求 1: 使用者界面元素 + test('使用者界面元素存在且正確佈局', async ({ page }) => { + // 檢查主要元素是否存在 + await expect(page.getByRole('heading', { name: '來點什麼...' })).toBeVisible(); + await expect(page.getByTestId('price-display')).toBeVisible(); + await expect(page.getByTestId('total-display')).toBeVisible(); + await expect(page.getByRole('button', { name: '確認' })).toBeVisible(); + + // 檢查下拉式選單元素 + await expect(page.getByRole('combobox', { name: '品名' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '容量' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '溫度' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '甜度' })).toBeVisible(); + + // 檢查輸入元素 + await expect(page.getByRole('spinbutton', { name: '數量' })).toBeVisible(); + await expect(page.getByRole('textbox', { name: '訂購人' })).toBeVisible(); + }); + + // 需求 2a, 2b, 2c: 品名和容量選項 + test('品名和容量選項正確填充', async ({ page }) => { + const itemSelect = page.getByRole('combobox', { name: '品名' }); + await expect(itemSelect).toHaveCount(1); + + const itemOptions = await itemSelect.getByRole('option').all(); + expect(itemOptions.length).toBeGreaterThan(0); + expect(itemOptions.length).toBeLessThanOrEqual(10); + + // 選擇第一個品項並檢查容量選項 + await itemSelect.selectOption({ index: 0 }); + const sizeSelect = page.getByRole('combobox', { name: '容量' }); + const sizeOptions = await sizeSelect.getByRole('option').all(); + expect(sizeOptions.length).toBeGreaterThan(0); + }); + + // 需求 2d, 3: 價格和總金額計算 + test('價格和總金額計算正確', async ({ page }) => { + const itemSelect = page.getByRole('combobox', { name: '品名' }); + const sizeSelect = page.getByRole('combobox', { name: '容量' }); + const quantityInput = page.getByRole('spinbutton', { name: '數量' }); + const priceDisplay = page.getByTestId('price-display'); + const totalDisplay = page.getByTestId('total-display'); + + await itemSelect.selectOption({ index: 0 }); + await sizeSelect.selectOption({ index: 0 }); + await expect(priceDisplay).not.toHaveText('0'); + + await quantityInput.fill('2'); + const price = await priceDisplay.innerText(); + const expectedTotal = (parseInt(price) * 2).toString(); + await expect(totalDisplay).toHaveText(expectedTotal); + }); + + // 需求 4: 溫度選項 + test('溫度選項正確', async ({ page }) => { + const tempSelect = page.getByRole('combobox', { name: '溫度' }); + + // 等待選擇器元素出現並可見 + await expect(tempSelect).toBeVisible({ timeout: 5000 }); + + const expectedOptions = ['熱', '全冰', '少冰', '去冰']; + + // 獲取實際的選項 + const actualOptions = await tempSelect.getByRole('option').allInnerTexts(); + + // 檢查所有預期的選項是否都存在,忽略順序 + for (const option of expectedOptions) { + expect(actualOptions).toContain(option); + } + + // 檢查選項數量是否正確 + expect(actualOptions.length).toBe(expectedOptions.length); + + // 如果需要檢查順序,可以使用以下代碼: + // expect(actualOptions).toEqual(expectedOptions); + }); + + // 需求 5: 甜度選項 + test('甜度選項正確', async ({ page }) => { + const sweetSelect = page.getByRole('combobox', { name: '甜度' }); + + // 等待選擇器元素出現並可交互 + await expect(sweetSelect).toBeEnabled({ timeout: 5000 }); + + // 點擊 combobox 以打開選項列表 + await sweetSelect.click(); + + const expectedOptions = ['全糖', '8分糖', '5分糖', '3分糖', '1分糖', '無糖']; + + for (const option of expectedOptions) { + // 使用 getByRole 和 name 選項來定位每個選項 + const optionElement = sweetSelect.getByRole('option', { name: option }); + + // 等待選項存在於 DOM 中 + await expect(optionElement).toBeAttached({ timeout: 2000 }); + + // 檢查選項是否可以被選擇 + await expect(optionElement).toBeEnabled(); + + // 額外檢查:確保選項文本正確 + const optionText = await optionElement.innerText(); + expect(optionText.trim()).toBe(option); + } + + // 檢查選項數量是否正確 + const allOptions = await sweetSelect.getByRole('option').all(); + expect(allOptions.length).toBe(expectedOptions.length); + + // 選擇一個選項來驗證功能 + await sweetSelect.selectOption(expectedOptions[0]); + await expect(sweetSelect).toHaveValue(expectedOptions[0]); + + // 關閉 combobox(可選,取決於您的 UI 行為) + await page.keyboard.press('Escape'); + }); + + // 需求 6, 7: 訂單確認和顯示在訂購清單中 + test('確認後訂單添加到訂購清單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const orderList = page.getByTestId('order-list'); + await expect(orderList.getByRole('row')).toHaveCount(2); // 表頭 + 1 個訂單 + await expect(orderList.getByRole('checkbox')).toBeVisible(); + }); + + // 需求 8: 刪除選定的訂單 + test('可以刪除選定的訂單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + await page.getByTestId('order-list').getByRole('checkbox').check(); + await page.getByRole('button', { name: '刪除勾選' }).click(); + await expect(page.getByTestId('order-list').getByRole('row')).toHaveCount(1); // 只剩表頭 + }); + + // 需求 9: 訂單摘要 + test('訂單摘要正確更新', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const summary = page.getByTestId('order-summary'); + await expect(summary).toContainText('總數量: 1'); + await expect(summary).toContainText('總金額: 30'); + }); + + // 需求 10: 自動填充客戶姓名 + test('第一筆訂單後自動填充客戶姓名', async ({ page }) => { + await fillOrderForm(page, '測試客戶'); + await page.getByRole('button', { name: '確認' }).click(); + await fillOrderForm(page); + const customerInput = page.getByRole('textbox', { name: '訂購人' }); + await expect(customerInput).toHaveValue('測試客戶'); + }); + + // 需求 11: 結帳流程 + test('結帳流程顯示訂單摘要並清空訂購清單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const checkoutButton = page.getByRole('button', { name: '結帳' }); + + const dialogPromise = page.waitForEvent('dialog'); + await checkoutButton.click(); + const dialog = await dialogPromise; + + expect(dialog.message()).toContain('訂單摘要'); + expect(dialog.message()).toContain('總金額'); + + await dialog.dismiss(); + await expect(page.getByTestId('order-list').getByRole('row')).toHaveCount(1); // 只剩表頭 + }); +}); + +async function fillOrderForm(page, customerName = '測試客戶') { + await page.getByRole('combobox', { name: '品名' }).selectOption({ index: 0 }); + await page.getByRole('combobox', { name: '容量' }).selectOption({ index: 0 }); + await page.getByRole('spinbutton', { name: '數量' }).fill('1'); + await page.getByRole('combobox', { name: '溫度' }).selectOption('全冰'); + await page.getByRole('combobox', { name: '甜度' }).selectOption('全糖'); + await page.getByRole('textbox', { name: '訂購人' }).fill(customerName); +} \ No newline at end of file diff --git a/test3/tests/llm4.spec.ts b/test3/tests/llm4.spec.ts new file mode 100644 index 0000000..fd8bd7e --- /dev/null +++ b/test3/tests/llm4.spec.ts @@ -0,0 +1,205 @@ +import { test, expect } from '@playwright/test'; + +test.describe('飲料訂購系統', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:8080'); // 根據需要調整 URL + }); + + // 需求 1: 使用者界面元素 + test('使用者界面元素存在且正確佈局', async ({ page }) => { + // 檢查主要元素是否存在 + await expect(page.getByRole('heading', { name: '來點什麼...' })).toBeVisible(); + await expect(page.getByTestId('price-display')).toBeVisible(); + await expect(page.getByTestId('total-display')).toBeVisible(); + await expect(page.getByRole('button', { name: '確認' })).toBeVisible(); + + // 檢查下拉式選單元素 + await expect(page.getByRole('combobox', { name: '品名' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '容量' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '溫度' })).toBeVisible(); + await expect(page.getByRole('combobox', { name: '甜度' })).toBeVisible(); + + // 檢查輸入元素 + await expect(page.getByRole('spinbutton', { name: '數量' })).toBeVisible(); + await expect(page.getByRole('textbox', { name: '訂購人' })).toBeVisible(); + }); + + // 需求 2a, 2b, 2c: 品名和容量選項 + test('品名和容量選項正確填充', async ({ page }) => { + const itemSelect = page.getByRole('combobox', { name: '品名' }); + await expect(itemSelect).toHaveCount(1); + + const itemOptions = await itemSelect.getByRole('option').all(); + expect(itemOptions.length).toBeGreaterThan(0); + expect(itemOptions.length).toBeLessThanOrEqual(10); + + // 選擇第一個品項並檢查容量選項 + await itemSelect.selectOption({ index: 0 }); + const sizeSelect = page.getByRole('combobox', { name: '容量' }); + const sizeOptions = await sizeSelect.getByRole('option').all(); + expect(sizeOptions.length).toBeGreaterThan(0); + }); + + // 需求 2d, 3: 價格和總金額計算 + test('價格和總金額計算正確', async ({ page }) => { + const itemSelect = page.getByRole('combobox', { name: '品名' }); + const sizeSelect = page.getByRole('combobox', { name: '容量' }); + const quantityInput = page.getByRole('spinbutton', { name: '數量' }); + const priceDisplay = page.getByTestId('price-display'); + const totalDisplay = page.getByTestId('total-display'); + + await itemSelect.selectOption({ index: 0 }); + await sizeSelect.selectOption({ index: 0 }); + await expect(priceDisplay).not.toHaveText('0'); + + await quantityInput.fill('2'); + const price = await priceDisplay.innerText(); + const expectedTotal = (parseInt(price) * 2).toString(); + await expect(totalDisplay).toHaveText(expectedTotal); + }); + + // 需求 4: 溫度選項 + test('溫度選項正確', async ({ page }) => { + const tempSelect = page.getByRole('combobox', { name: '溫度' }); + + // 等待選擇器元素出現並可見 + await expect(tempSelect).toBeVisible({ timeout: 5000 }); + + const expectedOptions = ['熱', '全冰', '少冰', '去冰']; + + // 獲取實際的選項 + const actualOptions = await tempSelect.getByRole('option').allInnerTexts(); + + // 檢查所有預期的選項是否都存在,忽略順序 + for (const option of expectedOptions) { + expect(actualOptions).toContain(option); + } + + // 檢查選項數量是否正確 + expect(actualOptions.length).toBe(expectedOptions.length); + + // 如果需要檢查順序,可以使用以下代碼: + // expect(actualOptions).toEqual(expectedOptions); + }); + + // 需求 5: 甜度選項 + test('甜度選項正確', async ({ page }) => { + const sweetSelect = page.getByRole('combobox', { name: '甜度' }); + + // 等待選擇器元素出現並可交互 + await expect(sweetSelect).toBeEnabled({ timeout: 5000 }); + + // 點擊 combobox 以打開選項列表 + await sweetSelect.click(); + + const expectedOptions = ['全糖', '8分糖', '5分糖', '3分糖', '1分糖', '無糖']; + + for (const option of expectedOptions) { + // 使用 getByRole 和 name 選項來定位每個選項 + const optionElement = sweetSelect.getByRole('option', { name: option }); + + // 等待選項存在於 DOM 中 + await expect(optionElement).toBeAttached({ timeout: 2000 }); + + // 檢查選項是否可以被選擇 + await expect(optionElement).toBeEnabled(); + + // 額外檢查:確保選項文本正確 + const optionText = await optionElement.innerText(); + expect(optionText.trim()).toBe(option); + } + + // 檢查選項數量是否正確 + const allOptions = await sweetSelect.getByRole('option').all(); + expect(allOptions.length).toBe(expectedOptions.length); + + // 選擇一個選項來驗證功能 + await sweetSelect.selectOption(expectedOptions[0]); + await expect(sweetSelect).toHaveValue(expectedOptions[0]); + + // 關閉 combobox(可選,取決於您的 UI 行為) + await page.keyboard.press('Escape'); + }); + + // 需求 6, 7: 訂單確認和顯示在訂購清單中 + test('確認後訂單添加到訂購清單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const orderList = page.getByTestId('order-list'); + await expect(orderList.getByRole('row')).toHaveCount(2); // 表頭 + 1 個訂單 + await expect(orderList.getByRole('checkbox')).toBeVisible(); + }); + + // 需求 8: 刪除選定的訂單 + test('可以刪除選定的訂單', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + await page.getByTestId('order-list').getByRole('checkbox').check(); + await page.getByRole('button', { name: '刪除勾選' }).click(); + await expect(page.getByTestId('order-list').getByRole('row')).toHaveCount(1); // 只剩表頭 + }); + + // 需求 9: 訂單摘要 + test('訂單摘要正確更新', async ({ page }) => { + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + const summary = page.getByTestId('order-summary'); + await expect(summary).toContainText('總數量: 1'); + await expect(summary).toContainText('總金額: 30'); + }); + + // 需求 10: 自動填充客戶姓名 + test('第一筆訂單後自動填充客戶姓名', async ({ page }) => { + await fillOrderForm(page, '測試客戶'); + await page.getByRole('button', { name: '確認' }).click(); + await fillOrderForm(page); + const customerInput = page.getByRole('textbox', { name: '訂購人' }); + await expect(customerInput).toHaveValue('測試客戶'); + }); + + // 需求 11: 結帳流程 + test('結帳流程顯示訂單摘要並清空訂購清單', async ({ page }) => { + // 添加訂單 + await fillOrderForm(page); + await page.getByRole('button', { name: '確認' }).click(); + + // 確保訂單已經添加到清單中 + await expect(page.getByTestId('order-list').getByRole('row')).toHaveCount(2); // 表頭 + 1 個訂單 + + const checkoutButton = page.getByRole('button', { name: '結帳' }); + + // 等待結帳按鈕可用 + await expect(checkoutButton).toBeEnabled(); + + // 設置對話框處理 + page.on('dialog', async dialog => { + console.log('對話框信息:', dialog.message()); + expect(dialog.message()).toContain('訂單摘要'); + expect(dialog.message()).toContain('總金額'); + await dialog.accept(); + }); + + // 點擊結帳按鈕 + await checkoutButton.click(); + + // 等待一段時間,確保對話框已經處理完畢 + await page.waitForTimeout(1000); + + // 檢查訂購清單是否被清空 + await expect(page.getByTestId('order-list').getByRole('row')).toHaveCount(1, { timeout: 5000 }); // 只剩表頭 + + // 檢查訂單摘要是否被重置 + const summary = page.getByTestId('order-summary'); + await expect(summary).toContainText('總數量: 0'); + await expect(summary).toContainText('總金額: 0'); + }); +}); + +async function fillOrderForm(page, customerName = '測試客戶') { + await page.getByRole('combobox', { name: '品名' }).selectOption({ index: 0 }); + await page.getByRole('combobox', { name: '容量' }).selectOption({ index: 0 }); + await page.getByRole('spinbutton', { name: '數量' }).fill('1'); + await page.getByRole('combobox', { name: '溫度' }).selectOption('全冰'); + await page.getByRole('combobox', { name: '甜度' }).selectOption('全糖'); + await page.getByRole('textbox', { name: '訂購人' }).fill(customerName); +} \ No newline at end of file