import { describe, expect, it } from "vitest" import { footerTextWidth, getLayout, LAYOUTS, QR_GAP, QR_SIZE, qrRect, type LayoutId, } from "@/lib/layouts" const IDS: LayoutId[] = ["strip-4", "strip-3"] describe("geometri ekspor strip", () => { it("kanvas kedua layout pas 600 x 1800 px", () => { for (const id of IDS) { expect(getLayout(id).canvas).toEqual({ width: 600, height: 1800 }) } }) it("lebar pas: dua margin ditambah lebar slot sama dengan lebar kanvas", () => { for (const id of IDS) { const layout = getLayout(id) expect(layout.margin * 2 + layout.slot.width).toBe(layout.canvas.width) expect(layout.footerRect.width).toBe(layout.slot.width) expect(layout.footerRect.x).toBe(layout.margin) } }) it("tinggi pas: margin, seluruh slot, jarak, dan kaki sama dengan tinggi kanvas", () => { for (const id of IDS) { const { margin, shotCount, slot, gap, footer, canvas } = getLayout(id) const used = margin + shotCount * slot.height + (shotCount - 1) * gap + footer.height expect(used).toBe(canvas.height) } }) it("kaki strip menyentuh tepi bawah kanvas", () => { for (const id of IDS) { const { footerRect, canvas } = getLayout(id) expect(footerRect.y + footerRect.height).toBe(canvas.height) } }) it("slot berurutan dari atas tanpa tumpang tindih, berjarak tepat satu gap", () => { for (const id of IDS) { const { slotRects, gap, margin, slot } = getLayout(id) slotRects.forEach((rect, index) => { expect(rect.x).toBe(margin) expect(rect.width).toBe(slot.width) expect(rect.height).toBe(slot.height) expect(rect.y).toBe(margin + index * (slot.height + gap)) if (index > 0) { expect(rect.y - (slotRects[index - 1].y + slot.height)).toBe(gap) } }) } }) it("jumlah slot mengikuti layout dan berhenti sebelum kaki strip", () => { expect(getLayout("strip-4").shotCount).toBe(4) expect(getLayout("strip-4").slotRects).toHaveLength(4) expect(getLayout("strip-4").slot.height).toBe(372) expect(getLayout("strip-3").shotCount).toBe(3) expect(getLayout("strip-3").slotRects).toHaveLength(3) expect(getLayout("strip-3").slot.height).toBe(504) const lastSlot = getLayout("strip-4").slotRects.at(-1) expect(lastSlot?.y).toBeDefined() expect((lastSlot?.y ?? 0) + (lastSlot?.height ?? 0)).toBeLessThanOrEqual( getLayout("strip-4").footerRect.y ) }) it("label mengikuti DESIGN-SYSTEM ยง12", () => { expect(LAYOUTS["strip-4"].label).toBe("Strip 4 foto") expect(LAYOUTS["strip-3"].label).toBe("Strip 3 foto") }) }) /* FR-03: Grid 2 x 2 selebar lembar 4R (1200 x 1800). */ describe("layout grid 2 x 2", () => { it("kanvas 1200 x 1800 dengan empat slot portret dua kolom", () => { const layout = getLayout("grid-2x2") expect(layout.label).toBe("Grid 2 x 2") expect(layout.shotCount).toBe(4) expect(layout.canvas).toEqual({ width: 1200, height: 1800 }) expect(layout.slot).toEqual({ width: 540, height: 744 }) expect(layout.slotRects).toEqual([ { x: 48, y: 48, width: 540, height: 744 }, { x: 612, y: 48, width: 540, height: 744 }, { x: 48, y: 816, width: 540, height: 744 }, { x: 612, y: 816, width: 540, height: 744 }, ]) expect(layout.footerRect).toEqual({ x: 48, y: 1560, width: 1104, height: 240, }) }) it("margin, jarak, dan kaki pas sampai tepi kanvas", () => { const layout = getLayout("grid-2x2") const kolomKedua = layout.slotRects[1] const barisKedua = layout.slotRects[2] expect(kolomKedua.x + kolomKedua.width + layout.margin).toBe( layout.canvas.width ) expect(kolomKedua.x - (layout.slotRects[0].x + layout.slotRects[0].width)).toBe( layout.gap ) expect(barisKedua.y - (layout.slotRects[0].y + layout.slotRects[0].height)).toBe( layout.gap ) expect(barisKedua.y + barisKedua.height).toBe(layout.footerRect.y) expect(layout.footerRect.y + layout.footerRect.height).toBe( layout.canvas.height ) }) }) /* SH-04: QR 168 x 168 px di kanan kaki strip, 16 px dari kotak teks. */ describe("geometri QR di kaki strip", () => { it("kotak QR 168 px dan menempel tepi kanan kaki", () => { for (const id of ["strip-4", "strip-3", "grid-2x2"] as LayoutId[]) { const layout = getLayout(id) const kotak = qrRect(layout) expect(kotak.width).toBe(QR_SIZE) expect(kotak.height).toBe(QR_SIZE) expect(kotak.x + kotak.width).toBe( layout.footerRect.x + layout.footerRect.width ) /* Tegak lurus di tengah kaki strip. */ expect(kotak.y + kotak.height / 2).toBe( layout.footerRect.y + layout.footerRect.height / 2 ) expect(kotak.y).toBeGreaterThanOrEqual(layout.footerRect.y) expect(kotak.y + kotak.height).toBeLessThanOrEqual( layout.footerRect.y + layout.footerRect.height ) } }) it("posisi QR sama dengan angka di rencana M6", () => { expect(qrRect(getLayout("strip-4"))).toMatchObject({ x: 396, y: 1614 }) expect(qrRect(getLayout("grid-2x2"))).toMatchObject({ x: 984, y: 1596 }) }) it("kotak teks menyempit tepat selebar kaki dikurangi QR dan jaraknya", () => { for (const id of ["strip-4", "grid-2x2"] as LayoutId[]) { const layout = getLayout(id) expect(footerTextWidth(layout, false)).toBe(layout.footerRect.width) expect(footerTextWidth(layout, true)).toBe( layout.footerRect.width - QR_SIZE - QR_GAP ) /* Kotak teks tidak boleh menyentuh QR. */ expect( layout.footerRect.x + footerTextWidth(layout, true) + QR_GAP ).toBe(qrRect(layout).x) } }) })