生活分享

實作功能:完成待辦篩選

從 start 材料補上 Active/Completed 篩選,保留資料格式並用正常與邊界案例驗收。

閱讀時間約 14 分鐘 · 操作 30 分鐘

原創流程示意圖,非產品介面截圖。
圖片:Mokaair (© Mokaair)
回總目錄:Codex 學習中心:完整教學目錄

實作 · Desktop / CLI / VS Code / JetBrains / cloud

本篇目錄
  1. 目標與準備
  2. 步驟 1:從未完成版本建立基準
  3. 步驟 2:用案例固定完成條件
  4. 步驟 3:實作最小功能
  5. 步驟 4:驗收資料、畫面與範圍
  6. 反例驗證:全數計數與同名任務
  7. 失敗排除、交付與還原

目標與準備

本段提到的教學與資源:

練習審閱與交付計畫;這篇把焦點放在功能從失敗到完成的證據。題目相同,驗收更深入:不能只看畫面剩一列就說篩選成功,還要證明被隱藏的資料沒有被刪除、排序不變,兩個相同標題仍能依識別碼正確處理。

步驟 1:從未完成版本建立基準

從練習 ZIP 的 start 複製五個檔案到新的 codex-feature-lab,不沿用已完成的 expected,也不把前一篇修好的 core.mjs 帶進來。開啟資料夾後確認路徑;PowerShell 用 Get-Location,macOS/Linux 用 pwd。保留 start 原版,先跑下列既有測試,預期 2 項通過、1 項篩選失敗。

終端機:原始測試 · sh
node --test core.test.mjs

先讀 index.html 的選項與 app.js 的 filter change 事件,確認控制項已經存在。缺少的是 visibleTasks 的行為,不需要重寫 UI 或安裝框架。這也是先讀專案的價值:檔案名稱看起來像前端,不代表所有變更都要從畫面開始。將既有失敗結果記在筆記,後續不能靠刪測試改寫成功。

步驟 2:用案例固定完成條件

將下列三個驗收測試存為根目錄 filter.test.mjs。它是本篇新增的測試檔,不覆蓋 core.test.mjs。第一個用 a 與 c 兩個同名 Read,確認完成篩選保留兩個識別碼且順序相同;第二個確認 All 沒丟資料;第三個涵蓋三種空清單。凍結輸入會讓不當原地修改更容易被抓出來。

新增檔案:filter.test.mjs · javascript
import test from "node:test";
import assert from "node:assert/strict";
import { visibleTasks } from "./core.mjs";

const tasks = Object.freeze([
  Object.freeze({ id: "a", title: "Read", completed: true }),
  Object.freeze({ id: "b", title: "Build", completed: false }),
  Object.freeze({ id: "c", title: "Read", completed: true }),
]);
const ids = (items) => items.map((item) => item.id);

test("filters preserve identity and order with duplicate titles", () => {
  assert.deepEqual(ids(visibleTasks(tasks, "active")), ["b"]);
  assert.deepEqual(ids(visibleTasks(tasks, "completed")), ["a", "c"]);
  assert.deepEqual(ids(tasks), ["a", "b", "c"]);
});

test("All retains every task without changing its completion state", () => {
  assert.deepEqual(ids(visibleTasks(tasks, "all")), ["a", "b", "c"]);
  assert.deepEqual(tasks.map((task) => task.completed), [true, false, true]);
});

test("every filter accepts an empty list", () => {
  for (const filter of ["all", "active", "completed"]) {
    assert.deepEqual(visibleTasks(Object.freeze([]), filter), []);
  }
});
終端機:原始與新增測試 · sh
node --test core.test.mjs filter.test.mjs

在 start 起點,合併執行應是 4 過、2 敗:既有篩選測試與新第一項測試會失敗,All 和空陣列本來就通過。這讓你知道新測試確實能抓到缺少篩選的問題。若出現語法錯誤或找不到檔案,先修正儲存位置與複製內容;它們不是預期的功能失敗,不能混成同一個紅燈。

