test(noc): add API and E2E tests for NOC synchronization
This commit is contained in:
@@ -1,9 +1,19 @@
|
|||||||
import { describe, expect, it } from "bun:test";
|
import { describe, expect, it } from "bun:test";
|
||||||
import api from "@/api";
|
import api from "@/api";
|
||||||
|
import { prisma } from "@/utils/db";
|
||||||
|
|
||||||
describe("NOC API Module", () => {
|
describe("NOC API Module", () => {
|
||||||
const idDesa = "darmasaba";
|
const idDesa = "darmasaba";
|
||||||
|
|
||||||
|
it("should return last sync timestamp", async () => {
|
||||||
|
const response = await api.handle(
|
||||||
|
new Request(`http://localhost/api/noc/last-sync?idDesa=${idDesa}`),
|
||||||
|
);
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
const data = await response.json();
|
||||||
|
expect(data).toHaveProperty("lastSyncedAt");
|
||||||
|
});
|
||||||
|
|
||||||
it("should return active divisions", async () => {
|
it("should return active divisions", async () => {
|
||||||
const response = await api.handle(
|
const response = await api.handle(
|
||||||
new Request(`http://localhost/api/noc/active-divisions?idDesa=${idDesa}`),
|
new Request(`http://localhost/api/noc/active-divisions?idDesa=${idDesa}`),
|
||||||
@@ -71,4 +81,13 @@ describe("NOC API Module", () => {
|
|||||||
// Elysia returns 400 or 422 for validation errors
|
// Elysia returns 400 or 422 for validation errors
|
||||||
expect([400, 422]).toContain(response.status);
|
expect([400, 422]).toContain(response.status);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should return 401 for sync without admin auth", async () => {
|
||||||
|
const response = await api.handle(
|
||||||
|
new Request("http://localhost/api/noc/sync", {
|
||||||
|
method: "POST",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(response.status).toBe(401);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
110
__tests__/e2e/noc-sync.spec.ts
Normal file
110
__tests__/e2e/noc-sync.spec.ts
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
|
test.describe("NOC Synchronization UI", () => {
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
// Mock the session API to simulate being logged in as an admin
|
||||||
|
await page.route("**/api/session", async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({
|
||||||
|
data: {
|
||||||
|
user: {
|
||||||
|
id: "user_123",
|
||||||
|
name: "Admin User",
|
||||||
|
email: "admin@example.com",
|
||||||
|
role: "admin",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock the last-sync API
|
||||||
|
await page.route("**/api/noc/last-sync*", async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({
|
||||||
|
lastSyncedAt: new Date(Date.now() - 3600000).toISOString(), // 1 hour ago
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should navigate to NOC Sync page from sidebar", async ({ page }) => {
|
||||||
|
await page.goto("/");
|
||||||
|
|
||||||
|
// Open Settings/Pengaturan submenu if not open
|
||||||
|
const settingsNavLink = page.locator('button:has-text("Pengaturan")');
|
||||||
|
await settingsNavLink.click();
|
||||||
|
|
||||||
|
// Click on Sinkronisasi NOC
|
||||||
|
const syncNavLink = page.locator('a:has-text("Sinkronisasi NOC")');
|
||||||
|
// In Mantine NavLink with navigate, it might be a button or div with role button depending on implementation
|
||||||
|
// Based on Sidebar.tsx, it's a MantineNavLink which renders as a button or anchor
|
||||||
|
const syncLink = page.getByRole("button", { name: "Sinkronisasi NOC" });
|
||||||
|
await syncLink.click();
|
||||||
|
|
||||||
|
// Verify we are on the sync page
|
||||||
|
await expect(page).toHaveURL(/\/pengaturan\/sinkronisasi/);
|
||||||
|
await expect(page.locator("h2")).toContainText("Sinkronisasi Data NOC");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should perform synchronization successfully", async ({ page }) => {
|
||||||
|
await page.goto("/pengaturan/sinkronisasi");
|
||||||
|
|
||||||
|
// Initial state check
|
||||||
|
await expect(page.locator("text=Waktu Sinkronisasi Terakhir:")).toBeVisible();
|
||||||
|
|
||||||
|
// Mock the sync API
|
||||||
|
const now = new Date().toISOString();
|
||||||
|
await page.route("**/api/noc/sync", async (route) => {
|
||||||
|
if (route.request().method() === "POST") {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: "Sinkronisasi berhasil diselesaikan",
|
||||||
|
lastSyncedAt: now,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click Sync button
|
||||||
|
await page.click('button:has-text("Sinkronkan Sekarang")');
|
||||||
|
|
||||||
|
// Verify success message
|
||||||
|
await expect(page.locator("text=Sinkronisasi berhasil dilakukan")).toBeVisible();
|
||||||
|
|
||||||
|
// Verify timestamp updated (it should show "beberapa detik yang lalu" or similar because of dayjs fromNow)
|
||||||
|
// We can just check if the new time format is there or the relative time updated
|
||||||
|
await expect(page.locator("text=beberapa detik yang lalu")).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle synchronization error", async ({ page }) => {
|
||||||
|
await page.goto("/pengaturan/sinkronisasi");
|
||||||
|
|
||||||
|
// Mock the sync API failure
|
||||||
|
await page.route("**/api/noc/sync", async (route) => {
|
||||||
|
if (route.request().method() === "POST") {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200, // API returns 200 but with success: false for business logic errors
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({
|
||||||
|
success: false,
|
||||||
|
error: "Sinkronisasi gagal dijalankan",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click Sync button
|
||||||
|
await page.click('button:has-text("Sinkronkan Sekarang")');
|
||||||
|
|
||||||
|
// Verify error message
|
||||||
|
await expect(page.locator("text=Sinkronisasi gagal dijalankan")).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user