# `testing.FakeSessionOptions.profile`

> 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.FakeSessionOptions`](testing.FakeSessionOptions.md) · stable · since 2.0.0

```ts
profile?: {
  unsupported?: readonly Capability[];
  permits?: readonly string[];
  tier?: InfoReport['renderer']['tier'];
  linkedLights?: number;
  maxTextureSize?: number;
  arModes?: InfoReport['ar']['modes'];
  arPending?: boolean;
  arEscapeOnly?: boolean;
  arEnabled?: boolean;
  tryOn?: {
    kinds: readonly TryOnKind[];
    blocked?: 'switched-off' | 'unsupported';
    outcome?: 'launched' | 'camera-denied' | 'needs-tap' | 'error' | 'other';
    durationMs?: number;
  };
  problems?: ReadonlyArray<Omit<Problem, 'at' | 'docs'>>;
  session?: {
    accountId?: number;
    configuratorId?: string | null;
  };
};
```

The runtime, device and account the fake answers for (spec section 9.4). Unset, it is a
full-featured desktop: [`supports()`](Session.supports.md), [`has()`](has.md) true for each KnownCapability the fake builds (a
member still to come, and an option the runtime takes its fallback for: false, as on the
runtime), [`permits()`](Session.permits.md)
false, tier 'high', 'ar:modes'
answered. [`supports()`](Session.supports.md), [`has()`](has.md), [`permits()`](Session.permits.md), the gated operations and [`info()`](Session.info.md) read it alike;
`info().ar.enabled` and [`info().document`](InfoReport.document.md) follow the applied documents, as in the runtime.

**Default:** {}

## Fields

<a id="unsupported"></a>
### `unsupported`

```ts
unsupported?: readonly Capability[];
```

Ids this runtime lacks: [`supports()`](Session.supports.md) and [`has()`](has.md) answer false for them.

**Default:** []

<a id="permits"></a>
### `permits`

```ts
permits?: readonly string[];
```

Gated capabilities this account may use ([`permits()`](Session.permits.md)).

**Default:** []

<a id="tier"></a>
### `tier`

```ts
tier?: InfoReport['renderer']['tier'];
```

`info().renderer.tier`.

**Default:** 'high'

<a id="linkedlights"></a>
### `linkedLights`

```ts
linkedLights?: number;
```

`info().renderer.limits.linkedLights`.

**Default:** the runtime's floor for every tier (spec section 5.15)

<a id="maxtexturesize"></a>
### `maxTextureSize`

```ts
maxTextureSize?: number;
```

`info().device.maxTextureSize`, px.

**Default:** 4096

<a id="armodes"></a>
### `arModes`

```ts
arModes?: InfoReport['ar']['modes'];
```

The device's AR modes at boot, as the runtime reads them: `info().ar.fileModes` is its
native part, and `info().ar.modes` lists it without 'scene-viewer' while the committed
product is encrypted with no public plain twin, as the runtime's (a call with an http(s)
`glbUrl` opens Scene Viewer where this lists it).

**Default:** []

<a id="arpending"></a>
### `arPending`

```ts
arPending?: boolean;
```

`info().ar.pending` at boot; `setArModes` answers it.

**Default:** false

<a id="arescapeonly"></a>
### `arEscapeOnly`

```ts
arEscapeOnly?: boolean;
```

The in-page route is the escape sheet only (an app shell not yet measured, a host's deny
row; DL50): `info().ar.modes` lists `'webxr'` as set in `arModes`, and [`activateAR()`](Session.activateAR.md) opens
the sheet, resolving `{ launched: false, reason: 'unsupported' }` with no 'ar:status'
(the sheet is the message), `'webxr'` still listed, as on the runtime.

**Default:** false

<a id="arenabled"></a>
### `arEnabled`

```ts
arEnabled?: boolean;
```

`info().ar.enabled`: false is an account (or document) with AR off. `info().ar.modes` is
then empty and [`activateAR()`](Session.activateAR.md) resolves `{ launched: false, reason: 'disabled' }` with
'ar:status' 'disabled', as in the runtime.

**Default:** true

<a id="tryon"></a>
### `tryOn`

```ts
tryOn?: { … }
```

The account's try-on, so a wearable page tests its Try-on control. Unset, the account
offers none: [`canTryOn()`](Session.canTryOn.md) answers `{ available: false, kind: 'other', modes: [], reason:
'not-enabled' }` and `tryOn()` resolves 'unavailable'. Set, `canTryOn()` follows the
committed product's rig as the runtime does, re-read after each commit: the first
enabled ModelNode with a stored `tryOn` record (a GroupNode's active child, by
`defaultNodeId`) gives `kind`; a kind `kinds` lacks answers 'not-enabled', then `blocked`,
then a kind with no tracker in this runtime (every kind but 'face' today) 'unsupported',
then a node without a `tryOnUrl` and an approved rig of a format the runtime reads
(`tryOn.approved.format` 1), or no such node, 'no-rig'; else `{ available: true, kind,
modes: ['here', 'handoff'] }` (`blocked: 'unsupported'` with such a rig lists
`['handoff']`). [`tryOn()`](Session.tryOn.md) then resolves as the runtime's: 'unavailable' (mode 'other';
'unsupported' where [`canTryOn()`](Session.canTryOn.md) says 'unsupported') when it can't open, 'unsupported' for
`ui: 'headless'`; `mode: 'handoff'` opens where `modes` lists it and resolves `{ launched:
true, mode: 'handoff', handoffUrl }` after `durationMs` whatever `outcome` says (no camera
opens here), its link `returnUrl` read against the page's address ('unsupported' with
neither, as in Node); else `outcome` after `durationMs`; a call while one is open returns
its Promise, [`endAR()`](Session.endAR.md) closes it at once (`durationMs` the time it ran; an unlaunched
`outcome` closes as 'other'), and an aborted `signal` rejects AbortError. No 'xr:start' or
'xr:end' fires (no camera opens). The profile's example runs a Try-on control.

**Default:** undefined (no try-on)

<a id="tryon.kinds"></a>
### `tryOn.kinds`

```ts
kinds: readonly TryOnKind[];
```

The kinds the account offers, its consent mode set.

<a id="tryon.blocked"></a>
### `tryOn.blocked`

```ts
blocked?: 'switched-off' | 'unsupported';
```

'switched-off' (Holostep switched try-on off) or 'unsupported' (this device: no camera, or the page blocks it).

**Default:** undefined (neither)

<a id="tryon.outcome"></a>
### `tryOn.outcome`

```ts
outcome?: 'launched' | 'camera-denied' | 'needs-tap' | 'error' | 'other';
```

How a try-on that opens ends: 'launched' (the camera picture showed), else the reason it closed without it.

**Default:** 'launched'

<a id="tryon.durationms"></a>
### `tryOn.durationMs`

```ts
durationMs?: number;
```

Fake-clock ms from the call to the close ('needs-tap' answers at once).

**Default:** 0

<a id="problems"></a>
### `problems`

```ts
problems?: ReadonlyArray<Omit<Problem, 'at' | 'docs'>>;
```

Problems [`boot`](boot.md) would record before it resolves, such as 'tenant-document-unavailable'
(spec section 5.1): in `problems()` and [`info().problems`](InfoReport.problems.md) from the start, and dispatched to
no handler, as the runtime's are.

**Default:** []

<a id="session"></a>
### `session`

```ts
session?: { … }
```

[`info().session`](InfoReport.session.md)'s ids.

**Default:** {}

<a id="session.accountid"></a>
### `session.accountId`

```ts
accountId?: number;
```

`info().session.accountId`.

**Default:** 0

<a id="session.configuratorid"></a>
### `session.configuratorId`

```ts
configuratorId?: string | null;
```

`info().session.configuratorId`.

**Default:** null

## 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-FakeSessionOptions.profile.ts`):

```ts
import { createFakeSession } from '@holostep-configurators/runtime-client/testing';
import type { IModelNode, Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
// A glasses page shows its Try-on button only where try-on can open, and tells the
// shopper when the camera was refused.
declare const aviator: IModelNode;  // glbUrl 'glasses.glb', an approved 'face' rig (tryOn, tryOnUrl)
const fake = createFakeSession({
  models: { 'glasses.glb': { nodes: [{ name: 'Frame', mesh: true }] } },
  root: [aviator],
  profile: { tryOn: { kinds: ['face'], outcome: 'camera-denied', durationMs: 400 } },
});
let loaded = false;
void fake.idle().then(() => (loaded = true));
for (let i = 0; i < 100 && !loaded; i++) {
  fake.tick(16);
  await new Promise((resolve) => setTimeout(resolve, 0));
}
export const button = { shown: fake.canTryOn?.().available === true };
const closed = fake.tryOn?.();
fake.tick(400);                                          // the shopper refuses the camera
export const report = await closed;
if (!button.shown || report?.reason !== 'camera-denied') {
  throw new Error('the Try-on control did not follow the account and the product');
}
```

## Generic-API test (spec 3.1)

| Rule | Result |
| --- | --- |
| 1. Neutral name | pass: `profile` 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) |
