id มาตรฐานเดียว
Playwright ที่ไม่พังทุกครั้งที่ refactor

tidwright ให้ AI เติม data-testid ตามมาตรฐานเดียวกันทั้งโปรเจกต์ โดยมีเครื่องมือตรวจว่าโค้ดไม่พัง แล้วให้ QA เขียน E2E บน id เหล่านั้นผ่าน getByTestId ไม่ต้องไล่อ่าน source หรือเดา CSS selector

Claude Code · Codex · CursorReact · Next · Vue · Nuxt · Svelte · Angular · HTMLruntime dependency = 0

ไม่ใช้ กับ ใช้ ต่างกันยังไง

เทียบวิธีทำงานของทีม ตอนยังไม่มีมาตรฐาน กับตอนใช้ tidwright

เรื่องไม่ใช้ใช้ tidwright
การตั้งชื่อ idแต่ละคนตั้งเอง (btn1, submitBtn) พอ refactor test ก็พังgrammar เดียว <area>-<element>-<type> เช่น login-submit-button และ lint บังคับให้
QA หา selectorอ่าน source เอง หรือถอยไปใช้ CSS, XPath, textอ่านรายการ e2e/TESTIDS.md ที่สร้างให้ แล้วใช้ getByTestId อย่างเดียว
เติม id ทั้งฟีเจอร์เติมมือทีละ element ไม่เคยจบtool เติมชุดที่ปลอดภัยให้ทีเดียว ที่เหลือ AI ตัดสินผ่าน plan ที่ตรวจแล้ว ไม่แก้ไฟล์สดๆ
กลัวว่าแก้แล้วพังต้องอ่าน diff ทุกบรรทัดด้วยตาตรวจด้วย parser ของโปรเจกต์ + build/typecheck ต้องผ่านทั้งก่อนและหลัง ไม่ผ่านก็คืนไฟล์ทั้งหมด
id ซ้ำใน DOMรู้ตอน test flaky หรือไม่รู้เลยguard ใน unit test และ E2E ทำให้ test fail พร้อมบอก id กับจำนวนที่ซ้ำ
spec ใช้ id ที่ source ลบไปแล้วรู้ตอนรันพัง/tidwright:audit จับก่อนรัน พร้อมจับ sleep, .only, credential ใน spec
ใครรับผิดชอบ selectordev กับ QA เถียงกันเขียนเป็นสัญญา: dev เป็นเจ้าของ id, QA เป็นเจ้าของ test, auditor จับ drift

ตัวอย่าง ก่อน → หลัง

- <button onClick={save}>Save</button>
+ <button data-testid="order-form-save-button" onClick={save}>Save</button>
- <input name="customerName" />
+ <input data-testid="order-form-customer-name-input" name="customerName" />

ผลจริงของ scan --write บนไฟล์ OrderForm.tsx (ปุ่ม Save ที่ชื่อมาจากข้อความอย่างเดียวจะถูกแค่ลิสต์ให้ review เว้นแต่ใส่ --accept-text)

ทำงานยังไง

ห้าขั้น และมีสองขั้นที่ต้องมีคนอนุมัติ

  1. Scanparser ของโปรเจกต์ระบุตำแหน่ง element ที่ test จะแตะ (ปุ่ม ลิงก์ input form) แล้วเสนอชื่อมาตรฐาน มีแค่ชุดแคบๆ ที่ถูกมาร์คว่าปลอดภัย ที่เหลือเป็นคิว review พร้อมเหตุผลทุกบรรทัด
  2. เติม idtool เติมชุดที่ปลอดภัย AI ตัดสินส่วนที่เหลือ (element ใน loop ได้ ${key} ที่คงที่) ผ่าน plan ที่ตรวจแล้ว ก่อนเขียนต้องถามคนก่อนเสมอ
  3. ตรวจใน transaction เดียวparse ไฟล์ที่แก้ซ้ำ, typecheck/build ต้องเขียว, และถ้าใส่ --smoke จะเปิดเบราว์เซอร์เช็กว่า id ไม่ซ้ำและมีครบ ถ้าขั้นไหนที่รันแล้วพัง ไฟล์ถูกคืนทั้งหมด
  4. Catalogสร้าง e2e/TESTIDS.md ให้ QA ทุกครั้งที่เขียน บอกด้วยว่า id แต่ละตัวนิยามด้วยวิธีไหน
  5. QA เขียน testspec ใช้ getByTestId และ audit จับ id ที่ spec ใช้แต่ source ไม่มีแล้ว
Scan, add ids with AI, compile check, catalog, QA tests

หลังใช้ ได้อะไรเพิ่ม

สิ่งที่ทีมได้เพิ่มจากที่ไม่เคยมีมาก่อน

🏷️ มาตรฐาน + lint

รายการ type ปิด (button input select row modal toast page…) ห้ามคำเลื่อนลอยอย่าง btn div red และห้ามเลขลอย (ใช้ business id แทน)

🧩 สูตรต่อ component

recipe modal orders-delete บอกชุด id ที่ modal/table/form/tabs/toast ควรมี ไม่ต้องคิดใหม่ทุกหน้า

🎯 Locator helper

