package main import ( "os" "path/filepath" "testing" ) func TestCheckPluginPagesFlagsQueryClientProvider(t *testing.T) { dir := t.TempDir() file := filepath.Join(dir, "editor.tsx") if err := os.WriteFile(file, []byte(`import { QueryClientProvider } from '@tanstack/react-query' export function Editor() { return
} `), 0644); err != nil { t.Fatalf("write editor.tsx: %v", err) } violations := checkPluginPages([]string{dir}) found := false for _, v := range violations { if v.rule != "no-queryclientprovider-in-plugin" { continue } if v.snippet != "remove QueryClientProvider; BlockNinja core provides it." { t.Fatalf("snippet = %q, want concise remediation", v.snippet) } found = true break } if !found { t.Fatalf("expected QueryClientProvider violation, got %#v", violations) } } func TestCheckFrontendSkipsTestFiles(t *testing.T) { dir := t.TempDir() // A transport-layer regression test legitimately drives createClient over the // real transport to exercise interceptors — it is not shipped UI. The frontend // pattern checks must skip *.test.* / *.spec.* files, matching the convention // the coming-soon and TODO checks already follow. for _, name := range []string{"transport.test.ts", "auth.test.tsx", "util.spec.ts", "view.spec.tsx"} { if err := os.WriteFile(filepath.Join(dir, name), []byte(`import { createClient } from "@connectrpc/connect"; import axios from "axios"; const client = createClient(AuthService, transport); `), 0644); err != nil { t.Fatalf("write %s: %v", name, err) } } violations, _ := checkFrontend(dir) if len(violations) != 0 { t.Fatalf("expected test/spec files to be skipped, got %d violation(s): %#v", len(violations), violations) } } func TestIsBrowserConfirmCall(t *testing.T) { tests := []struct { lines []string idx int want bool }{ {lines: []string{`if (window.confirm("delete?")) {`}, idx: 0, want: true}, {lines: []string{`if (confirm("delete?")) {`}, idx: 0, want: true}, {lines: []string{`const confirmed = await confirm({ title: "Delete" })`}, idx: 0, want: false}, {lines: []string{`confirm({ title: "Delete" }).then((confirmed) => {`}, idx: 0, want: false}, { lines: []string{ `const confirmed = await`, ` confirm({`, ` title: "Delete",`, ` })`, }, idx: 1, want: false, }, { lines: []string{ `confirm({`, ` title: "Leave",`, `}).then((confirmed) => {`, }, idx: 0, want: false, }, } for _, tt := range tests { if got := isBrowserConfirmCall(tt.lines, tt.idx); got != tt.want { t.Fatalf("isBrowserConfirmCall(%#v, %d) = %v, want %v", tt.lines, tt.idx, got, tt.want) } } } func TestPluginRESTFileAllowed(t *testing.T) { if !pluginRESTFileAllowed("/tmp/repo/backend/internal/plugins/calcomblock/web/settings.tsx") { t.Fatalf("expected documented calcom settings panel to be allowlisted") } if !pluginRESTFileAllowed("/tmp/repo/backend/internal/plugins/calcomblock/web/editor.tsx") { t.Fatalf("expected documented calcom editor panel to be allowlisted") } if pluginRESTFileAllowed("/tmp/repo/backend/internal/plugins/calcomblock/web/dashboard.tsx") { t.Fatalf("unexpected allowlist hit for non-allowlisted plugin file") } }