@@ -1242,4 +1242,101 @@ describe('validateReactPageProps — unprovisioned injected anchors (#8340)', ()
12421242 ) ;
12431243 expect ( f ) . toEqual ( [ ] ) ;
12441244 } ) ;
1245+
1246+ // ───────────────────────────────────────────────────────────────────────
1247+ // [#8943] The four `checkBlockFieldProps` calls that were threaded by
1248+ // CONVENTION ONLY.
1249+ //
1250+ // `checkFieldRefs` takes the anchor index as an OPTIONAL trailing
1251+ // parameter, so a call site that stops passing it keeps compiling and
1252+ // silently downgrades to the pre-#8340 behaviour: existence still
1253+ // answered, provenance never asked. Nothing else can see that happen —
1254+ // the parameter being optional means no type error, and the local
1255+ // `unprovisionedAnchors` binding stays READ by the sibling calls in the
1256+ // same function, so there is no TS6133 either. No CI gate reads call
1257+ // sites at all (#8664).
1258+ //
1259+ // #8943 measured all six threading sites by dropping the argument at each
1260+ // in turn. The two module seams were already pinned — `validatePageFieldBindings`
1261+ // and the `queried` bucket, both covered by the FILTER-position test above.
1262+ // The four below stayed GREEN across the whole packages/lint suite with the
1263+ // index dropped. Each test here was then written ablation-first and observed
1264+ // RED under its own site's ablation, which is precisely why it exists.
1265+ //
1266+ // The branches are disjoint by tag and prop, so each test names exactly one
1267+ // site: `own` refs reach `<ListView columns>`; the subform pair needs
1268+ // `<ObjectForm subforms>`; the COMPONENT_FIELD_SPECS table is reachable
1269+ // only through `<Block type>`, since no react tag's own schemaType
1270+ // (`list-view`, `object-form`, `object-chart`) is a key in it.
1271+ // ───────────────────────────────────────────────────────────────────────
1272+
1273+ /** A LOCAL object — platform storage is real, so it registers no unprovisioned anchor. */
1274+ const localOrder = { name : 'crm_order' , fields : [ { name : 'code' } ] } ;
1275+
1276+ it ( '[#8943] R1 — PINS the `own` display-ref call (the `skipped` bucket of REACT_FIELD_SPECS)' , ( ) => {
1277+ // `<ListView columns>` is a DISPLAY binding, not a query: the consequence
1278+ // clause is the blank-column one, not the constant-false one.
1279+ const f = validateReactPageProps (
1280+ extPage ( `function Page(){ return <ListView objectName="ext_customer" columns={['owner_id']} />; }` ) ,
1281+ ) ;
1282+ expect ( f . filter ( ( x ) => x . rule === PAGE_FIELD_UNKNOWN ) ) . toHaveLength ( 0 ) ;
1283+ const warned = f . filter ( ( x ) => x . rule === PAGE_FIELD_UNPROVISIONED ) ;
1284+ expect ( warned ) . toHaveLength ( 1 ) ;
1285+ expect ( warned [ 0 ] . severity ) . toBe ( 'warning' ) ;
1286+ expect ( warned [ 0 ] . path ) . toBe ( 'pages[0].source › columns[0]' ) ;
1287+ expect ( warned [ 0 ] . message ) . toContain ( 'external object (ADR-0015)' ) ;
1288+ expect ( warned [ 0 ] . message ) . toContain ( 'blank, on every record' ) ;
1289+ } ) ;
1290+
1291+ it ( '[#8943] R3 — PINS the ObjectForm subform CHILD refs (resolved against `childObject`)' , ( ) => {
1292+ // The child batch resolves against the subform's own object, so the
1293+ // external one is the CHILD here and the form's object is local.
1294+ const f = validateReactPageProps (
1295+ extPage (
1296+ `function Page(){ return <ObjectForm objectName="crm_order" subforms={[{ childObject: 'ext_customer', columns: ['owner_id'] }]} />; }` ,
1297+ [ extCustomer ( ) , localOrder ] ,
1298+ ) ,
1299+ ) ;
1300+ expect ( f . filter ( ( x ) => x . rule === PAGE_FIELD_UNKNOWN ) ) . toHaveLength ( 0 ) ;
1301+ const warned = f . filter ( ( x ) => x . rule === PAGE_FIELD_UNPROVISIONED ) ;
1302+ expect ( warned ) . toHaveLength ( 1 ) ;
1303+ expect ( warned [ 0 ] . severity ) . toBe ( 'warning' ) ;
1304+ expect ( warned [ 0 ] . path ) . toBe ( 'pages[0].source › subforms[0].columns[0]' ) ;
1305+ expect ( warned [ 0 ] . message ) . toContain ( '"ext_customer"' ) ;
1306+ expect ( warned [ 0 ] . message ) . toContain ( 'blank, on every record' ) ;
1307+ } ) ;
1308+
1309+ it ( '[#8943] R4 — PINS the subform `totalField` rollup (resolved against the FORM object)' , ( ) => {
1310+ // The exception inside the exception: `totalField` names the PARENT
1311+ // object's field the child sum rolls up into, so the external object is
1312+ // the form's own and the child is local.
1313+ const f = validateReactPageProps (
1314+ extPage (
1315+ `function Page(){ return <ObjectForm objectName="ext_customer" subforms={[{ childObject: 'crm_order', columns: ['code'], totalField: 'owner_id' }]} />; }` ,
1316+ [ extCustomer ( ) , localOrder ] ,
1317+ ) ,
1318+ ) ;
1319+ expect ( f . filter ( ( x ) => x . rule === PAGE_FIELD_UNKNOWN ) ) . toHaveLength ( 0 ) ;
1320+ const warned = f . filter ( ( x ) => x . rule === PAGE_FIELD_UNPROVISIONED ) ;
1321+ expect ( warned ) . toHaveLength ( 1 ) ;
1322+ expect ( warned [ 0 ] . severity ) . toBe ( 'warning' ) ;
1323+ expect ( warned [ 0 ] . path ) . toBe ( 'pages[0].source › subforms[0].totalField' ) ;
1324+ expect ( warned [ 0 ] . message ) . toContain ( '"ext_customer"' ) ;
1325+ } ) ;
1326+
1327+ it ( '[#8943] R5 — PINS the COMPONENT_FIELD_SPECS path reached by `<Block type>`' , ( ) => {
1328+ // The shared table `validate-page-field-bindings` walks; on this surface
1329+ // it is reachable only by the type the author spells out, which is what
1330+ // makes the escape hatch checked rather than a hole.
1331+ const f = validateReactPageProps (
1332+ extPage ( `function Page(){ return <Block type="element:form" objectName="ext_customer" fields={['owner_id']} />; }` ) ,
1333+ ) ;
1334+ expect ( f . filter ( ( x ) => x . rule === PAGE_FIELD_UNKNOWN ) ) . toHaveLength ( 0 ) ;
1335+ const warned = f . filter ( ( x ) => x . rule === PAGE_FIELD_UNPROVISIONED ) ;
1336+ expect ( warned ) . toHaveLength ( 1 ) ;
1337+ expect ( warned [ 0 ] . severity ) . toBe ( 'warning' ) ;
1338+ expect ( warned [ 0 ] . where ) . toBe ( 'page "p" › <Block>' ) ;
1339+ expect ( warned [ 0 ] . path ) . toBe ( 'pages[0].source › fields[0]' ) ;
1340+ expect ( warned [ 0 ] . message ) . toContain ( 'external object (ADR-0015)' ) ;
1341+ } ) ;
12451342} ) ;
0 commit comments