modal(page,"orders-delete").confirm และ table(page,"orders").row(id) ตรงกับสูตร

🛡️ Guard id ซ้ำ

template สำหรับ vitest/jest และ Playwright fixture ที่ fail เมื่อ data-testid เดียวกันอยู่ใน DOM สองที่

🔍 Auditor

จับ sleep, .only, credential literal, selector แบบ CSS/XPath/text และ id ที่ source ไม่มี

🔒 กันพลาดเรื่อง host

template playwright.config.ts ปฏิเสธ host ที่ไม่ใช่ local เว้นแต่อยู่ใน allow list กันเผลอรันกับ production

รายงานจากผู้พัฒนา (ตรวจซ้ำโดยคนอื่นไม่ได้): ตอนลองกับแอป React/TSX ขนาดใหญ่ของทีม guard เจอ id ซ้ำ 49 ตัว ที่ typecheck, lint และ unit test ราว 9,000 ตัวผ่านไปหมด

มาตรฐาน id

<area>-<element>-<type> · kebab-case ASCII · ส่วนท้ายเป็น type จากรายการปิด · แต่ละ id นิยามครั้งเดียว (id แบบ dynamic ก็นับ)

Componentid ที่ควรมี (<n> = ชื่อที่ตั้ง)
modal / drawer<n>-modal · -title-text · -close-button · -confirm-button · -cancel-button · -error-banner
form + field<n>-form · -submit-button · <form>-<field>-input · …-error
table<n>-table · -row-${id} · -cell-${id}-${col} · -sort-button-${col} · -empty · -loading · -error
tabs · menu · pagination · toast<n>-tab-${key} / -panel-${key} · -trigger-button / -item-${key} · -prev-button / -next-button · <action>-success-toast

ความปลอดภัยที่ออกแบบไว้

ไม่เขียนถ้าไม่สั่ง

scan อ่านอย่างเดียว ต้องมี --write และ AI ต้องโชว์ dry run แล้วถามก่อน ไฟล์ที่มีการแก้ค้างใน git ถูกข้าม

ชุดอัตโนมัติแคบโดยตั้งใจ

เติมเฉพาะ native element ที่ชื่อเชื่อถือได้ ไม่ซ้ำ ไม่มี spread และ render ออกมาจริง ที่เหลือลิสต์พร้อมเหตุผล

ตำแหน่งมาจาก parser

ใช้ parser ของโปรเจกต์เอง (TypeScript, Vue, Svelte, Angular, parse5) string, regex หรือ comment จึงไม่ถูกเข้าใจผิดเป็น element ไม่มี parser = แค่ลิสต์ ไม่เขียน

build ต้องเขียว ไม่งั้นคืนไฟล์

--write บังคับ --verify ต้องผ่านทั้งก่อนและหลัง ถ้าไม่ได้รัน browser check รายงานจะบอกชัดว่า NOT run

repo ที่ clone มาสั่งรันคำสั่งเองไม่ได้

คำสั่ง verify ที่มาจากไฟล์ใน repo อย่างเดียวจะไม่ถูกรันจนกว่าคนอนุมัติ

รับโปรเจกต์เก่าได้

lint --write-baseline ยอมรับปัญหาที่มีอยู่ แล้ว CI ล้มเฉพาะปัญหาใหม่

หลักฐาน

รันซ้ำได้ด้วย node tools/corpus-check.mjs บน open-source app 9 ตัวที่ pin commit ไว้ (RealWorld Conduit ใน Vue / SvelteKit / Angular / React, elk, ngx-admin, shadcn-svelte, excalidraw, vercel/commerce)

9แอปจริง
4,691ไฟล์ source ที่สแกน
431id ที่เติมใน 199 ไฟล์
0ไฟล์ที่แก้แล้วไม่ใช่การแทรก attribute ล้วน
0id ที่เติมแล้วผิดมาตรฐานของตัวเอง
0ไฟล์ที่ parse แล้วมี syntax error เพิ่ม
พิสูจน์ได้
  • tool รันบนโค้ดจริงโดยไม่ crash
  • สิ่งที่เขียนคือการแทรก attribute ล้วน (ลบ attribute ที่เติมแล้วได้ไฟล์เดิมทุก byte)
  • id ที่เติมเป็นไปตามมาตรฐานและไม่ชนกัน
ยังไม่พิสูจน์
  • ว่าแอปที่แก้แล้ว compile ผ่าน (ใช้ --verify ในโปรเจกต์จริง)
  • ว่าชื่อที่เสนอเป็นชื่อที่ดีที่สุด
  • ว่า test ที่เขียนบน id เหล่านี้ flaky น้อยลง

ข้อจำกัด (พูดตรงๆ)

เริ่มใช้

ขั้นตอนติดตั้งทั้ง Claude Code, Codex และ Cursor อยู่ใน README ของ repo (ขอ URL ของ repo จากทีม)

/plugin install tidwright@tidwright-marketplace      # Claude Code
/tidwright:init                       # ค้นหา repo → เขียน profile → scaffold (ต้องอนุมัติ)
/tidwright:testid src/features/orders # เติม id ให้หนึ่งฟีเจอร์ แล้วสร้าง catalog
/tidwright:audit                      # ตรวจ spec