classroom-wait-device-permissions

star 0

驗證 /classroom/wait 頁面的設備權限檢查流程,包括麥克風、攝影機、聲音權限申請與測試。

jvtutorcorner-sketch By jvtutorcorner-sketch schedule Updated 5/9/2026

name: classroom-wait-device-permissions

description: '驗證 /classroom/wait 頁面的設備權限檢查流程,包括麥克風、攝影機、聲音權限申請與測試。'

argument-hint: '執行設備權限檢查測試,驗證音視訊設備權限流程、設備列舉、並發測試。'

metadata:

verified-status: '✅ READY_FOR_TESTING'

last-verified-date: '2026-04-10'

test-count: 8

all-passing: true

architecture-aligned: true

related-skills:

- auto-login

- classroom-wait

- classroom-room-whiteboard-sync

設備權限檢查驗證技能

Mobile Optimization Standard (iOS & Android)

在設計或除錯移動端 UI 時,必須同時驗證 WebKit(iOS Safari)與 Blink(Android Chrome)的一致性:

  1. 高度適應:禁止單純使用 100vh 定位全螢幕元素。必須優先考慮動態視窗單位(dvh/svh)或使用 JS 動態計算 --vh,避免 iOS/Android 瀏覽器工具列遮擋內容。

  2. 邊距緩衝:所有 Fixed/Absolute 且貼齊邊緣的元素,必須使用 env(safe-area-inset-top)env(safe-area-inset-bottom)env(safe-area-inset-left)env(safe-area-inset-right)

  3. 寬度防禦:避免在移動端使用 vw 定義主要容器寬度,改用 %max-widthcalc(100% - padding),避免 Android 捲軸導致水平溢出。

  4. 渲染測試:修復與驗證步驟必須同時覆蓋 Safari(iOS)與 Chrome(Android),不得僅在單一引擎驗收。

快速啟動 (30 秒)


cd d:\jvtutorcorner-rwd

npx playwright test e2e/classroom-wait-device-permissions.spec.ts --project=chromium

環境前置.env.local 須包含以下變數:


NEXT_PUBLIC_BASE_URL=http://localhost:3000

LOGIN_BYPASS_SECRET=<YOUR_BYPASS_SECRET>

TEST_TEACHER_EMAIL=teacher@test.com

TEST_TEACHER_PASSWORD=<YOUR_PASSWORD>

TEST_STUDENT_EMAIL=student@test.com

TEST_STUDENT_PASSWORD=<YOUR_PASSWORD>

📊 完整測試概覽

測試檔案:e2e/classroom-wait-device-permissions.spec.ts

運行時間:~110 秒(8 個測試用例,均已通過 ✅)

測試用例列表

| # | 測試名稱 | 驗證項目 | 耗時 | 依賴項 |

|----|---------|--------|------|--------|

| 1 | Device Permission UI | 授予按鈕、麥克風/攝影機/聲音測試按鈕、設備檢查部分 | ~10s | 基礎 UI 元素 |

| 2 | Microphone Permission | 麥克風權限流程、按鈕啟用、音量指示器 | ~15s | getUserMedia API |

| 3 | Camera Permission | 攝影機權限、視頻預覽、<video> 元素渲染 | ~15s | 視頻流 API |

| 4 | Speaker/Audio Output | 聲音輸出測試按鈕、按鈕啟用狀態 | ~10s | 音訊輸出設備 |

| 5 | Device Check Flow | 權限 bypass、就緒按鈕狀態、設備檢查章節可見性 | ~15s | E2E_BYPASS_DEVICE_CHECK |

| 6 | Device Selection | 設備選擇器、設備列表、標籤結構 | ~12s | 設備列舉 |

| 7 | Initial State | 初始禁用狀態、權限授予按鈕存在 | ~12s | DOM 可見性 |

| 8 | Concurrent Testing | 教師+學生並發、BrowserContext 隔離、權限同步 | ~20s | 雙上下文隔離 |


🎯 核心驗證點

權限申請流程


// 1️⃣ 權限授予(音頻+視頻組合申請)

async function requestPermissions(): Promise<void> {

  const stream = await navigator.mediaDevices.getUserMedia({

    audio: true,

    video: { width: { ideal: 1280 }, height: { ideal: 720 } }

  });

  

  setPermissionGranted(true);  // ✅ 更新 UI 狀態

  enumerateDevices();           // 填充設備選擇器

  enableTestButtons();          // 啟用麥吣/攝影機/聲音測試按鈕

}

ESestabalishedapp/classroom/wait/page.tsx (VideoSetup 元件 L1073+)

設備測試函數

| 函數 | 職責 | 觸發 |

|-----|------|------|

| startCameraPreview() | 開啟視頻流,將其顯示到 <video srcObject={stream}> | "📹 預覽攝影機" 按鈕 |

| startMicTest() | 開啟麥克風,分析頻率數據(0-255),實時更新 UI 條形圖 | "🎤 測試麥克風" 按鈕 |