步驟 3:實作最小功能

桌面版加入 codex-feature-lab 為本機專案並建立新任務;CLI 從此練習目錄執行 codex。確認任務路徑後,貼上完整要求。這時原始程式只有 core.mjs 可修改,新增的 filter.test.mjs 內容也已經固定。若模型認為需要其他檔案,先請它指出具體原因;不要一開始就讓它把所有測試、畫面與設定一起「整理」。沒有讀到測試時,要求先讀,避免只照自然語言猜測。

Codex 提示詞:實作篩選 · text
Implement the missing filters in this codex-feature-lab.
Read index.html, app.js, core.mjs, core.test.mjs and filter.test.mjs first.
Modify only visibleTasks in core.mjs: all keeps every task, active returns unfinished tasks, completed returns finished tasks. Preserve original order, IDs and input data. Do not edit tests, add dependencies or change UI/storage.
Run node --test core.test.mjs filter.test.mjs. Inspect the final diff and report actual results and any unperformed browser checks. Stop and explain if the requested scope cannot satisfy the tests. Do not publish or deploy.

參考實作如下。filter 產生符合條件的新陣列,裡面仍引用原任務物件;這個函式只挑選,不修改物件,因此符合本題要求。All 可以回傳原陣列,不必為了看起來「更安全」進行沒有必要的深層複製。使用 !task.completed 與 task.completed 的差別要能自己讀懂,不能只認測試變綠。

參考修正:core.mjs 的 visibleTasks · javascript
export function visibleTasks(tasks, filter) {
  if (filter === "active") return tasks.filter((task) => !task.completed);
  if (filter === "completed") return tasks.filter((task) => task.completed);
  return tasks;
}

步驟 4:驗收資料、畫面與範圍

重跑同一條測試命令,參考修正預期 6 項全過。接著核對差異:原本五個檔案只改了 core.mjs,另外多一個你新增的 filter.test.mjs。若測試 assertions 被刪除,先恢復固定測試再跑;若增加無關檔案,逐一檢查來源,不能因為成功摘要漂亮就忽略範圍。修改完成與驗收完成分開記錄。

從 codex-feature-lab 啟動 Python HTTP 預覽,Windows 用 py -m http.server 4173 --bind 127.0.0.1,macOS/Linux 用 python3 代替 py,瀏覽器開 http://127.0.0.1:4173。先處理自己原有預覽的埠占用,不停其他服務。直接雙擊 HTML 可能因模組限制載入失敗;若看到舊畫面,確認伺服器目錄及重新整理後再判斷。

先保留仍需要的舊練習紀錄,再於 About this exercise 使用 Reset practice data。選 All tasks 並確認空清單後,依序加入 Read、Build、Read,勾選第一與第三項。照表切換,再回 All 確認三筆都在;同名項目以原先位置與勾選狀態辨認。取消第一項完成後,Completed 應只剩原第三項,Active 應保留原第一項與 Build。最後切回 All 重新整理,確認狀態仍在。

檢查點AllActiveCompleted
原第一、第三項完成Read、Build、ReadBuildRead、Read
原第一項取消完成Read、Build、ReadRead、Build原第三項 Read
清空教材任務後空空空

反例驗證:全數計數與同名任務

畫面練習中,Read、Build、Read 共三筆,第一和第三筆完成時,All/Active/Completed 的列數分別為 3/1/2,但三種畫面的計數都應是 1 active / 3 total。取消第一筆完成後,三種列數變成 3/2/1,計數都為 2 active / 3 total。app.js 用完整 tasks 計數;只通過核心篩選測試不能證明畫面計數正確,請另記實際結果。

六項測試通過後,先把修好的 core.mjs 複製到練習資料夾外保存。在練習版只暫時把 Completed 分支替換成下面的錯誤寫法,再跑原兩個測試檔。它先按完成狀態篩選,再按 title 去重,會遺失 ID c;預期 5 過 1 敗。原三項測試單獨跑仍全過,因為其兩筆資料沒有同名。讀出失敗的實際 ID 後,立刻用剛才保存的修正版恢復 core.mjs,應回到 6 過 0 敗;測試檔保持不變。

