diff --git a/Makefile b/Makefile
index a7cd8ea..cdc2216 100644
--- a/Makefile
+++ b/Makefile
@@ -72,8 +72,13 @@ test-list:
## Show test counts by marker
test-stats:
@echo "=== Test counts by marker ==="
- @$(PYTEST) --collect-only -q -m unit 2>/dev/null | grep -c "test" || echo "0"
+ @$(PYTEST) --collect-only -q -m unit 2>/dev/null | tail -1
@echo "^ unit tests"
- @$(PYTEST) --collect-only -q -m integration 2>/dev/null | grep -c "test" || echo "0"
+ @$(PYTEST) --collect-only -q -m integration 2>/dev/null | tail -1
@echo "^ integration tests"
+ @$(PYTEST) --collect-only -q -m e2e 2>/dev/null | tail -1
+ @echo "^ e2e tests"
+ @$(PYTEST) --collect-only -q -m "not unit and not integration and not e2e" 2>/dev/null | tail -1
+ @echo "^ ORPHAN tests (tanpa marker) — harus 0, unit + integration == total"
@$(PYTEST) --collect-only -q 2>/dev/null | tail -1
+ @echo "^ total collected"
diff --git a/README.md b/README.md
index c474739..a70b9d2 100644
--- a/README.md
+++ b/README.md
@@ -493,14 +493,20 @@ student_id;token;nama_siswa;hello_world;variabel
|| `./elemes.sh test` | Full test suite (alias ke `test-all`) |
|| `./elemes.sh test-unit` | Unit test saja (cepat, no DB) |
|| `./elemes.sh test-integration` | Integration test (butuh PostgreSQL `elemes_test`) |
-|| `./elemes.sh test-all` | Full test suite (CI gate) |
+|| `./elemes.sh test-all` | Full test suite |
|| `./elemes.sh test-smoke` | Smoke test post-deploy (unit + sub-home subset) |
|| `./elemes.sh docs-validate` | Validasi frontmatter & broken link di `docs/*.md` |
+> **Catatan testing (CI):** CI otomatis (GitHub Actions) **belum diaktifkan** —
+> solo dev, test dijalankan manual sebelum push via
+> `make test-unit && make test-integration` (atau `./elemes.sh test-all`).
+> Rencana diaktifkan lagi saat ada kontributor lain; file `ci.yml` versi lama
+> tetap tersimpan di git history (`git show a5aedf6:.github/workflows/ci.yml`).
+
## Dokumentasi & Referensi API
- **Docs Viewer**: Buka `http://localhost:3000/docs` untuk panduan teknis lengkap (arsitektur, backend, frontend, kuis, velxio, embed, dll) yang merender file `docs/*.md` secara dinamis.
-- **API Reference**: `http://localhost:3000/docs/api-reference` menampilkan daftar semua endpoint Flask dengan docstring, metode, path, dan requirement auth.
+- **API Reference**: Buka `/docs` lalu klik **API Reference** di sidebar (entry pertama) untuk melihat daftar semua endpoint Flask dengan docstring, metode, path, dan requirement auth.
- **Troubleshooting**: Buka `http://localhost:3000/help` untuk tutorial siswa; tautan ke Docs Viewer ada di sana.
## Database & Penyimpanan (PostgreSQL)
diff --git a/frontend/src/lib/types/docs.ts b/frontend/src/lib/types/docs.ts
index f87da8b..25a6e6f 100644
--- a/frontend/src/lib/types/docs.ts
+++ b/frontend/src/lib/types/docs.ts
@@ -17,3 +17,16 @@ export interface DocMeta {
order: number;
category: string;
}
+
+/** Satu entry dari GET /api/docs/api-reference. */
+export interface ApiReferenceEntry {
+ method: string[];
+ path: string;
+ name: string;
+ auth: boolean;
+ doc: string;
+}
+
+export interface ApiReferenceResponse {
+ endpoints: ApiReferenceEntry[];
+}
diff --git a/frontend/src/routes/docs/+page.svelte b/frontend/src/routes/docs/+page.svelte
index 665a26e..38bbad8 100644
--- a/frontend/src/routes/docs/+page.svelte
+++ b/frontend/src/routes/docs/+page.svelte
@@ -2,6 +2,7 @@
import { env } from '$env/dynamic/public';
import { renderMath } from '$lib/actions/renderMath';
import { tick } from 'svelte';
+ import type { ApiReferenceEntry } from '$types/docs';
export let data: {
docs: import('$types/docs').DocsIndexEntry[]
@@ -12,11 +13,16 @@
let docContent: { title: string; html: string } | null = null;
let docError = false;
+ let apiRefMode = false;
+ let apiRefEntries: ApiReferenceEntry[] | null = null;
+ let apiRefError = false;
+
$: filteredDocs = data.docs.filter(d =>
d.title.toLowerCase().includes(searchQuery.toLowerCase())
);
async function selectDoc(doc: import('$types/docs').DocsIndexEntry) {
+ apiRefMode = false;
selectedDoc = doc;
searchQuery = '';
docError = false;
@@ -30,10 +36,29 @@
docError = true;
}
}
+
+ async function selectApiReference() {
+ apiRefMode = true;
+ selectedDoc = data.docs[0];
+ searchQuery = '';
+ docError = false;
+ docContent = null;
+
+ if (apiRefEntries) return; // sudah pernah dimuat — reuse
+ apiRefError = false;
+ try {
+ const res = await fetch('/api/docs/api-reference');
+ if (!res.ok) throw new Error('Failed to load API reference');
+ const payload = await res.json();
+ apiRefEntries = payload.endpoints;
+ } catch {
+ apiRefError = true;
+ }
+ }
- {selectedDoc?.title || 'Dokumentasi'} - Elemes LMS
+ {apiRefMode ? 'API Reference' : selectedDoc?.title || 'Dokumentasi'} - Elemes LMS
@@ -47,9 +72,18 @@
/>