01บทนำ · About This Guide
Mobile-specific test scope · devices · gestures
เอกสารนี้เป็น Mobile Test Documentation สำหรับหน้าจอ Subscribe Mobile v2.0 ครอบคลุม:
- Test Scenarios · 15 test areas — 6-step wizard + mobile-specific behavior
- Test Cases · 78 cases — Positive / Negative / Boundary / UI / Integration / Mobile / A11y
- Playwright Mobile Guide — Pixel 5 · iPhone 14 emulation · touch · geolocation · camera
- Mobile-specific coverage — Bottom sheet modal · virtual keyboard · slip upload via camera · offline · orientation
📌
Reference file:
ConstructQ_Subscribe_Mobile.html · React SPA in phone frame · 6-step wizard (steps 1-6 in UI · 0-5 index) · bilingual TH/EN · fixed portrait · bottom footer navigation · Add-User bottom sheet modal02Mobile Overview
Layout · gestures · mobile-specific components
📱 Mobile Layout
- Fixed viewport: Portrait 375×812 (iPhone) or 411×914 (Pixel 5) · phone frame on desktop preview
- Topbar (sticky): Logo · TH/EN language toggle · progress dots
- Header (per-step): Eyebrow "Step X / 6" · title · subtitle
- Content (scrollable): Main step content · scrolls under fixed footer
- Footer (fixed bottom): "ย้อนกลับ" + primary CTA (ถัดไป / ชำระเงิน / เริ่มใช้งาน)
- Toast: Bottom center · above footer · auto-dismiss ~2.5s
- Bottom sheet modal: Add User form · slide-up from bottom · backdrop
🗺️ Wizard Structure
| Step | Title (TH) | Title (EN) | Key Elements |
|---|---|---|---|
| 1 | ข้อกำหนดและเงื่อนไข | Terms & Conditions | Accordion (11 sections) · 2 required checkboxes |
| 2 | เลือกแพ็คเก็จ | Choose Package | 4 plan cards (Trial/Starter/Pro/Enterprise) · cycle toggle · Pro default |
| 3 | ข้อมูลบริษัท | Company Details | Company · Tax ID · Address · Tambon autocomplete · Contact |
| 4 | ผู้ใช้งาน | Users | Add-User bottom sheet modal · phone field · role · permission matrix |
| 5 | ชำระเงิน | Payment | Duration picker · 3 tabs (QR/Card/Bank) · slip upload · Trial notice (if Trial) |
| 6 | ขอแสดงความยินดี | Welcome | Success · order ref · pulse gold icon · CTA "เริ่มใช้งาน" |
💎 4 Packages (identical to Web)
- Trial (free) — 30-day free · locked duration · payment hidden
- Starter — ฿3,900/mo · 10 users · 3 projects · SME
- Professional — ฿7,900/mo · 30 users · 15 projects · default selection
- Enterprise — Contact Sales · SSO · unlimited
📱 Mobile-Specific Components
- Bottom sheet modal (Add User) — slides up · backdrop tap dismiss · scroll inside
- Tambon autocomplete list — appears below input · tap to select · auto-fill 3 fields
- T&C accordion — 11 articles · tap header to expand · smooth animation
- Payment tabs — 3 tabs (QR default · Card · Bank) · swipe-like tap only (no gesture swipe)
- Slip upload — camera capture or gallery pick · preview after select
- Trial notice — replaces payment tabs when free package chosen
- Duration picker — +/- stepper · locked state for Trial (fixed 30 days)
03Mobile Test Strategy
Devices · gestures · special mobile scenarios
📱 Target Devices
| Device | Viewport | DPR | Browser | Priority |
|---|---|---|---|---|
| iPhone 14 | 390×844 | 3 | Safari (WebKit) | P1 |
| Pixel 5 | 393×851 | 2.75 | Chrome | P1 |
| iPhone SE | 375×667 | 2 | Safari | P2 (small screen) |
| Samsung Galaxy S9+ | 320×658 | 4.5 | Chrome | P2 (narrowest) |
| iPad Mini | 768×1024 | 2 | Safari | P3 (tablet fallback) |
🎯 Test Types
| Type | Objective | Coverage |
|---|---|---|
| Positive | Happy path on real device size | ~35% |
| Negative | Invalid input · payment failure | ~20% |
| Mobile | Touch · gesture · orientation · keyboard | ~20% |
| Integration | Omise · SlipOK · camera · autocomplete | ~10% |
| Boundary | Tax ID · password · duration · viewport | ~8% |
| UI | Layout · safe area · font scaling | ~4% |
| A11y | VoiceOver · TalkBack · contrast · touch target | ~3% |
⚡ Priority
- P1 — Critical · payment · signup completion · Trial lock · touch input
- P2 — High · validation · bottom sheet · virtual keyboard behavior
- P3 — Medium · orientation · UI polish · edge devices
🌐 Mobile-Specific Considerations
- Touch targets — Minimum 44×44px per iOS HIG / 48×48dp per Material
- Virtual keyboard — Fields must remain visible when keyboard opens · scroll into view
- Camera permission — Test prompt · deny · fallback to gallery
- Network — Test on 4G · slow 3G · offline · retry after reconnect
- Font scaling — Respect OS accessibility font size (Dynamic Type on iOS · Font Scale on Android)
- Safe area — Respect notch/dynamic island · home indicator
04Test Scenarios + Test Cases
15 areas · 78 detailed mobile test cases
4.1 · 🚀 App Launch + Topbar
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M01 | เปิดหน้า Subscribe Mobile ครั้งแรก | 1. Launch app · navigate /subscribe | Portrait phone frame · topbar (logo · TH/EN) · Step 1 T&C · fixed footer visible | P1 | Pos |
| TC-M02 | Progress dots on topbar | 1. Advance step by step | 6 dots update · current step highlighted · completed = filled | P2 | UI |
| TC-M03 | Sticky topbar during scroll | 1. Scroll content downward | Topbar remains fixed at top · content scrolls under · footer stays at bottom | P2 | Mobile |
| TC-M04 | Language toggle in topbar | 1. Tap 🇹🇭 → 🇬🇧 | Whole UI switches to EN · flag icon changes · state persists | P1 | Pos |
| TC-M05 | Fixed footer buttons | 1. View any step | Back + primary CTA always visible at bottom · touch target ≥ 44px height | P1 | Mobile |
4.2 · 📜 Step 01 · Terms & Conditions
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M06 | T&C accordion load · 11 articles | 1. เข้า Step 01 | All 11 articles collapsed by default · tap header to expand · smooth animation | P2 | Pos |
| TC-M07 | T&C accordion · tap to expand | 1. Tap article #6 header | Expands smoothly · chevron rotates · content scrolls into view if needed | P2 | Mobile |
| TC-M08 | T&C checkbox required | 1. ไม่ติ๊ก T&C 2. Tap "ยอมรับและถัดไป" |
Button disabled visually · or toast "กรุณายอมรับ" · block advance | P1 | Neg |
| TC-M09 | PDPA checkbox required | 1. ติ๊ก T&C · ไม่ติ๊ก PDPA 2. Tap CTA |
Block advance · ต้องติ๊กทั้งคู่ | P1 | Neg |
| TC-M10 | Both checked · advance to Step 2 | 1. ติ๊ก 2 กล่อง 2. Tap CTA |
Advance · Step 2 loads · progress dot 2 highlighted | P1 | Pos |
4.3 · 💎 Step 02 · Choose Package
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M11 | 4 plan cards stacked vertically | 1. เข้า Step 02 | 4 cards vertical scroll · Trial + Starter + Pro + Enterprise · Pro badge "Most Popular" | P1 | Pos |
| TC-M12 | Professional default-selected | 1. เข้า Step 02 ครั้งแรก | Pro card highlighted default (per Mobile v26 · task #26) | P2 | Pos |
| TC-M13 | Cycle toggle · monthly ↔ yearly | 1. Tap "รายปี" | Prices update · Starter 3,900→42,900 · savings shown | P1 | Pos |
| TC-M14 | Trial card · plain text (not chips) | 1. View Trial card | Quota display as plain text (per Mobile v22 · task #22) · not chip pills | P2 | UI |
| TC-M15 | Trial subtitle | 1. View Trial card | Subtitle "เริ่มต้นฟรี ไม่เสียเงิน" (per Mobile v23) | P3 | UI |
| TC-M16 | Tap card → highlight | 1. Tap Starter card | Card border teal + shadow · other cards dim · footer CTA enabled | P1 | Pos |
| TC-M17 | Enterprise = Contact Sales | 1. Tap Enterprise | "ติดต่อทีมขาย / Contact Sales" text · no fixed price · CTA behavior "ติดต่อเรา" | P1 | Pos |
| TC-M18 | Card touch target size | 1. Measure card height | Card total height ≥ 120px · easy to tap accurately | P3 | Mobile |
4.4 · 🏢 Step 03 · Company Details
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M19 | Field sections · Company + Contact + Address | 1. เข้า Step 03 | 3 sections with headings · stack vertically | P2 | UI |
| TC-M20 | Tax ID input · 13-digit numeric keyboard | 1. Tap Tax ID field | Numeric keyboard opens (inputmode="numeric") · 13-digit limit | P1 | Mobile |
| TC-M21 | Tax ID validation · 13 digits required | 1. พิมพ์ "12345" · blur | Error text under field · block save | P1 | Neg |
| TC-M22 | Email · email keyboard | 1. Tap email field | Email keyboard variant (@, .com quick keys) | P1 | Mobile |
| TC-M23 | Email format validation | 1. Enter "bad-email" | Error "รูปแบบอีเมลไม่ถูกต้อง" | P1 | Neg |
| TC-M24 | Phone · tel keyboard | 1. Tap phone field | Telephone keypad opens (0-9 + symbols) | P2 | Mobile |
| TC-M25 | Tambon autocomplete list | 1. Type "ห้วยขวาง" in tambon | Dropdown appears below input · scrollable · tap to select | P1 | Int |
| TC-M26 | Tambon select · auto-fill 3 fields | 1. Tap first suggestion | District/Province/Postal auto-filled · list closes | P1 | Int |
| TC-M27 | Tambon reordered fields (v16 spec) | 1. View address section | Tambon field at top · then district → province → postal (per Mobile v16 · task #16) | P2 | UI |
| TC-M28 | Autocomplete list · dismiss on blur | 1. Open list · tap outside | List closes · no selection | P3 | Mobile |
4.5 · 👥 Step 04 · Users (Bottom Sheet Modal)
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M29 | Empty state · no users | 1. เข้า Step 04 | Empty message + "เพิ่มผู้ใช้งาน" CTA button | P2 | UI |
| TC-M30 | Open Add-User modal (bottom sheet) | 1. Tap "เพิ่มผู้ใช้งาน" | Modal slides up from bottom · backdrop dim · header + form + save/cancel | P1 | Mobile |
| TC-M31 | Modal · form fields (5 fields) | 1. Open modal | name · email · phone (per Mobile v21) · password · role dropdown | P1 | Pos |
| TC-M32 | Modal · permission matrix default collapsed | 1. Open modal · view matrix | Matrix collapsed default (per task #4 · reverted from #3) · tap to expand | P2 | UI |
| TC-M33 | Modal · role select → matrix updates | 1. Select role "qc_insp" | Matrix shows features accessible by Inspector · view-only | P2 | Pos |
| TC-M34 | Modal · save user | 1. Fill all fields · tap "บันทึก" | Modal closes · user row added to list · success toast | P1 | Pos |
| TC-M35 | Modal · cancel | 1. Open modal · tap "ยกเลิก" | Modal closes · no user added · form state cleared | P2 | Pos |
| TC-M36 | Modal · backdrop tap to dismiss | 1. Open modal · tap outside sheet | Modal closes · same as cancel | P3 | Mobile |
| TC-M37 | Modal · scroll inside when content overflows | 1. Open modal · expand matrix (long) | Sheet content scrolls independently · backdrop stays | P2 | Mobile |
| TC-M38 | Modal · password rules (no special-char rule) | 1. Type "Password123" | Accept · no special char required (per Mobile v12 · task #176) | P2 | Bnd |
| TC-M39 | User list · delete swipe or icon | 1. Tap delete icon on row | Confirm dialog · delete on confirm | P2 | Pos |
4.6 · 💳 Step 05 · Payment
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M40 | Duration picker · default 1 | 1. เข้า Step 05 | Default 1 · +/- buttons · large touch targets | P1 | Pos |
| TC-M41 | Duration +/- taps | 1. Tap + 3 times | Value 1→2→3→4 · summary recalculates each tap | P1 | Pos |
| TC-M42 | Duration min = 1 | 1. Duration = 1 · Tap - | - disabled state · stays at 1 | P2 | Bnd |
| TC-M43 | Payment tabs · 3 tabs (QR default) | 1. Enter Step 05 | QR tab active default · Card + Bank tabs visible | P1 | Pos |
| TC-M44 | Tab switch · tap | 1. Tap Card tab | Content changes to card form · active state visible | P1 | Pos |
| TC-M45 | QR tab · display QR + amount + merchant | 1. View QR tab | PromptPay QR image · amount in baht · merchant info in gold box | P1 | Pos |
| TC-M46 | Slip upload · file picker | 1. Tap "อัปโหลดสลิป" | Native file picker · camera or gallery options | P1 | Mobile |
| TC-M47 | Slip upload · camera capture | 1. Choose "Camera" | Camera opens · capture · preview · confirm/retake | P1 | Int |
| TC-M48 | Slip verify via SlipOK | 1. Upload valid slip | Loading spinner · verified < 30s · success badge · advance-enabled | P1 | Int |
| TC-M49 | Card tab · card preview animation | 1. Tap Card tab · type card number | Card preview graphic shows entered digits · updates live | P2 | UI |
| TC-M50 | Card · Omise TEST charge success | 1. Enter 4242 4242 4242 4242 + name/exp/CVC | Omise TEST returns success · advance to Welcome | P1 | Int |
| TC-M51 | Card · declined | 1. Enter 4000 0000 0000 0002 | Error "บัตรถูกปฏิเสธ" · toast + inline · retry option | P1 | Int |
| TC-M52 | Bank tab · display Kasikorn info | 1. Tap Bank tab | Bank name · branch · account no · copy button | P1 | Pos |
| TC-M53 | Bank · copy account no | 1. Tap copy icon | Toast "คัดลอกแล้ว" · Clipboard API sets value | P3 | Mobile |
| TC-M54 | Summary card · gradient background | 1. Scroll to summary | Teal → gold gradient · rows for package/cycle/subtotal/VAT/total | P2 | UI |
4.7 · 🎉 Step 06 · Welcome
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M55 | Welcome screen · pulse gold icon | 1. Complete payment | Gold circle with icon · pulse animation 2s infinite · title "🎉" | P2 | UI |
| TC-M56 | Welcome card · order details | 1. View welcome card | Package · cycle · users count · sent-to email count | P1 | Pos |
| TC-M57 | "เริ่มใช้งาน" CTA | 1. Tap CTA | Redirect to /dashboard (mobile) or /login · session created | P1 | Pos |
4.8 · 🔒 Trial Lock (Mobile v22-v25)
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M58 | Trial · payment section hidden | 1. Select Trial · go Step 05 | Payment tabs hidden · "trial-notice" element shows instead (per Mobile v23) | P1 | Pos |
| TC-M59 | Trial · duration picker locked | 1. Trial in Step 05 | Duration = 30 days fixed · +/- disabled · locked visual (per Mobile v24 · task #24) | P1 | Pos |
| TC-M60 | Trial · CTA "เริ่มใช้งาน" direct | 1. Trial · tap CTA | Skip payment validation · advance to Welcome directly · no charge | P1 | Pos |
4.9 · 👆 Touch Gestures
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M61 | Touch target · min 44×44 px | 1. Audit all buttons/links | All interactive elements ≥ 44px height · Material 48dp min | P2 | Mobile |
| TC-M62 | Tap ripple/feedback | 1. Tap CTA · card · tab | Visual feedback (color darken · scale) · immediate response | P2 | Mobile |
| TC-M63 | Vertical scroll · no horizontal | 1. Swipe left/right on content | No horizontal scroll · body overflow-x hidden | P2 | Mobile |
| TC-M64 | Fast scroll · footer stays fixed | 1. Flick scroll top ↔ bottom | Footer stays at bottom · topbar sticky · no jitter | P2 | Mobile |
4.10 · ⌨️ Virtual Keyboard
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M65 | Keyboard opens · field scrolls into view | 1. Tap bottom field · keyboard opens | Field auto-scrolls above keyboard · never hidden | P1 | Mobile |
| TC-M66 | Numeric fields · numeric keyboard | 1. Tap Tax ID · phone · duration | Numeric keypad opens (inputmode="numeric" or type="tel") | P1 | Mobile |
| TC-M67 | Email · email keyboard variant | 1. Tap email field | Email keyboard with @ + .com shortcut | P1 | Mobile |
| TC-M68 | Autofill · browser saved data | 1. Tap company/email field · autofill available | Browser autofill suggestions appear | P2 | Mobile |
4.11 · 📷 Slip Camera Upload
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M69 | Camera permission prompt | 1. First tap "Upload slip" · choose camera | OS permission dialog · Allow → camera opens | P1 | Mobile |
| TC-M70 | Camera permission denied · fallback | 1. Deny permission | Fallback to gallery only · toast "ต้องอนุญาตกล้อง" | P2 | Mobile |
| TC-M71 | Slip preview after capture | 1. Capture photo | Preview thumbnail · file name · size · retake button | P1 | Pos |
4.12 · 🌐 Language Toggle
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M72 | TH → EN toggle updates entire page | 1. Tap EN flag | All labels · steps · T&C content · package copy · buttons switch | P1 | Pos |
| TC-M73 | Data preserved on toggle | 1. Fill Step 3 · toggle EN · back TH | Data intact · no reset | P1 | Pos |
4.13 · 📡 Offline / Poor Network
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M74 | Offline mode · slip upload fails gracefully | 1. Enable airplane mode · upload slip | Error "ไม่มีอินเทอร์เน็ต" · retry button · no crash | P2 | Neg |
| TC-M75 | Slow 3G · loading state visible | 1. Throttle to slow 3G · submit payment | Spinner visible · no double-submit · disable button while loading | P2 | Mobile |
4.14 · 🔄 Orientation + Safe Area
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M76 | Portrait-only enforcement | 1. Rotate device to landscape | UI stays portrait or displays "โปรดหมุนเป็นแนวตั้ง" | P3 | UI |
| TC-M77 | Safe area · notch/home indicator | 1. iPhone 14 with notch | Content respects safe-area-inset-top/bottom · no overlap | P2 | Mobile |
4.15 · ♿ Mobile Accessibility
| TC ID | Scenario | Steps | Expected Result | Priority | Type |
|---|---|---|---|---|---|
| TC-M78 | VoiceOver / TalkBack · all buttons announced | 1. Enable VoiceOver · swipe through page | All actions announced correctly · Thai + English | P2 | A11y |
📊
Test Case Summary: รวม 78 mobile test cases · P1 = 36 · P2 = 32 · P3 = 10 · Positive 26 · Negative 8 · Boundary 3 · Integration 7 · Mobile-specific 22 · UI 10 · A11y 2
05Playwright Mobile Automation Guide
Device emulation · touch · geolocation · camera · sample tests
🚀 Mobile Setup
- Install:
npm init playwright@latest· include Mobile Chrome + Mobile Safari projects - Config: add devices
Pixel 5+iPhone 14toplaywright.config.ts - Touch: enable
hasTouch: trueandisMobile: truein device config - Geolocation/Camera: grant permissions via
context.grantPermissions([...]) - Slip fixture: pre-generated slip JPG in
fixtures/slips/· usesetInputFiles() - Network throttle: use
context.route()for slow 3G simulation
⚙️ playwright.config.ts · Mobile Projects
TypeScriptimport { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests', fullyParallel: true, use: { baseURL: process.env.BASE_URL ?? 'http://localhost:3000', trace: 'on-first-retry', screenshot: 'only-on-failure', video: 'retain-on-failure', locale: 'th-TH', }, projects: [ // Primary mobile targets { name: 'iPhone 14', use: { ...devices['iPhone 14'] }}, { name: 'Pixel 5', use: { ...devices['Pixel 5'] }}, // Edge devices { name: 'iPhone SE', use: { ...devices['iPhone SE'] }}, { name: 'Galaxy S9+', use: { ...devices['Galaxy S9+'] }}, { name: 'iPad Mini', use: { ...devices['iPad Mini'] }}, ] });
🎯 SubscribeMobilePage POM
TypeScriptimport { Page, Locator, expect } from '@playwright/test'; export class SubscribeMobilePage { readonly page: Page; readonly btnNext: Locator; readonly btnBack: Locator; readonly progressDots: Locator; constructor(page: Page) { this.page = page; this.btnNext = page.locator('.ph-foot button').last(); this.btnBack = page.locator('.ph-foot button').first(); this.progressDots = page.locator('.ph-step'); } async goto() { await this.page.goto('/subscribe-mobile'); } async tapNext() { await this.btnNext.tap(); } async acceptTerms() { await this.page.locator('input[type="checkbox"]').first().check(); await this.page.locator('input[type="checkbox"]').last().check(); await this.tapNext(); } async selectPackage(id: 'trial' | 'starter' | 'pro' | 'enterprise') { await this.page.locator(`[data-pkg="${id}"]`).tap(); await this.tapNext(); } async openAddUserSheet() { await this.page.getByRole('button', { name: /เพิ่มผู้ใช้งาน/ }).tap(); await this.page.locator('.user-modal').waitFor({ state: 'visible' }); } async uploadSlip(filePath: string) { const input = this.page.locator('input[type="file"][accept*="image"]'); await input.setInputFiles(filePath); } }
✅ Sample Test · Full Mobile E2E (Trial · no payment)
TypeScriptimport { test, expect } from '@playwright/test'; import { SubscribeMobilePage } from '../pages/SubscribeMobilePage'; test.describe('Subscribe Mobile · E2E', () => { test.use({ ...devices['Pixel 5'] }); // override device for this suite test('TC-E2E-M01 · Trial signup · skip payment', async ({ page, context }) => { const sub = new SubscribeMobilePage(page); await sub.goto(); // Step 1 · T&C await sub.acceptTerms(); // Step 2 · Trial await sub.selectPackage('trial'); // Step 3 · Company (tap fields · fill · autocomplete) await page.getByLabel(/ชื่อบริษัท/).fill('ธาร์ณัส จำกัด'); await page.getByLabel(/เลขผู้เสียภาษี/).fill('0105561234567'); await page.getByLabel(/ตำบล/).fill('ห้วยขวาง'); await page.locator('.addr-item').first().tap(); // pick autocomplete await page.getByLabel(/อีเมล/).fill('admin@tarnas.co.th'); await page.getByLabel(/โทรศัพท์/).fill('0812345678'); await sub.tapNext(); // Step 4 · Add user via bottom sheet await sub.openAddUserSheet(); await page.getByLabel(/ชื่อ/).fill('สมชาย'); await page.getByLabel(/อีเมล/).fill('somchai@tarnas.co.th'); await page.getByLabel(/โทรศัพท์/).fill('0812345678'); await page.getByLabel(/รหัสผ่าน/).fill('Password123'); await page.getByLabel(/บทบาท/).selectOption('pm'); await page.getByRole('button', { name: /บันทึก/ }).tap(); await sub.tapNext(); // Step 5 · Trial notice · locked duration · direct advance await expect(page.locator('.trial-notice')).toBeVisible(); await expect(page.locator('.pay-tabs')).not.toBeVisible(); await sub.tapNext(); // Step 6 · Welcome await expect(page.getByText(/ขอแสดงความยินดี|Welcome/)).toBeVisible({ timeout: 10000 }); }); test('TC-M59 · Trial locked duration', async ({ page }) => { const sub = new SubscribeMobilePage(page); await sub.goto(); await sub.acceptTerms(); await sub.selectPackage('trial'); // jump to step 5 via progress dots await page.locator('[data-s="4"]').tap(); await expect(page.locator('.dur-box.is-locked')).toBeVisible(); await expect(page.locator('.dur-box .btn-minus')).toBeDisabled(); }); test('TC-M30 · Add-User modal is bottom sheet', async ({ page }) => { const sub = new SubscribeMobilePage(page); await sub.goto(); await sub.acceptTerms(); await sub.selectPackage('pro'); // skip to Step 4 · Users await page.locator('[data-s="3"]').tap(); await sub.openAddUserSheet(); const sheet = page.locator('.user-modal'); const box = await sheet.boundingBox(); // verify sheet appears from bottom half of viewport expect(box!.y).toBeGreaterThan(100); }); test('TC-M65 · Virtual keyboard · field scrolls into view', async ({ page }) => { const sub = new SubscribeMobilePage(page); await sub.goto(); await sub.acceptTerms(); await sub.selectPackage('pro'); await page.locator('[data-s="2"]').tap(); const phone = page.getByLabel(/โทรศัพท์/); await phone.focus(); // simulate keyboard by reducing viewport height await page.setViewportSize({ width: 393, height: 500 }); const box = await phone.boundingBox(); expect(box!.y).toBeLessThan(400); // field visible above 400px }); });
📷 Camera Mock (for slip upload tests)
TypeScript// Playwright doesn't emulate real camera · use setInputFiles with fixture test('TC-M47 · Slip upload via capture (mocked)', async ({ page, context }) => { // Grant camera permission await context.grantPermissions(['camera']); const sub = new SubscribeMobilePage(page); await sub.goto(); // ... navigate to payment step ... // Upload fixture slip image (bypasses native camera picker) await sub.uploadSlip('fixtures/slips/valid-slip.jpg'); // Verify preview appears await expect(page.locator('.slip-preview img')).toBeVisible(); });
📡 Network Throttling · Slow 3G
TypeScripttest('TC-M75 · Slow 3G · loading state visible', async ({ page, context }) => { // Throttle · Chrome DevTools Protocol const client = await context.newCDPSession(page); await client.send('Network.emulateNetworkConditions', { offline: false, downloadThroughput: 400 * 1024 / 8, // 400 kbps uploadThroughput: 400 * 1024 / 8, latency: 400 }); // ... complete signup flow · verify spinner ... });
💡
Mobile Best Practices: Use
tap() not click() for touch events · verify hasTouch in device config · use setInputFiles() for camera mocks · throttle network via CDP session · grant permissions early via context.grantPermissions() · check safe area via window.visualViewport API06Appendix
📌 Version History
- ConstructQ_Subscribe_Mobile.html · source · phone frame preview · 6-step wizard
- v1.0 · earlier release (task #118)
- v2.0 · 13 ก.ค. 2569 · Refresh · 78 mobile-specific test cases · 15 areas · added Bottom Sheet Modal · Touch · Virtual Keyboard · Camera · Offline · Orientation coverage
🔗 References
- Playwright Mobile:
https://playwright.dev/docs/emulation - Playwright Devices:
https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/deviceDescriptorsSource.json - iOS HIG Touch Target:
https://developer.apple.com/design/human-interface-guidelines/accessibility - Material Touch Target:
https://m3.material.io/foundations/accessible-design/accessibility-basics - Test cards (Omise):
4242 4242 4242 4242success,4000 0000 0000 0002declined
📊 Excel Companion
ดูไฟล์ ConstructQ_Subscribe_Mobile_TestScenario_v2.0.xlsx คู่กัน · 4 sheets: Summary · Test Scenarios · Test Cases · Playwright Mobile Coverage