karawaci.kode

2026-08-16 · 10 min

Synthetic Monitoring Playwright di GitHub Actions: Cek User Flow Tiap 15 Menit

Klien fintech Jakarta saya punya masalah klasik: uptime checker bilang hijau, tapi user komplain tidak bisa login. Setelah investigasi, ketahuan bahwa CDN masih serving halaman tapi endpoint /api/auth/login sedang throttled oleh rate limiter yang salah konfigurasi. Uptime checker tidak tahu soal ini. Synthetic monitoring tahu.

Saya setup synthetic monitoring dengan Playwright yang jalan di GitHub Actions scheduled workflow, cek user flow kritis tiap 15 menit. Ini cara saya melakukannya.

Kenapa Synthetic Monitoring, Bukan Sekadar Health Check

Health check endpoint (/api/health yang return { status: "ok" }) bagus untuk load balancer. Tapi ia tidak merepresentasikan apa yang user sungguhan alami. Synthetic monitoring berbeda: ia menjalankan browser sungguhan, membuka halaman, mengklik tombol, mengisi form, dan memvalidasi hasilnya.

Untuk SaaS atau aplikasi dengan user flow yang non-trivial, ini berarti:

  • Login flow — apakah OAuth callback masih bekerja setelah rotate client secret?
  • Checkout flow — apakah QRIS masih bisa di-generate setelah update library payment?
  • Critical API — apakah response API masih dalam format yang diharapkan?

Alternatifnya adalah layanan dedicated: Checkly, Datadog Synthetics, New Relic Scripted Browser. Semuanya bagus, semua punya dashboard, alerting native, dan multiple region. Tapi untuk tim kecil yang belum siap bayar $50-150/bulan hanya untuk synthetic monitoring, GitHub Actions cron adalah titik awal yang sangat masuk akal.

Struktur Proyek

Saya pisahkan script synthetic monitoring dari test suite normal di folder berbeda agar tidak ikut jalan saat CI biasa:

playwright-monitors/
  auth-flow.spec.ts
  checkout-flow.spec.ts
  playwright.config.ts
package.json

File ini hidup di root repository atau sebagai package terpisah — terserah, yang penting tidak masuk ke direktori tests/ yang jalan di setiap PR.

Konfigurasi Playwright untuk Monitor

Playwright config untuk synthetic monitoring sedikit berbeda dari config test biasa: tidak perlu multiple browser, tidak perlu retries panjang (kalau gagal, alert cepat lebih penting dari retry), dan timeout yang ketat.

// playwright-monitors/playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './',
  timeout: 30_000,        // 30 detik per test — jangan terlalu longgar
  retries: 1,             // satu retry untuk flakiness transient
  workers: 1,             // jalan serial, bukan paralel
  reporter: [
    ['list'],
    ['json', { outputFile: 'results.json' }],
    ['html', { open: 'never', outputFolder: 'playwright-report' }],
  ],
  use: {
    baseURL: process.env.MONITOR_BASE_URL ?? 'https://app.example.com',
    screenshot: 'only-on-failure',
    video: 'off',         // hemat storage, screenshot sudah cukup
    trace: 'retain-on-failure',
    headless: true,
  },
});

Script Monitor: Auth Flow

Ini contoh monitor untuk login flow — yang paling kritis di hampir semua aplikasi:

// playwright-monitors/auth-flow.spec.ts
import { test, expect } from '@playwright/test';

const TEST_USER = {
  email: process.env.MONITOR_USER_EMAIL!,
  password: process.env.MONITOR_USER_PASSWORD!,
};

test('user dapat login dan melihat dashboard', async ({ page }) => {
  // 1. Buka halaman login
  await page.goto('/login');
  await expect(page).toHaveTitle(/Masuk|Login/i);

  // 2. Isi form
  await page.getByLabel('Email').fill(TEST_USER.email);
  await page.getByLabel('Kata Sandi').fill(TEST_USER.password);
  await page.getByRole('button', { name: 'Masuk' }).click();

  // 3. Tunggu redirect ke dashboard
  await page.waitForURL('**/dashboard', { timeout: 10_000 });

  // 4. Validasi elemen kritis sudah ada
  await expect(page.getByTestId('user-greeting')).toBeVisible();
  await expect(page.getByTestId('account-balance')).toBeVisible();
});

test('API auth mengembalikan token dalam 3 detik', async ({ request }) => {
  const start = Date.now();

  const response = await request.post('/api/auth/login', {
    data: {
      email: TEST_USER.email,
      password: TEST_USER.password,
    },
  });

  const elapsed = Date.now() - start;

  expect(response.status()).toBe(200);
  expect(elapsed).toBeLessThan(3_000); // SLA internal: 3 detik

  const body = await response.json();
  expect(body).toHaveProperty('token');
  expect(body.token).toBeTruthy();
});

Perhatikan saya pakai request fixture (Playwright APIRequestContext) untuk cek API langsung, tanpa buka browser. Ini lebih cepat dan lebih presisi untuk cek latency API.

GitHub Actions Workflow

Ini bagian utamanya. Scheduled workflow yang jalan tiap 15 menit:

# .github/workflows/synthetic-monitor.yml
name: Synthetic Monitor

on:
  schedule:
    # Tiap 17 menit — hindari jam penuh yang sering delay di GitHub Actions
    - cron: '*/17 * * * *'
  workflow_dispatch:  # Bisa trigger manual untuk debugging

