or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions.mdactors.mdgraph-utilities.mdguards.mdindex.mdstate-machines.md

graph-utilities.mddocs/

0

# Graph Utilities

1

2

Testing, visualization, and analysis tools for state machines including model-based testing, path generation, and graph analysis. These utilities enable comprehensive testing strategies and provide insights into state machine behavior.

3

4

## Capabilities

5

6

### Model-Based Testing

7

8

Comprehensive testing framework for state machines using model-based testing approaches.

9

10

```typescript { .api }

11

/**

12

* Creates a test model for model-based testing of state machines

13

* @param logic - Actor logic (typically a state machine) to create test model from

14

* @param options - Optional configuration for test model behavior

15

* @returns TestModel instance with path generation and testing capabilities

16

*/

17

function createTestModel<TLogic extends AnyActorLogic>(

18

logic: TLogic,

19

options?: TestModelOptions

20

): TestModel<TLogic>;

21

22

class TestModel<TLogic extends AnyActorLogic> {

23

/** Actor logic being tested */

24

readonly logic: TLogic;

25

/** Test model options */

26

readonly options: TestModelOptions;

27

28

/**

29

* Generate paths using a specified path generator

30

* @param pathGenerator - Function that generates paths from the logic

31

* @param options - Optional traversal configuration

32

* @returns Array of test paths

33

*/

34

getPaths(

35

pathGenerator: PathGenerator<TLogic>,

36

options?: TraversalOptions

37

): TestPath[];

38

39

/**

40

* Get shortest paths to all reachable states

41

* @param options - Optional traversal configuration

42

* @returns Array of shortest paths to each state

43

*/

44

getShortestPaths(options?: TraversalOptions): TestPath[];

45

46

/**

47

* Get shortest paths from specified starting paths

48

* @param paths - Starting paths to extend

49

* @param options - Optional traversal configuration

50

* @returns Extended shortest paths

51

*/

52

getShortestPathsFrom(paths: TestPath[], options?: TraversalOptions): TestPath[];

53

54

/**

55

* Get simple paths (without cycles) to all reachable states

56

* @param options - Optional traversal configuration

57

* @returns Array of simple paths to each state

58

*/

59

getSimplePaths(options?: TraversalOptions): TestPath[];

60

61

/**

62

* Get simple paths from specified starting paths

63

* @param paths - Starting paths to extend

64

* @param options - Optional traversal configuration

65

* @returns Extended simple paths

66

*/

67

getSimplePathsFrom(paths: TestPath[], options?: TraversalOptions): TestPath[];

68

69

/**

70

* Generate paths from a sequence of events

71

* @param events - Array of events to process

72

* @param options - Optional traversal configuration

73

* @returns Generated paths from event sequence

74

*/

75

getPathsFromEvents(events: EventFromLogic<TLogic>[], options?: TraversalOptions): TestPath[];

76

77

/**

78

* Get adjacency map representation of the state machine

79

* @returns Adjacency map showing all state transitions

80

*/

81

getAdjacencyMap(): AdjacencyMap;

82

83

/**

84

* Execute and test a specific path

85

* @param path - Path to test

86

* @param params - Test parameters with executors

87

* @param options - Optional test configuration

88

*/

89

testPath(path: TestPath, params: TestParam, options?: TestOptions): void;

90

91

/**

92

* Test a specific state

93

* @param params - Test parameters

94

* @param state - State to test

95

* @param options - Optional test configuration

96

*/

97

testState(params: TestParam, state: any, options?: TestOptions): void;

98

99

/**

100

* Test a specific transition step

101

* @param params - Test parameters

102

* @param step - Transition step to test

103

*/

104

testTransition(params: TestParam, step: Step): void;

105

}

106

107

interface TestModelOptions {

108

/** Custom state serializer */

109

serializeState?: (state: any) => string;

110

/** Custom event serializer */

111

serializeEvent?: (event: any) => string;

112

/** Maximum number of events to process */

113

events?: { [eventType: string]: Array<{ type: string; [key: string]: any }> };

114

}

115

```

116

117

**Usage Examples:**

118

119

```typescript

120

import { createTestModel } from "xstate/graph";

121

122

// Create test model from machine

123

const testModel = createTestModel(toggleMachine);

124

125

// Generate shortest paths to all states

126

const paths = testModel.getShortestPaths();

127

128

// Test each path

129

paths.forEach(path => {

130

testModel.testPath(path, {

131

states: {

132

// Test state assertions

133

inactive: (state) => {

134

expect(state.value).toBe("inactive");

135

expect(state.context.count).toBeGreaterThanOrEqual(0);

136

},

137

active: (state) => {

138

expect(state.value).toBe("active");

139

}

140

},

141

events: {

142

// Test event execution

143

TOGGLE: () => {

144

// Simulate user clicking toggle button

145

fireEvent.click(toggleButton);

146

}

147

}

148

});

149

});

150

```

151

152

### Path Generation

153

154

