package main
import (
"path/filepath"
"testing"
)
// ─── checkComingSoon ──────────────────────────────────────────────────────
func TestCheckComingSoon_ComingSoon_Flagged(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "hero.tsx", `export function Hero() {
return
Coming Soon
}
`)
vs := checkComingSoon(dir)
if len(vs) == 0 {
t.Fatal("expected violation for 'Coming Soon'")
}
if vs[0].file != "hero.tsx" {
t.Fatalf("expected file 'hero.tsx', got %q", vs[0].file)
}
}
func TestCheckComingSoon_UnderConstruction_Flagged(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "page.tsx", `export function Page() {
return Under Construction
}
`)
vs := checkComingSoon(dir)
if len(vs) == 0 {
t.Fatal("expected violation for 'Under Construction'")
}
}
func TestCheckComingSoon_WorkInProgress_Flagged(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "dash.tsx", `export function Dash() {
return Work in Progress
}
`)
vs := checkComingSoon(dir)
if len(vs) == 0 {
t.Fatal("expected violation for 'Work in Progress'")
}
}
func TestCheckComingSoon_NotYetAvailable_Flagged(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "feature.tsx", `export function Feature() {
return Not yet available
}
`)
vs := checkComingSoon(dir)
if len(vs) == 0 {
t.Fatal("expected violation for 'Not yet available'")
}
}
func TestCheckComingSoon_AvailableSoon_Flagged(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "feature.tsx", `export function Feature() {
return Available soon
}
`)
vs := checkComingSoon(dir)
if len(vs) == 0 {
t.Fatal("expected violation for 'Available soon'")
}
}
func TestCheckComingSoon_ComingLater_Flagged(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "feature.tsx", `export function Feature() {
return Coming later
}
`)
vs := checkComingSoon(dir)
if len(vs) == 0 {
t.Fatal("expected violation for 'Coming later'")
}
}
func TestCheckComingSoon_CaseInsensitive(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "feature.tsx", `export function Feature() {
return COMING SOON
}
`)
vs := checkComingSoon(dir)
if len(vs) == 0 {
t.Fatal("expected case-insensitive match for 'COMING SOON'")
}
}
func TestCheckComingSoon_CommentLine_NotFlagged(t *testing.T) {
// isCommentLine() skips lines starting with //, *, /*
dir := t.TempDir()
writeFile(t, dir, "feature.tsx", `// TODO: coming soon section
/* coming soon banner */
export function Feature() {
return Real content
}
`)
vs := checkComingSoon(dir)
if len(vs) != 0 {
t.Fatalf("expected comment lines to be skipped, got %d violations: %#v", len(vs), vs)
}
}
func TestCheckComingSoon_GoFile_Flagged(t *testing.T) {
// checkComingSoon uses todoFileExts which includes .go
dir := t.TempDir()
writeFile(t, dir, "handler.go", `package handler
const pageTitle = "Coming Soon"
`)
vs := checkComingSoon(dir)
if len(vs) == 0 {
t.Fatal("expected violation for 'Coming Soon' in .go file")
}
}
func TestCheckComingSoon_TestTSXFile_Skipped(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "comp.test.tsx", `// test: coming soon banner
describe("coming soon", () => {})
`)
vs := checkComingSoon(dir)
if len(vs) != 0 {
t.Fatalf("expected .test.tsx to be skipped, got %d violations", len(vs))
}
}
func TestCheckComingSoon_TestTSFile_Skipped(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "page.test.ts", `describe("coming soon", () => {})
`)
vs := checkComingSoon(dir)
if len(vs) != 0 {
t.Fatalf("expected .test.ts to be skipped, got %d violations", len(vs))
}
}
func TestCheckComingSoon_NonCodeFile_Skipped(t *testing.T) {
// .md not in todoFileExts
dir := t.TempDir()
writeFile(t, dir, "ROADMAP.md", `## Coming Soon
- Feature A
`)
vs := checkComingSoon(dir)
if len(vs) != 0 {
t.Fatalf("expected .md file to be skipped, got %d violations", len(vs))
}
}
func TestCheckComingSoon_AllowedFile_NotFlagged(t *testing.T) {
// comingSoonAllowedFiles contains specific web/src relative paths;
// checkComingSoon strips to relative from webSrcDir — so the file's relPath
// must match the allowlist key exactly.
dir := t.TempDir()
// "components/pages/system-pages-list.tsx" is an allowed file
allowedDir := filepath.Join(dir, "components", "pages")
writeTestFile(t, filepath.Join(allowedDir, "system-pages-list.tsx"), `export function SystemPages() {
return Coming Soon
}
`, 0644)
vs := checkComingSoon(dir)
if len(vs) != 0 {
t.Fatalf("expected allowed file to be exempt, got %d violations: %#v", len(vs), vs)
}
}
func TestCheckComingSoon_NodeModules_Skipped(t *testing.T) {
dir := t.TempDir()
nmDir := filepath.Join(dir, "node_modules", "some-lib")
writeTestFile(t, filepath.Join(nmDir, "index.ts"), `export const msg = "Coming Soon"
`, 0644)
vs := checkComingSoon(dir)
if len(vs) != 0 {
t.Fatalf("expected node_modules to be skipped, got %d violations", len(vs))
}
}
func TestCheckComingSoon_SortedByFileAndLine(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "z.tsx", `export function Z() {
return Coming Soon
}
`)
writeFile(t, dir, "a.tsx", `export function A() {
return Available soon
}
`)
vs := checkComingSoon(dir)
if len(vs) < 2 {
t.Fatalf("expected at least 2 violations, got %d", len(vs))
}
if vs[0].file > vs[1].file {
t.Fatalf("violations should be sorted by file alphabetically, got %q then %q", vs[0].file, vs[1].file)
}
}
// ─── checkPlaceholderLanguage ─────────────────────────────────────────────
func scanRoots(root string) []todoScanRoot {
return []todoScanRoot{{root: root}}
}
func TestCheckPlaceholderLanguage_StringLiteralWithPlaceholder_Flagged(t *testing.T) {
dir := t.TempDir()
// A string literal containing lowercase "placeholder" as meaningful content (not an attribute).
// Note: rePlaceholderStringLiteral is case-sensitive — "placeholder" (lowercase) matches;
// "Placeholder" (initial cap) does NOT match.
writeFile(t, dir, "comp.tsx", `export function Comp() {
const label = "placeholder content here"
return {label}
}
`)
vs := checkPlaceholderLanguage(scanRoots(dir))
if len(vs) == 0 {
t.Fatalf("expected placeholder language violation for lowercase 'placeholder' string literal, got none")
}
}
func TestCheckPlaceholderLanguage_HTMLAttribute_NotFlagged(t *testing.T) {
// placeholder= is an HTML attribute — explicitly excluded
dir := t.TempDir()
writeFile(t, dir, "input.tsx", `export function Input() {
return
}
`)
vs := checkPlaceholderLanguage(scanRoots(dir))
if len(vs) != 0 {
t.Fatalf("expected HTML placeholder= attribute to be skipped, got %d violations: %#v", len(vs), vs)
}
}
func TestCheckPlaceholderLanguage_GoFile_Flagged(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "handler.go", `package handler
// This is a "Placeholder feature"
func PlaceholderHandler() {}
`)
vs := checkPlaceholderLanguage(scanRoots(dir))
if len(vs) == 0 {
t.Fatalf("expected placeholder violation in .go file")
}
}
func TestCheckPlaceholderLanguage_TestGoFile_Skipped(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "handler_test.go", `package handler
// "Placeholder content"
func TestFoo(t *testing.T) {}
`)
vs := checkPlaceholderLanguage(scanRoots(dir))
if len(vs) != 0 {
t.Fatalf("expected _test.go to be skipped, got %d violations", len(vs))
}
}
func TestCheckPlaceholderLanguage_NonGoTSXFile_Skipped(t *testing.T) {
// checkPlaceholderLanguage only scans .go and .tsx
dir := t.TempDir()
writeFile(t, dir, "content.html", `"Placeholder content"
`)
vs := checkPlaceholderLanguage(scanRoots(dir))
if len(vs) != 0 {
t.Fatalf("expected .html file to be skipped by checkPlaceholderLanguage, got %d violations", len(vs))
}
}
func TestCheckPlaceholderLanguage_EmptyRoot_Skipped(t *testing.T) {
vs := checkPlaceholderLanguage([]todoScanRoot{{root: ""}})
if len(vs) != 0 {
t.Fatalf("expected no violations for empty root, got %d", len(vs))
}
}
func TestCheckPlaceholderLanguage_LiteralJustPlaceholder_NotFlagged(t *testing.T) {
// The word "placeholder" alone (without additional content) is excluded
dir := t.TempDir()
writeFile(t, dir, "comp.tsx", `export function Comp() {
const type = "placeholder"
return
}
`)
vs := checkPlaceholderLanguage(scanRoots(dir))
if len(vs) != 0 {
t.Fatalf("bare 'placeholder' string should not be flagged, got %d violations: %#v", len(vs), vs)
}
}
func TestCheckPlaceholderLanguage_PropertyAccess_NotFlagged(t *testing.T) {
// .placeholder (CSS property access) should not be flagged
dir := t.TempDir()
writeFile(t, dir, "styles.tsx", `const styles = {
color: input.placeholder,
}
`)
vs := checkPlaceholderLanguage(scanRoots(dir))
if len(vs) != 0 {
t.Fatalf("expected .placeholder property access to be skipped, got %d violations: %#v", len(vs), vs)
}
}
func TestCheckPlaceholderLanguage_DisplayPrefix(t *testing.T) {
dir := t.TempDir()
// rePlaceholderStringLiteral is case-sensitive — use lowercase "placeholder"
writeFile(t, dir, "comp.tsx", `const x = "placeholder content here"
`)
vs := checkPlaceholderLanguage([]todoScanRoot{{root: dir, displayPrefix: "web/src"}})
if len(vs) == 0 {
t.Fatal("expected violation")
}
if vs[0].file != "web/src/comp.tsx" {
t.Fatalf("expected prefixed path 'web/src/comp.tsx', got %q", vs[0].file)
}
}
func TestCheckPlaceholderLanguage_TestTSXFile_Skipped(t *testing.T) {
dir := t.TempDir()
writeFile(t, dir, "comp.test.tsx", `// "Placeholder content"
describe("test", () => {})
`)
vs := checkPlaceholderLanguage(scanRoots(dir))
if len(vs) != 0 {
t.Fatalf("expected .test.tsx to be skipped, got %d violations", len(vs))
}
}