故意錯誤:暫時替換 core.mjs 的 Completed 分支 · javascript
if (filter === "completed") return tasks.filter((task) => task.completed).filter((task, index, selected) => selected.findIndex((other) => other.title === task.title) === index);

若反例沒有失敗,先確認有執行 filter.test.mjs、第三筆仍為不同 ID 的 Read,以及正在測試修改過的副本。若變成語法或找不到模組錯誤,先恢復檔案再核對替換位置;那不是成功抓到去重缺陷。這個反例只檢查測試覆蓋,不需要改 UI 或提交錯誤版本。

失敗排除、交付與還原

篩選時資料真的少了,先看是否錯把 filter 結果寫回 tasks 或保存;畫面數量對但同名任務不對,要檢查是否用 title 代替 id。核心全過但畫面無變,先讀 app.js 是否載入正確 core.mjs,並核對 HTTP 來源與瀏覽器快取。不要連續要求「再修一次」卻不提供哪個表格列失敗,先把重現順序與預期寫清楚。

交付應包含六項測試摘要、畫面案例、最終差異,以及沒有操作的檢查清單。重做時停止預覽,只從 start 還原 core.mjs;若保留新測試,應回到 4 過 2 敗,若只執行舊測試,則是 2 過 1 敗。保留兩個命令的差別,避免把測試數量不同誤當另一個故障。圖中 1 是固定案例,2 是限定修改,3 是回歸與還原。這些是可執行參考材料,不是聲稱已替你執行 Codex 任務。

原創流程示意圖,非產品介面截圖。
原創流程示意圖,非產品介面截圖。 · 圖片:Mokaair (© Mokaair)
閱讀完整文字說明

Three numbered stages: identify the starting point, perform the exercise, and verify the result. Original illustration, not a product screenshot.

Completed 篩選顯示兩筆 Read,總計仍有三筆任務。
本篇參考修正的實際成果:Completed 保留兩筆不同識別碼的 Read,計數仍為 1 active / 3 total。Windows / Edge 153.0.4234.32,2026-09-14;虛構資料,窄版為響應式視窗,非實體手機。這是練習網站,不是 Codex 介面或模型執行證據。 · 圖片:Mokaair (© Mokaair)
Completed 篩選顯示兩筆 Read,總計仍有三筆任務。
本篇參考修正的實際成果:Completed 保留兩筆不同識別碼的 Read,計數仍為 1 active / 3 total。Windows / Edge 153.0.4234.32,2026-09-14;虛構資料,窄版為響應式視窗,非實體手機。這是練習網站,不是 Codex 介面或模型執行證據。 · 圖片:Mokaair (© Mokaair)

回總目錄

  • 生活分享

    Codex 學習中心:完整教學目錄

    從安裝、第一個任務到 MD 規則與進階整合,規劃 60 篇 Codex 教學、十個單元。依程度、平台、需求或指令搜尋下一篇;尚未公開的教學會標示狀態,方便安排學習路線。

  • 生活分享

    Worktree 與多任務隔離

    Worktree 讓同一個 Git 程式庫有不同的工作目錄,各自承接不同分支。它適合讓兩項工作分開改檔,但資料庫、連接埠與外部服務仍可能共用,不能把檔案隔離當成所有資源隔離。

  • 生活分享

    實戰:製作小網站

    從 brief.md 規劃並製作 Small Steps 待辦網站,完成新增、完成、刪除、篩選與本機資料保存。將 HTML、CSS、資料函式、畫面事件與測試分開,以 Node 測試和瀏覽器操作驗收,並留下可重新啟動與還原的交接紀錄。

  • 生活分享

    用量與效率:減少重工

    記錄任務條件、模型選項、時間與成果,找出能減少無效重試和過多上下文的調整。

最新旅遊情報攻略

資料來源

生活分享