Algorithms for generating different types of paths through state machines.

155

156

```typescript { .api }

157

/**

158

* Generates shortest paths to all reachable states using Dijkstra's algorithm

159

* @param logic - Actor logic to analyze

160

* @param options - Optional traversal configuration

161

* @returns Array of shortest paths to each reachable state

162

*/

163

function getShortestPaths<TLogic extends AnyActorLogic>(

164

logic: TLogic,

165

options?: TraversalOptions

166

): StatePath[];

167

168

/**

169

* Generates simple paths (without cycles) to all reachable states

170

* @param logic - Actor logic to analyze

171

* @param options - Optional traversal configuration

172

* @returns Array of simple paths to each reachable state

173

*/

174

function getSimplePaths<TLogic extends AnyActorLogic>(

175

logic: TLogic,

176

options?: TraversalOptions

177

): StatePath[];

178

179

/**

180

* Generates paths by executing a sequence of events

181

* @param logic - Actor logic to execute events on

182

* @param events - Array of events to process in sequence

183

* @param options - Optional traversal configuration

184

* @returns Generated state paths from event sequence

185

*/

186

function getPathsFromEvents<TLogic extends AnyActorLogic>(

187

logic: TLogic,

188

events: EventFromLogic<TLogic>[],

189

options?: TraversalOptions

190

): StatePath[];

191

192

/**

193

* Creates a path generator that produces shortest paths

194

* @returns PathGenerator function for shortest paths

195

*/

196

function createShortestPathsGen<TLogic extends AnyActorLogic>(): PathGenerator<TLogic>;

197

198

/**

199

* Creates a path generator that produces simple paths (no cycles)

200

* @returns PathGenerator function for simple paths

201

*/

202

function createSimplePathsGen<TLogic extends AnyActorLogic>(): PathGenerator<TLogic>;

203

204

type PathGenerator<TLogic extends AnyActorLogic> = (

205

logic: TLogic,

206

options?: TraversalOptions

207

) => StatePath[];

208

```

209

210

### Graph Analysis

211

212

Functions for analyzing and visualizing state machine structure.

213

214

```typescript { .api }

215

/**

216

* Converts a state machine or state node to a directed graph representation

217

* @param stateMachine - State machine or state node to convert

218

* @returns Directed graph representation with nodes and edges

219

*/

220

function toDirectedGraph(stateMachine: AnyStateMachine | AnyStateNode): DirectedGraph;

221

222

/**

223

* Gets all state nodes recursively from a state machine or state node

224

* @param stateNode - State machine or state node to traverse

225

* @returns Array of all state nodes in the hierarchy

226

*/

227

function getStateNodes(stateNode: AnyStateMachine | AnyStateNode): AnyStateNode[];

228

229

/**

230

* Creates an adjacency map representation of state transitions

231

* @param logic - Actor logic to analyze

232

* @param options - Optional traversal configuration

233

* @returns Adjacency map showing all possible transitions

234

*/

235

function getAdjacencyMap<TLogic extends AnyActorLogic>(

236

logic: TLogic,

237

options?: TraversalOptions

238

): AdjacencyMap;

239

240

/**

241

* Converts an adjacency map to an array of state-event-nextState tuples

242

* @param adjMap - Adjacency map to convert

243

* @returns Array of transition tuples

244

*/

245

function adjacencyMapToArray(adjMap: AdjacencyMap): Array<{

246

state: SerializedSnapshot;

247

event: SerializedEvent;

248

nextState: SerializedSnapshot;

249

}>;

250

```

251

252

### Path Operations

253

254

Utilities for working with and manipulating state paths.

255

256

```typescript { .api }

257

/**

258

* Joins two state paths together, combining their steps

259

* @param headPath - First path (beginning portion)

260

* @param tailPath - Second path (ending portion)

261

* @returns Combined path with steps from both input paths

262

*/

263

function joinPaths(headPath: StatePath, tailPath: StatePath): StatePath;

264

265

/**

266

* Serializes a snapshot to a string representation

267

* @param snapshot - Snapshot to serialize

268

* @returns String representation containing value and context

269

*/

270

function serializeSnapshot(snapshot: AnyMachineSnapshot): SerializedSnapshot;

271

```

272

273

## Type Definitions

274

275

### Path and Step Types

276

277

```typescript { .api }

278

interface StatePath {

279

/** Final state reached by this path */

280

state: any;

281

/** Array of steps taken to reach the final state */

282

steps: Step[];

283

/** Total weight/cost of the path */

284

weight: number;

285

}

286

287

interface Step {

288

/** Event that triggered this step */

289

event: EventObject;

290

/** Resulting state after processing the event */

291

state: any;

292

}

293

294

interface TestPath extends StatePath {

295

/** Human-readable description of the path */

296

description: string;

297

/** Test-specific metadata */

298

test?: (state: any) => void;

299

}

300

```

301

302

### Testing Configuration

303

304

