IKHSAN
ENID Baca dalam Bahasa Indonesia

snapme

Public
Branch
master
Latest commit
130fdb828 October 2026

crossbrowser.spec.ts

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

import { masukHiasDariGaleri } from "./helpers"

/* M9: uji lintas peramban (PRD §8: dua versi mayor terakhir Chrome, Edge,
   Firefox, dan Safari).

   Berkas ini sengaja tidak menyentuh kamera: perangkat media palsu adalah
   bendera Chromium, sedangkan WebKit dan Firefox tidak punya padanannya.
   Karena itu jalur foto dari galeri yang dipakai (FR-13), dan asersi pratinjau
   cetak tetap di chromium-only result.spec.ts. Berkas ini dijalankan proyek
   chromium, firefox, webkit, dan msedge (playwright.config.ts). */

test("welcome memuat aksi utama dan halaman statis menjawab judulnya", async ({
  page,
}) => {
  await page.goto("/")
  await expect(
    page.getByRole("heading", { name: "Foto strip tanpa antre." })
  ).toBeVisible()
  await expect(page.getByRole("link", { name: "Mulai Foto" }).first()).toBeVisible()

  await page.goto("/privasi")
  await expect(page.getByRole("heading", { level: 1 })).toContainText("Privasi")

  await page.goto("/s/tidakada1234")
  await expect(page.getByText("Halaman tidak ditemukan.")).toBeVisible()

  await page.goto("/r/tidakada1234")
  await expect(page.getByText("Halaman tidak ditemukan.")).toBeVisible()
})

test("header keamanan terpasang di rute publik dan halaman pribadi", async ({
  page,
}) => {
  /* M9: CSP penuh berlaku untuk semua rute. Tanpa asersi ini, satu suntingan
     di `next.config.ts` bisa menghapusnya tanpa ada uji yang gagal (halaman
     tanpa CSP tetap "jalan"). */
  const beranda = await page.goto("/")
  const csp = beranda?.headers()["content-security-policy"] ?? ""
  expect(csp).toContain("default-src 'self'")
  expect(csp).toContain("frame-ancestors 'none'")
  expect(csp).toContain("object-src 'none'")
  expect(csp).toContain("base-uri 'self'")
  expect(csp).toContain("wasm-unsafe-eval")

  /* Produksi tidak boleh memakai 'unsafe-eval'; server pengembangan memakainya
     untuk React, dan penandanya halaman /dev/ui (ARCHITECTURE §4). */
  await page.goto("/dev/ui")
  const produksi = await page.getByText("Hanya saat pengembangan.").isVisible()
  if (produksi) {
    expect(csp).not.toContain("'unsafe-eval'")
  }

  const privat = await page.goto("/s/tidakada1234")
  expect(privat?.headers()["x-robots-tag"]).toBe("noindex")
  expect(privat?.headers()["referrer-policy"]).toBe("no-referrer")
  expect(privat?.headers()["content-security-policy"]).toBe(csp)
})

test("alur galeri sampai unduh PNG berjalan di peramban ini", async ({ page }) => {
  test.setTimeout(120_000)
  await masukHiasDariGaleri(page)

  /* Tiga jenis hias yang mesinnya berbeda: swatch frame (kanvas), daftar
     stiker (dek), dan input teks (formulir). */
  await page.getByRole("button", { name: "Malam", exact: true }).click()
  await page.getByRole("radio", { name: "Stiker" }).click()
  await page.getByRole("button", { name: "Tempel Bintang" }).click()
  await expect(page.getByText("Stiker Bintang")).toBeVisible()

  await page.getByRole("radio", { name: "Teks" }).click()
  await page.locator("#strip-text").fill("halo dari peramban lain")
  await expect(page.locator("#strip-text")).toHaveValue("halo dari peramban lain")

  await page.getByRole("link", { name: "Selesai & Unduh" }).click()
  await page.waitForURL("**/booth/result")
  await expect(page.getByAltText("Pratinjau strip")).toBeVisible({
    timeout: 30_000,
  })

  /* Galeri tidak punya klip, jadi tombol unduh versi hidup (SH-03) memang
     tidak ada; yang diperiksa di sini tombol cetak tetap tersedia. */
  await expect(page.getByRole("button", { name: "Cetak 4R" })).toBeEnabled()

  const [download] = await Promise.all([
    page.waitForEvent("download", { timeout: 60_000 }),
    page.getByRole("button", { name: "Selesai & Unduh" }).click(),
  ])
  expect(download.suggestedFilename()).toMatch(/^snapme-\d{8}-\d{4}\.png$/)
})
bytes
3,836
lines
92
Type
ts
Latest commit
28 October 2026