# `testing.FakeModel.nodes`

> Contract 2.0.0 · package sha256 `a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e` · generated from `testing.d.ts` by `services/onsite-scripter/scripts/build-contract-docs.mjs`; edit the source, not this file.

Property of [`testing.FakeModel`](testing.FakeModel.md) · stable · since 2.0.0

```ts
nodes: ReadonlyArray<{
  name: string;
  id?: string;
  parent?: string | null;
  mesh?: boolean;
  tags?: string[];
  slot?: string;
  joint?: boolean;
  bounds?: {
    min: {
      x: number;
      y: number;
      z: number;
    };
    max: {
      x: number;
      y: number;
      z: number;
    };
  };
  doubleSided?: boolean;
  uvChannels?: number[];
  material?: Pick<MaterialSpec, 'baseColor' | 'metallic' | 'roughness' | 'alpha' | 'alphaMode' | 'alphaCutoff' | 'side' | 'emissive' | 'emissiveStrength'>;
}>;
```

Declared node tree of a GLB (names, ids, tags, slots, joints, local bounds). `parent` is
another entry's `id`, else its `name`. List a connector plane here too, as a mesh entry named as its
connector (the GLB holds it as a mesh), when a test reads it as a part: the fake then hides
it, never picks it and marks it a connector, as the runtime does. [`connectors`](testing.FakeModel.connectors.md) alone drives
[`assembly`](Writer.assembly.md) (attach, `candidates`, [`connectors`](testing.FakeModel.connectors.md)) and adds no part, so [`find()`](Writer.find.md) doesn't see it.
A mesh entry's `material` declares its GLB material's own values, which [`Part.material`](Part.material.md) reads.

## Example

Imports: values (`has`, `validate`, the `Holo*Error` classes, the pure helpers) from `@holostep-configurators/runtime-client`, types from `@holostep-configurators/runtime-client/contract`, `/math` and `/testing` names from those entries; `boot` from the runtime module the root's `loadRuntimeAtFloor` resolves to. A type your page defines is your app's: declare it there, never import it from the package.

Compiled under the contract's strict settings (`examples/declaration/24-FakeModel.nodes.ts`):

```ts
import { createFakeSession } from '@holostep-configurators/runtime-client/testing';
import type { IModelNode, Session } from '@holostep-configurators/runtime-client/contract';
import type { FakeModel } from '@holostep-configurators/runtime-client/testing';
declare const session: Session;
declare const signal: AbortSignal;
declare const cabinet: IModelNode;              // glbUrl 'cabinet.glb'
const model: FakeModel = {
  nodes: [{ name: 'Body', mesh: true, slot: 'Oak', material: { baseColor: '#ceb390', metallic: 0, roughness: 0.7 } }],
};
export const fake = createFakeSession({ models: { 'cabinet.glb': model }, root: [cabinet] });
// once the root has loaded: { baseColor: '#ceb390', metallic: 0, roughness: 0.7 }, as the runtime reads the GLB
export const glbLook = () => fake.find({ name: 'Body' }).first()?.material?.spec;
```

## Generic-API test (spec 3.1)

| Rule | Result |
| --- | --- |
| 1. Neutral name | pass: `nodes` names no product |
| 2. A precedent | not recorded: no ledger row (coverage.json) names this member or its owner |
| 3. Two apps, or labs | not recorded: no ledger row names this member or its owner |
| 4. The cheaper fix first | not recorded per member (Appendix B decides it per need) |