| testSpeaker() | 生成 440Hz 正弦波 (A4),播放 0.5 秒 | "🔊 測試聲音" 按鈕 |

| enumerateDevices() | 掃描系統設備,填充 <select> 選擇器 | 權限授予後 / devicechange 事件 |

設備列舉 & 自動選擇


// 掃描所有媒體設備

const devices = await navigator.mediaDevices.enumerateDevices();

const audioInputs = devices.filter(d => d.kind === 'audioinput');

const videoInputs = devices.filter(d => d.kind === 'videoinput');

const audioOutputs = devices.filter(d => d.kind === 'audiooutput');



// 填充 HTML <select> 元素,允許使用者選擇

// 使用者選擇被儲存到 localStorage:

localStorage.setItem('selectedMicrophoneId', deviceId);



// 設備變更時(新插入/拔出),自動更新

navigator.mediaDevices.addEventListener('devicechange', enumerateDevices);

就緒按鈕狀態


// 就緒按鈕 enabled 的條件:

const deviceCheckPassed = audioOk && videoOk && permissionGranted;



// 或使用 E2E bypass(CI/CD 無設備環境)

if ((window as any).__E2E_BYPASS_DEVICE_CHECK__) {

  setAudioOk(true);           // ✅ 自動設為已測試

  setVideoOk(true);           // ✅ 自動設為已測試

  setPermissionGranted(true); // ✅ 自動設為已授予

}

🔍 故障排除決策樹

問題 1:找不到「授予權限」按鈕

| 症狀 | 根因 | 修復 |

|------|------|------|

| 頁面上完全看不到此按鈕 | VideoSetup 元件未被渲染 | 檢查 app/classroom/wait/page.tsx 是否引入 <VideoSetup> |

| 按鈕存在但點擊無反應 | getUserMedia() API 不可用或被阻擋 | 檢查瀏覽器是否允許媒體存取;開啟 DevTools Network 查看 API 呼叫 |

| 按鈕文本不符(測試選擇器失敗) | 多語言或 UI 更新 | 更新測試中的 hasText() 模式,例如 /授予\|Permission/i |

快速驗證(DevTools Console):


// 檢查 MediaDevices API 可用性

navigator.mediaDevices.getUserMedia({ audio: true })

  .then(stream => {

    console.log('✅ Permission granted');

    stream.getTracks().forEach(t => t.stop());

  })

  .catch(err => console.error('❌', err.name));

問題 2:設備測試按鈕被禁用

| 症狀 | 根因 | 修復 |

|------|------|------|

| 所有按鈕都灰色禁用 | 未授予權限(permissionGranted = false) | 先點擊「授予權限」按鈕 |

| 只有麥克風禁用 | audioOk = false 或麥克風未被偵測 | 檢查系統設備管理員是否允許;檢查麥克風是否被其他應用占用 |

| 只有攝影機禁用 | videoOk = false 或未偵測到攝影機 | 檢查 USB 攝影機是否連接;檢查驅動程式 |

| 聲音輸出禁用 | 輸出設備未列舉 | 檢查系統音訊設定(Windows: 設定 > 音量混合器) |

分步排除


# 1️⃣ 確認 app/classroom/wait/page.tsx 中的 VideoSetup 函式

grep -n "function VideoSetup\|requestPermissions\|startMicTest" \

  app/classroom/wait/page.tsx



# 2️⃣ 查看指定行的完整實現

sed -n '1162,1250p' app/classroom/wait/page.tsx



# 3️⃣ 執行單一測試看詳細日誌

npx playwright test e2e/classroom-wait-device-permissions.spec.ts -g "Test 2" --reporter=verbose

問題 3:Test 2(麥克風)超時

| 症狀 | 根因 | 修復 |

|------|------|------|

| waitFor 麥克風按鈕超時 | 按鈕仍被禁用(權限未授予) | 確認 injectDeviceCheckBypass()page.goto() 前被呼叫 |

| 點擊麥克風按鈕後掛起 | AudioContext 初始化失敗 | 檢查瀏覽器是否支援 Web Audio API |

| 音量指示器未更新 | 頻率分析迴圈卡住 | 檢查 analyser.getByteFrequencyData() 是否正常執行;可能需要延長 timeout |

測試隱喻


# 單獨執行麥克風測試(獨立排除)

npx playwright test e2e/classroom-wait-device-permissions.spec.ts -g "Test 2" --timeout=120000

問題 4:Test 5(Bypass)未啟用

| 症狀 | 根因 | 修復 |

|------|------|------|

| 就緒按鈕仍禁用 | Bypass flag 未正確注入 | 檢查 injectDeviceCheckBypass() 時機;確保在 navigate() 前執行 |

| 未看到「跳過設備檢測」日誌 | 頁面未檢查此 flag | 驗證 app/classroom/wait/page.tsx 中有 __E2E_BYPASS_DEVICE_CHECK__ check |

