IKHSAN
EN Read in EnglishID

snapme

Publik
Branch
master
Commit terakhir
130fdb828 Oktober 2026

gesture.spec.ts

Buka raw
import { expect, test } from "@playwright/test"

import { izinkanKamera, jepret, masukAmbilFoto } from "./helpers"

/* M3 PRD §7.2 dengan kamera palsu Chromium (ARCHITECTURE §10):
   GS-01 (deteksi lokal, aset di-host sendiri), GS-06 (sakelar mati dan diingat),
   GS-07 (gagal muat), dan HUD selesai. Keadaan `holding` tidak bisa dipicu tanpa
   tangan sungguhan: dibuktikan uji komponen di tests/features/gesture/gesture-hud.test.tsx.
   Navigasi dan kamera memakai konvensi bersama di tests/e2e/helpers.ts; sakelar
   gesture sengaja TIDAK dimatikan di berkas ini. */

const HUD_SIAP = "Angkat dua jari, tahan sebentar"

test("model dimuat dari origin sendiri tanpa permintaan membawa gambar", async ({
  page,
}) => {
  test.setTimeout(90_000)

  const requests: {
    url: string
    method: string
    type: string
    body: string
  }[] = []
  page.on("request", (request) => {
    requests.push({
      url: request.url(),
      method: request.method(),
      type: request.resourceType(),
      body: request.postData() ?? "",
    })
  })

  await masukAmbilFoto(page)
  await izinkanKamera(page)
  /* GS-07: status "Menyiapkan gesture..." boleh terlewat kalau model cepat,
     jadi yang dijamin adalah HUD siap setelah model termuat. */
  await expect(page.getByText(HUD_SIAP)).toBeVisible({ timeout: 30_000 })

  for (let nomor = 0; nomor < 4; nomor += 1) {
    await jepret(page)
  }
  await expect(page.getByText("4 foto siap dihias")).toBeVisible()

  /* GS-01: tidak ada permintaan yang membawa gambar. Semua permintaan GET dan
     seorigin; model serta wasm datang dari /public, bukan CDN. `__nextjs_`
     adalah diagnostik Next dev yang muncul saat MediaPipe menulis peringatan ke
     konsol, bukan bagian dari alur foto. */
  const halaman = requests.filter(
    (request) =>
      request.type !== "websocket" && !request.url.includes("/__nextjs_")
  )
  expect(
    halaman
      .filter((request) => request.method !== "GET")
      .map((request) => `${request.method} ${request.url}`)
  ).toEqual([])
  expect(
    halaman
      .filter(
        (request) => request.body.includes("data:image") || request.body.includes("/9j/")
      )
      .map((request) => request.url)
  ).toEqual([])
  expect(
    halaman.filter((request) => !request.url.startsWith("http://localhost:3000"))
  ).toEqual([])
  expect(
    halaman.some((request) =>
      request.url.includes("/models/gesture_recognizer.task")
    )
  ).toBe(true)
  expect(
    halaman.some((request) => request.url.includes("/mediapipe/wasm/"))
  ).toBe(true)
})

test("sakelar mati: model tidak dimuat dan HUD menyebut jalan tombol", async ({
  page,
}) => {
  const requests: string[] = []
  page.on("request", (request) => requests.push(request.url()))

  await masukAmbilFoto(page)
  const toggle = page.getByRole("button", { name: /Gesture Snap/ })
  await toggle.click()
  await expect(toggle).toHaveAttribute("aria-pressed", "false")

  await izinkanKamera(page)
  await expect(
    page.getByText("Gesture mati. Pakai tombol kamera.")
  ).toBeVisible()
  await page.waitForTimeout(1500)

  expect(
    requests.filter(
      (url) => url.includes("/models/") || url.includes("/mediapipe/")
    )
  ).toEqual([])
})

test("pilihan sakelar diingat setelah halaman dimuat ulang", async ({ page }) => {
  await masukAmbilFoto(page)
  const toggle = page.getByRole("button", { name: /Gesture Snap/ })
  await toggle.click()
  await expect(toggle).toHaveAttribute("aria-pressed", "false")

  /* FR-14 menyimpan sesi dengan jeda 400 ms; tunggu tulisannya selesai supaya
     muat ulang di bawah benar-benar menguji ingatan sakelar. Sesi foto ikut
     lintas muat ulang, jadi halaman tetap di langkah yang sama. */
  await page.waitForTimeout(600)
  await page.reload()
  await expect(page).toHaveURL(/\/booth\/capture$/)

  await expect(
    page.getByRole("button", { name: /Gesture Snap/ })
  ).toHaveAttribute("aria-pressed", "false")
})

test("model gagal dimuat: sakelar hilang, toast tampil, tombol kamera tetap jalan", async ({
  page,
}) => {
  await page.route("**/models/**", (route) => route.abort())

  await masukAmbilFoto(page)
  await izinkanKamera(page)

  await expect(
    page.getByText(
      "Gesture Snap gagal dimuat. Tombol kamera tetap bisa dipakai."
    )
  ).toBeVisible({ timeout: 30_000 })
  await expect(page.getByRole("button", { name: /Gesture Snap/ })).toHaveCount(0)

  await jepret(page)
  await expect(page.getByText("1/4")).toBeVisible()
})
byte
4.466
baris
138
Jenis
ts
Commit terakhir
28 Oktober 2026