```typescript { .api }

305

interface TestParam {

306

/** State testing functions */

307

states?: {

308

[stateKey: string]: (state: any, step?: Step) => void;

309

};

310

/** Event execution functions */

311

events?: {

312

[eventType: string]: (step: Step) => void | Promise<void>;

313

};

314

}

315

316

interface TestOptions {

317

/** Timeout for test execution */

318

timeout?: number;

319

/** Skip specific states */

320

skip?: string[];

321

/** Only test specific states */

322

only?: string[];

323

}

324

```

325

326

### Graph Structure Types

327

328

```typescript { .api }

329

interface DirectedGraph {

330

/** Graph identifier */

331

id: string;

332

/** Root nodes of the graph */

333

children: DirectedGraphNode[];

334

/** All edges in the graph */

335

edges: DirectedGraphEdge[];

336

}

337

338

interface DirectedGraphNode {

339

/** Node identifier */

340

id: string;

341

/** Node type */

342

type: string;

343

/** Child nodes */

344

children: DirectedGraphNode[];

345

/** Outgoing edges */

346

edges: DirectedGraphEdge[];

347

/** Node metadata */

348

data?: any;

349

}

350

351

interface DirectedGraphEdge {

352

/** Source node ID */

353

source: string;

354

/** Target node ID */

355

target: string;

356

/** Edge label (typically event type) */

357

label: string;

358

/** Transition data */

359

data?: any;

360

}

361

```

362

363

### Adjacency and Traversal Types

364

365

```typescript { .api }

366

interface AdjacencyMap {

367

[serializedState: string]: AdjacencyValue;

368

}

369

370

interface AdjacencyValue {

371

/** The state snapshot */

372

state: any;

373

/** Possible transitions from this state */

374

transitions: {

375

[eventType: string]: {

376

/** Target state after transition */

377

state: any;

378

/** Transition event */

379

event: EventObject;

380

/** Actions executed during transition */

381

actions: any[];

382

};

383

};

384

}

385

386

interface TraversalOptions {

387

/** Custom state serializer function */

388

serializeState?: (state: any) => string;

389

/** Custom event serializer function */

390

serializeEvent?: (event: EventObject) => string;

391

/** Maximum depth to traverse */

392

depth?: number;

393

/** Filter function for events */

394

filter?: (state: any) => boolean;

395

/** Maximum number of paths to generate */

396

limit?: number;

397

/** Events to explore at each state */

398

events?: EventObject[];

399

}

400

401

type SerializedSnapshot = string & { _brand: "SerializedSnapshot" };

402

type SerializedEvent = string & { _brand: "SerializedEvent" };

403

```

404

405

## Advanced Testing Patterns

406

407

### Integration Testing

408

409

```typescript

410

import { createTestModel } from "xstate/graph";

411

import { render, fireEvent } from "@testing-library/react";

412

413

const testModel = createTestModel(formMachine);

414

415

describe("Form Machine Integration", () => {

416

testModel.getShortestPaths().forEach(path => {

417

it(`should handle path: ${path.description}`, async () => {

418

const { getByRole, getByLabelText } = render(<FormComponent />);

419

420

testModel.testPath(path, {

421

states: {

422

editing: (state) => {

423

expect(getByRole("form")).toBeInTheDocument();

424

expect(state.context.isEditing).toBe(true);

425

},

426

validating: (state) => {

427

expect(getByRole("status")).toHaveTextContent("Validating...");

428

},

429

success: (state) => {

430

expect(getByRole("status")).toHaveTextContent("Success!");

431

}

432

},

433

events: {

434

INPUT_CHANGE: (step) => {

435

const input = getByLabelText("Email");

436

fireEvent.change(input, { target: { value: step.event.value } });

437

},

438

SUBMIT: () => {

439

fireEvent.click(getByRole("button", { name: "Submit" }));

440

}

441

}

442

});

443

});

444

});

445

});

446

```

447

448

### Coverage Analysis

449

450

```typescript

451

// Generate comprehensive test coverage

452

const testModel = createTestModel(complexMachine);

453

454

// Test all simple paths for full coverage

455

const simplePaths = testModel.getSimplePaths();

456

console.log(`Testing ${simplePaths.length} paths for full coverage`);

457

458

// Analyze adjacency for missing transitions

459

const adjacency = testModel.getAdjacencyMap();

460

const coverage = adjacencyMapToArray(adjacency);

461

console.log(`Found ${coverage.length} possible transitions`);

462

```

463

464

### Performance Testing

465

466

```typescript

467

// Test path performance

468

const performanceModel = createTestModel(heavyMachine);

469

470

const shortestPaths = performanceModel.getShortestPaths({

471

limit: 100,

472

depth: 10

473

});

474

475

shortestPaths.forEach(path => {

476

const startTime = performance.now();

477

performanceModel.testPath(path, testParams);

478

const duration = performance.now() - startTime;

479

480

if (duration > 1000) {

481

console.warn(`Slow path detected: ${path.description} (${duration}ms)`);

482

}

483

});

484

```