| 一半按鈕啟用,一半禁用 | 部分狀態未被正確設置 | 檢查 bypass 邏輯是否設置所有狀態(audioOk, videoOk, permissionGranted) |

Bypass 邏輯驗證


// 在 DevTools console 驗證

window.__E2E_BYPASS_DEVICE_CHECK__  // 應為 true



// 如果為 undefined,在頁面加載時執行

(window as any).__E2E_BYPASS_DEVICE_CHECK__ = true;

location.reload();  // 重新加載頁面

問題 5:Test 8(並發)失敗

| 症狀 | 根因 | 修復 |

|------|------|------|

| 教師頁面加載成功,學生頁面掛起 | context 建立失敗 | 確保有足夠的系統資源;檢查 CPU/記憶體使用率 |

| 兩頁都加載但權限狀態不同步 | 各 context 獨立(正常) | 不需修復;這是設計行為(每個瀏覽器上下文有獨立的權限狀態) |

| 參與者統計失敗 | SSE 連線問題 | 檢查 /api/classroom/stream 是否正常工作 |

並發隔離原理


// 教師 context(一般視窗)

const teacherContext = await browser.newContext();

const teacherPage = await teacherContext.newPage();



// 學生 context(無痕視窗,完全隔離)

const studentContext = await browser.newContext();

const studentPage = await studentContext.newPage();



// 各自獨立的 cookie/localStorage/媒體權限

// 不會互相影響

📋 檢查清單

在執行設備權限測試前,確保:

  • .env.local 已配置所有必需環境變數

  • npm run dev 正常運行(http://localhost:3000 可達)

  • app/classroom/wait/page.tsx 中的 VideoSetup 元件存在(L1073+)

  • VideoSetup 包含:requestPermissions(), startMicTest(), startCameraPreview(), testSpeaker()

  • Bypass 機制已實装(檢查 __E2E_BYPASS_DEVICE_CHECK__ flag)

  • 沒有其他程式占用麥克風/攝影機(某些情況需要)

  • Playwright 已安裝(npx playwright install


運行變種

1️⃣ 運行全部 8 個測試


npx playwright test e2e/classroom-wait-device-permissions.spec.ts

2️⃣ 只運行特定測試


# 只測試麥克風

npx playwright test e2e/classroom-wait-device-permissions.spec.ts -g "Test 2"



# 只測試並發

npx playwright test e2e/classroom-wait-device-permissions.spec.ts -g "Test 8"

3️⃣ 調試模式(Playwright Inspector 開啟)


npx playwright test e2e/classroom-wait-device-permissions.spec.ts --debug

4️⃣ 詳細輸出報告


npx playwright test e2e/classroom-wait-device-permissions.spec.ts --reporter=verbose



# 或產生 HTML 報告

npx playwright test e2e/classroom-wait-device-permissions.spec.ts --reporter=html

open playwright-report/index.html

5️⃣ 特定瀏覽器運行


# 只在 Firefox 上測試

npx playwright test e2e/classroom-wait-device-permissions.spec.ts --project=firefox



# 只在 Safari (WebKit) 上測試

npx playwright test e2e/classroom-wait-device-permissions.spec.ts --project=webkit

6️⃣ 延長超時(慢速網路環境)


npx playwright test e2e/classroom-wait-device-permissions.spec.ts --timeout=120000

🔗 相關文件

| 類型 | 檔案 | 用途 |

|------|------|------|

| 測試 | e2e/classroom-wait-device-permissions.spec.ts | 8 個設備權限測試用例 |

| 實装 | app/classroom/wait/page.tsx | VideoSetup 元件(L1073+) |

| 實装 | app/classroom/wait/page.tsx | 設備檢測狀態管理 |

| API | app/api/classroom/stream/route.ts | SSE 同步 (參與者狀態) |

| 文檔 | DEVICE_PERMISSIONS_TEST_SUMMARY.md | 舊版測試報告 |


📚 相關技能


已知侷限

  1. 實際媒體設備 — 可執行真實麥克風/攝影機測試,但需要物理設備存在

  2. 跨域限制 — Agora Canvas 可能有跨域限制(已在 classroom-room-whiteboard-sync skill 中說明修復)

  3. 權限對話框 — 某些瀏覽器的權限對話框無法透過 Playwright 自動化點擊,使用 bypass flag 規避


修復歷史

2026-04-10 — 初版完成

  • ✅ 8 個測試用例全部通過

  • ✅ 支援 E2E bypass(CI/CD 環境)

  • ✅ 支援並發教師+學生測試

  • ✅ 完整的設備列舉與選擇邏輯

  • ✅ 詳細的故障排除指南

Install via CLI
npx skills add https://github.com/jvtutorcorner-sketch/jvtutorcorner-rwd --skill classroom-wait-device-permissions
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
jvtutorcorner-sketch
jvtutorcorner-sketch Explore all skills →