Proyek kalkulator melatih state berupa beberapa nilai yang saling menggantikan. Proyek kedua ini melatih wujud state yang jauh lebih umum di aplikasi nyata: daftar. Aplikasi todo terlihat sederhana di mata penggunanya — tulis tugas baru, centang yang sudah selesai, hapus yang tidak diperlukan lagi, dan daftar tetap utuh ketika tab ditutup lalu dibuka kembali. Di balik kesederhanaan itu ada pertanyaan desain yang nyata: bagaimana mewakili satu item di dalam daftar, bagaimana menambah dan mengurangi isi array tanpa mengubah array lama, dan bagaimana daftar itu bertahan di luar memori halaman.
Empat pertanyaan itu dijawab bertahap dalam satu produk utuh: aplikasi todo dengan form input, kotak centang status, tiga pilihan filter, ringkasan sisa tugas, dan penyimpanan localStorage. Pendekatan utamanya sama dengan kalkulator — pemisahan logika dari tampilan. Seluruh keputusan data hidup di src/tugas.mjs, module murni tanpa satu pun document maupun localStorage, sehingga seluruh perilakunya bisa dibuktikan lewat node --test dari terminal. Berkas HTML hanya menampilkan state dan meneruskan peristiwa ke logika.
Proyek dikerjakan dalam empat bagian berurutan:
- Bagian 1: Menambah dan Menghapus Tugas — merancang state berupa array yang berisi object
{ id, teks }, menulis tiga fungsi murni pertama (tambahTugas,hapusTugas,daftarTugas), menyusun kerangka antarmuka HTML beserta CSS-nya, dan mengunci perilaku awal dengan delapan uji otomatis. Kita belajar memberi identitasidpada setiap item dan menjaga immutability di setiap operasi. - Bagian 2: Status dan Filter — menambahkan field
selesaipada setiap tugas, fungsiubahStatusyang membaliknya,filterTugasdengan tiga nilai sah (semua,aktif,selesai), danhitungAktifuntuk ringkasan sisa pekerjaan. Antarmuka bertambah kotak centang, tombol filter, dan paragraf ringkasan; delapan uji tumbuh menjadi tujuh belas. - Bagian 3: Penyimpanan Lokal — memasang
localStoragetanpa membuat uji Node gagal jalan:simpanTugasdanmuatTugasbekerja dengan string JSON di dalam logika murni, sementara pembungkuslocalStorageyang tipis tinggal di antarmuka.muatTugasmenjaga data dengan tiga lapis pemeriksaan, dan uji mencapai dua puluh empat. - Bagian 4: Pengujian — meninjau seluruh strategi pengujian proyek sebagai satu gambaran utuh: peta sepuluh suite uji, pola Arrange-Act-Assert, cara menjalankan dan membaca keluaran
node --test, serta opsi menguji antarmuka dengan jsdom yang dibahas tuntas pada section Testing.
Struktur Berkas Proyek #
Seluruh kode proyek tinggal di tiga folder pada content/javascript/proyek/aplikasi-todo/:
aplikasi-todo/
├── src/
│ ├── tugas.mjs # logika inti: daftar tugas, status, filter, penyimpanan
│ └── index.html.txt # contoh antarmuka (salin ulang sebagai index.html)
├── styles/
│ └── styles.css # tampilan aplikasi todo
└── tests/
└── tugas.test.mjs # uji otomatis untuk tugas.mjsPeran tiap berkas:
| Berkas | Peran |
|---|---|
src/tugas.mjs | Otak aplikasi: menambah, menghapus, mengubah status, memfilter, dan mengubah daftar menjadi JSON. Tidak menyentuh DOM sama sekali. |
src/index.html.txt | Wajah aplikasi: form input, kotak centang, tombol filter, ringkasan, dan skrip penghubung ke logika. |
styles/styles.css | Penampil: kartu aplikasi di tengah layar, baris tugas, dan tombol-tombolnya. |
tests/tugas.test.mjs | Penjaga: dua puluh empat uji otomatis yang dijalankan test runner bawaan Node. |
Setiap bagian menampilkan kondisi berkas pada akhir bagian tersebut: blok kode yang membahas sebuah berkas menyediakan tautan unduhan salinannya di header blok. Versi lengkap dan terakhir dari seluruh berkas selalu ada di folder src/, styles/, dan tests/ di atas — jika Anda mengikuti proyek dari Bagian 1, berkas-berkas itu tumbuh bertahap mengikuti pembahasan.
Menjalankan Pengujian #
Uji otomatis dijalankan dari folder proyek dengan test runner bawaan Node.js:
cd aplikasi-todo
node --testNode mencari berkas uji dengan pola nama seperti *.test.mjs, menemukan tests/tugas.test.mjs, lalu menjalankan seluruh ujinya. Ringkasan di ujung keluarannya pada akhir proyek berbunyi:
ℹ tests 24
ℹ suites 10
ℹ pass 24
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 81.670704Angka pass 24 berarti seluruh uji lulus; duration_ms akan berbeda-beda di mesin Anda karena mengukur waktu eksekusi. Bila ingin menyasar satu berkas uji secara eksplisit, tulis nama berkasnya utuh: node --test tests/tugas.test.mjs.
Mulailah dari Bagian 1 dan ikuti urutannya — setiap bagian dibangun di atas bagian sebelumnya, dan pada bagian akhir aplikasi todo siap dipakai lengkap dengan penyimpanan lokalnya serta dua puluh empat uji yang menjaganya.
Membangun CRUD dasar tugas dengan DOM dan state array.
Buka → # Bagian 2: Status dan FilterMenandai tugas selesai dan memfilter berdasarkan statusnya.
Buka → # Bagian 3: Penyimpanan LokalMenyimpan daftar tugas ke localStorage dan memulihkannya saat dimuat.
Buka → # Bagian 4: PengujianMenguji logika aplikasi todo dengan node:test dan jsdom.
Buka →Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
styles/styles.css
Unduh berkas/* styles.css — tampilan aplikasi todo sederhana */
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: #f3f4f6;
font-family: system-ui, sans-serif;
}
.aplikasi {
width: 340px;
padding: 20px;
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.aplikasi h1 {
margin: 0 0 16px;
font-size: 1.4rem;
}
.form-tugas {
display: flex;
gap: 8px;
margin-bottom: 12px;
}
.form-tugas input {
flex: 1;
padding: 10px;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 1rem;
}
.tombol-tambah {
padding: 10px 16px;
border: none;
border-radius: 8px;
background-color: #3b82f6;
color: #ffffff;
font-size: 1rem;
cursor: pointer;
}
.tombol-tambah:hover {
background-color: #2563eb;
}
.pesan {
margin: 0 0 12px;
color: #b91c1c;
font-size: 0.9rem;
}
.filter {
display: flex;
gap: 8px;
margin-bottom: 12px;
}
.filter-tombol {
flex: 1;
padding: 8px 0;
border: 1px solid #d1d5db;
border-radius: 8px;
background-color: #f9fafb;
font-size: 0.9rem;
cursor: pointer;
}
.filter-tombol:hover {
background-color: #e5e7eb;
}
.ringkasan {
margin: 0 0 12px;
color: #6b7280;
font-size: 0.9rem;
}
.daftar-tugas {
margin: 0;
padding: 0;
list-style: none;
display: grid;
gap: 8px;
}
.tugas {
display: flex;
align-items: center;
gap: 10px;
padding: 10px;
background-color: #f9fafb;
border-radius: 8px;
}
.tugas span {
flex: 1;
}
.tugas--selesai span {
color: #9ca3af;
text-decoration: line-through;
}
.tombol-hapus {
padding: 6px 10px;
border: none;
border-radius: 6px;
background-color: #fecaca;
font-size: 0.85rem;
cursor: pointer;
}
.tombol-hapus:hover {
background-color: #fca5a5;
}