jobs:
  monitor:
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'
          cache-dependency-path: playwright-monitors/package-lock.json

      - name: Install dependencies
        working-directory: playwright-monitors
        run: npm ci

      - name: Install Playwright browsers
        working-directory: playwright-monitors
        run: npx playwright install --with-deps chromium

      - name: Run synthetic monitors
        working-directory: playwright-monitors
        env:
          MONITOR_BASE_URL: ${{ secrets.MONITOR_BASE_URL }}
          MONITOR_USER_EMAIL: ${{ secrets.MONITOR_USER_EMAIL }}
          MONITOR_USER_PASSWORD: ${{ secrets.MONITOR_USER_PASSWORD }}
        run: npx playwright test

      - name: Upload test artifacts
        if: always()  # Selalu upload, bahkan saat gagal
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report-${{ github.run_id }}
          path: playwright-monitors/playwright-report/
          retention-days: 7

      - name: Alert ke Slack saat gagal
        if: failure()
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": ":rotating_light: *Synthetic Monitor GAGAL*",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": ":rotating_light: *Synthetic Monitor GAGAL*\n*Workflow:* ${{ github.workflow }}\n*Run:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Lihat detail>"
                  }
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_MONITOR_WEBHOOK }}
          SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

Dua hal yang saya perhatikan di sini:

timeout-minutes: 10 di level job — ini safety net agar tidak ada run yang menggantung lebih dari 10 menit dan memakan menit GitHub Actions. Tanpa ini, run yang hang karena browser crash bisa terus berjalan.

if: always() di upload artifact — report Playwright harus diupload meskipun test gagal, justru itulah saat kita paling butuh screenshot dan trace.

Dedikasi Test User

Jangan pakai akun user sungguhan untuk synthetic monitoring. Buat akun dedicated di production (atau staging, tergantung policy) dengan label yang jelas — misalnya [email protected]. Alasannya:

  1. Log login dari monitor tidak bercampur dengan log user nyata
  2. Kalau perlu reset password atau token, tidak mengganggu user lain
  3. Beberapa sistem mengirim notifikasi login — akun monitor sebaiknya dikecualikan dari notifikasi ini

Di sistem yang saya kelola, akun monitor dikasih flag is_synthetic: true di database. Middleware logging memakai flag ini untuk memberi label “synthetic” di log entries, sehingga analyst tidak bingung saat melihat 96 login per hari dari satu akun.

Trade-off yang Harus Diakui

Delay jadwal cron GitHub Actions. Ini bukan monitoring real-time. Di jam sibuk, jadwal bisa delay 10-20 menit. Ini bukan masalah besar kalau Anda kombinasikan dengan uptime checker eksternal yang cepat (UptimeRobot gratis, Better Uptime, dsb.) sebagai alarm pertama. Synthetic monitoring Playwright lebih cocok sebagai “lapisan kedua” yang memberikan konteks lengkap tentang apa yang rusak, bukan hanya apakah rusak.

Biaya menit GitHub Actions. Satu run monitor memakan sekitar 3-5 menit (install dependency + browser + jalankan test). Dengan interval 17 menit, itu sekitar 85 run per hari, atau 425-425 menit per hari. Dalam sebulan: ~13.000 menit. GitHub Actions gratis hanya 2.000 menit untuk private repo — jadi ini akan over limit. Solusinya: gunakan cache agresif untuk node_modules dan browser Playwright, atau jadikan repository monitoring ini public (kalau tidak ada credential di kode), sehingga unlimited menit.

Untuk runner cost yang lebih efisien di production, saya pakai self-hosted runner di VPS Hetzner kecil (4 euro/bulan) yang sudah punya Chromium terinstall. Tidak ada biaya menit GitHub Actions, install browser skip.

Flakiness. Browser test selalu punya tingkat flakiness. Dengan retries: 1, satu kegagalan transient tidak langsung memicu alert. Tapi tetap monitor false positive rate — kalau tim mulai “ignore” alert karena sering false alarm, monitoring kehilangan nilainya.

Meningkatkan Resolusi: Multiple Regions

Satu kelemahan GitHub Actions adalah tidak ada pemilihan region runner — Anda tidak bisa pilih “jalankan dari Singapura”. Untuk monitoring latency yang region-aware, Checkly atau Datadog Synthetics tetap menang. Workaround di GitHub Actions: setup multiple repository dengan self-hosted runner di VPS yang berbeda region (misalnya Hetzner Helsinki dan Vultr Singapore), lalu sinkronkan workflow-nya.

Saya belum melakukan ini di semua klien — hanya di satu yang SLA-nya mengharuskan monitoring dari beberapa region.

Verdict

Synthetic monitoring Playwright di GitHub Actions adalah titik awal yang sangat layak untuk tim yang belum siap komit ke Checkly atau Datadog Synthetics. Setup 1-2 jam, tidak ada biaya tambahan untuk public repo atau dengan self-hosted runner, dan hasilnya jauh lebih bermakna dari sekadar ping uptime.

Pakai ini kalau: tim kecil (1-5 engineer), proyek masih pre-revenue atau early stage, atau Anda ingin validasi user flow kritis tanpa menambah vendor baru. Migrasi ke Checkly atau Datadog Synthetics ketika: Anda butuh monitoring multi-region yang akurat, Anda butuh alerting dengan SLA yang ketat (respon dalam 1-2 menit), atau ketika GitHub Actions delay sudah sering menyebabkan miss pada incident nyata.

Yang paling penting: mulai dengan flow yang paling sering rusak dan paling mahal kalau down. Untuk klien fintech saya, itu adalah login dan payment flow. Dua test itu sudah menangkap tiga incident sebelum user report masuk.

Ditulis oleh Reza Pradipta