Skip to content

UI testing: static check for the grid-gate class, a dev seeder, and the grid pass recorded - #372

Merged
WebTigers merged 1 commit into
mainfrom
feat/tiger-390-paradigm-datatable-check
Oct 25, 2026
Merged

WebTigers merged 1 commit into
mainfrom
feat/tiger-390-paradigm-datatable-check

Conversation

@WebTigers

Copy link
Copy Markdown
Owner

Three things from the same morning's findings. Test-harness only — no shipped code changes.

1. paradigm-scan grows a fifth check — datatable_gate (blocker)

A view whose registered JS calls tigerDataTable() while its owning controller never sets useDataTables. That's the TIGER-390 class: HTTP 200, empty table, zero console errors, because the grid script's bail-out guard is doing its job. Invisible to PHPUnit (nothing renders) and to the browser probe's "no console errors" detector — so it has to be caught statically.

Mutation-verified twice. Removing the flag from system/SitesController, then from newsletter/AdminController, each produces exactly one finding naming the file and the cause; restoring it goes quiet.

== datatable_gate Grid screen never sets useDataTables (lib never loads) BLOCKER (1)
   modules/newsletter/views/scripts/admin/index.phtml:1
      grid JS calls tigerDataTable() but modules/newsletter/controllers/AdminController.php
      never sets $this->view->useDataTables

Scope is stated in the code: the flag is looked for anywhere in the controller, not inside the specific action. A finding therefore means the controller never sets it at all — the case that actually shipped, and zero false positives.

It also made the scan 20× slower, so that's fixed too. Reading a view's registered JS meant a find shell-out per asset per view, and hasAjaxHandler was already doing the same thing. Both now share one basename→path index built once: 29s → 1.4s, faster than before the check existed.

2. tests/ui/bin/seed-dev.php

The probe account's org owned no content, so every grid looked exactly like the three broken ones — and telling them apart took a SQL query a scenario can't run. Seeds one page, one article, one menu, one inactive snippet and one subscriber, all labelled UI Probe, addresses on the reserved .invalid domain. Dry run unless --apply; idempotent.

Its guard failed honestly three times before it worked, which is the part worth reading:

  1. First version read config.json relative to __DIR__, found none when run from a copied path, and so "passed" with an empty never_target — a safety check that could only ever succeed.
  2. Second version compared hosts as substrings, which would refuse tiger-dev.webtigers.com for containing webtigers.com.
  3. Third discovery killed the approach entirely: an install cannot tell you which site it is. On tiger-dev, APPLICATION_ENV is literally production, tiger.site.url is unset, and gethostname() returns ip-172-31-44-189.ec2.internal.

So the operator names the target with --base and never_target refuses the forbidden ones — the contract both probes already use. Five branches, each verified on dev:

Invocation Result
no --base refused
--base=https://tiger.webtigers.com refused
--base=https://www.webtigers.com refused
missing never_target refused (fail closed)
--base=https://tiger-dev.webtigers.com proceeds

3. scenarios/admin-grids.json

The grid pass recorded in the same intent-level shape as auth.json — five scenarios, each with a verified line carrying what the real run saw.

UI-GRID-EMPTY-TRUTH is the one to keep: an empty grid and a broken grid are the same picture, so never record a PASS on an empty grid without establishing why it's empty. That discipline is what surfaced TIGER-391 — /media's honest 0 rows next to /cms/page's 25 is what exposed the missing org filter.


Scan: 108 views, 0 findings on all five checks. Unit suite: 1200 tests, 18318 assertions, green.

🤖 Generated with Claude Code

https://claude.ai/code/session_013vdyTeoDrGE4hKxt53TsE8

… the grid pass recorded

Three things, all from the same morning's findings.

1. paradigm-scan grows a FIFTH check, `datatable_gate` (blocker). A view whose registered JS calls
   tigerDataTable() while its owning controller never sets useDataTables is the TIGER-390 class:
   HTTP 200, empty table, zero console errors, because the grid script's bail-out guard is doing its
   job. Invisible to PHPUnit (nothing renders) and to the browser probe's "no console errors"
   detector, so it has to be caught statically. Mutation-verified twice — removing the flag from
   system/SitesController and then newsletter/AdminController each produces exactly one finding
   naming the file and the cause; restoring it goes quiet. Scope stated in the code: the flag is
   looked for anywhere in the controller, not inside the specific action, so a finding means the
   controller never sets it at all — the case that actually shipped, and zero false positives.

   That check also made the scan 20x SLOWER (29s), because reading a view's registered JS meant a
   `find` shell-out per asset per view — and hasAjaxHandler was already doing the same. Both now
   share one basename->path index built once: 29s -> 1.4s, faster than before the check existed.

2. tests/ui/bin/seed-dev.php — the probe org owned no content, so EVERY grid looked exactly like the
   three broken ones and separating them took a SQL query a scenario cannot run. Seeds one page, one
   article, one menu, one inactive snippet and one subscriber, all labelled `UI Probe`, addresses on
   the reserved .invalid domain. Dry run unless --apply; idempotent.

   Its guard went through three honest failures worth recording. First version read config.json
   relative to __DIR__, found none when run from a copied path, and so "passed" with an empty
   never_target — a check that could only ever succeed. Second version matched hosts as substrings,
   which would refuse tiger-dev.webtigers.com for containing webtigers.com. Third discovery killed
   the whole approach: an install cannot tell you which site it is — on tiger-dev APPLICATION_ENV is
   literally "production", tiger.site.url is unset, and gethostname() returns an EC2 internal name.
   So the operator NAMES the target with --base and never_target refuses the forbidden ones, which is
   the contract the two probes already use. Five branches each verified on dev: no --base refuses,
   tiger.webtigers.com refuses, www.webtigers.com refuses, a missing never_target refuses, and the
   dev base proceeds.

3. scenarios/admin-grids.json — the grid pass recorded in the same intent-level shape as auth.json,
   five scenarios with `verified` lines carrying what the real run saw. UI-GRID-EMPTY-TRUTH is the
   one to keep: an empty grid and a broken grid are the same picture, so never record a PASS on an
   empty grid without establishing why it is empty. That discipline is what surfaced TIGER-391 —
   /media's honest 0 rows next to /cms/page's 25 is what exposed the missing org filter.

Scan: 108 views, 0 findings on all five checks. Unit suite: 1200 tests, 18318 assertions, green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vdyTeoDrGE4hKxt53TsE8
@WebTigers
WebTigers merged commit 0205f8c into main Oct 25, 2026
14 checks passed
@WebTigers
WebTigers deleted the feat/tiger-390-paradigm-datatable-check branch October 25, 2026 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant