or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mdhttp-client.mdhttp-utilities.mdindex.mdrequest-response-mocking.mdtest-doubles.mdtest-sandbox.mdvalidation-helpers.md

test-sandbox.mddocs/

0

# Test Sandbox

1

2

File system utilities for creating isolated test directories and managing test data with automatic cleanup capabilities.

3

4

## Capabilities

5

6

### TestSandbox Class

7

8

Main class for creating and managing isolated test directories.

9

10

```typescript { .api }

11

/**

12

* TestSandbox class provides a convenient way to get a reference to a

13

* sandbox folder in which you can perform operations for testing purposes

14

*/

15

class TestSandbox {

16

/**

17

* Create a new TestSandbox instance

18

* @param rootPath - Root path for the sandbox (resolved against current directory if relative)

19

* @param options - Configuration options for sandbox creation

20

*/

21

constructor(rootPath: string, options?: TestSandboxOptions);

22

23

/**

24

* Get the absolute path to the sandbox directory

25

* @throws Error if the sandbox has been deleted

26

*/

27

get path(): string;

28

29

/**

30

* Reset the TestSandbox by removing all files in it

31

* Also clears require cache for any files in the sandbox path

32

*/

33

reset(): Promise<void>;

34

35

/**

36

* Delete the TestSandbox completely

37

* After deletion, the sandbox instance becomes unusable

38

*/

39

delete(): Promise<void>;

40

41

/**

42

* Create a directory in the TestSandbox

43

* @param dir - Directory path relative to sandbox root

44

*/

45

mkdir(dir: string): Promise<void>;

46

47

/**

48

* Copy a file from source to the TestSandbox

49

* Handles source maps for .js files automatically

50

* @param src - Absolute path of source file

51

* @param dest - Destination filename (relative to sandbox), defaults to original filename

52

* @param transform - Optional function to transform file content

53

*/

54

copyFile(

55

src: string,

56

dest?: string,

57

transform?: (content: string) => string

58

): Promise<void>;

59

60

/**

61

* Create a JSON file in the TestSandbox

62

* @param dest - Destination filename (relative to sandbox)

63

* @param data - Data to serialize as JSON

64

*/

65

writeJsonFile(dest: string, data: unknown): Promise<void>;

66

67

/**

68

* Create a text file in the TestSandbox

69

* @param dest - Destination filename (relative to sandbox)

70

* @param data - Text content to write

71

*/

72

writeTextFile(dest: string, data: string): Promise<void>;

73

}

74

75

/**

76

* Configuration options for TestSandbox

77

*/

78

interface TestSandboxOptions {

79

/**

80

* Controls subdirectory creation:

81

* - true: Creates unique temporary subdirectory (default)

82

* - false: Uses root path directly

83

* - string: Creates named subdirectory

84

*/

85

subdir: boolean | string;

86

}

87

```

88

89

**Usage Examples:**

90

91

```typescript

92

import { TestSandbox, expect } from "@loopback/testlab";

93

import path from "path";

94

import fs from "fs";

95

96

// Basic sandbox usage

97

const sandbox = new TestSandbox("/tmp/my-tests");

98

99

// Create test files

100

await sandbox.writeTextFile("config.txt", "debug=true\nport=3000");

101

await sandbox.writeJsonFile("package.json", {

102

name: "test-app",

103

version: "1.0.0"

104

});

105

106

// Create directories

107

await sandbox.mkdir("src");

108

await sandbox.mkdir("src/controllers");

109

110

// Work with files

111

const configPath = path.join(sandbox.path, "config.txt");

112

const content = fs.readFileSync(configPath, "utf8");

113

expect(content).to.equal("debug=true\nport=3000");

114

115

// Clean up

116

await sandbox.reset(); // Remove all files but keep sandbox

117

// or

118

await sandbox.delete(); // Remove entire sandbox

119

```

120

121

### Sandbox Creation Options

122

123

Different ways to create and configure sandboxes.

124

125

**Usage Examples:**

126

127

```typescript

128

import { TestSandbox } from "@loopback/testlab";

129

130

// Default: Creates unique temporary subdirectory

131

const sandbox1 = new TestSandbox("/tmp/tests");

132

// Creates: /tmp/tests/{process.pid}{random}

133

134

// Use root path directly

135

const sandbox2 = new TestSandbox("/tmp/tests", {subdir: false});

136

// Uses: /tmp/tests

137

138

// Create named subdirectory

139

const sandbox3 = new TestSandbox("/tmp/tests", {subdir: "test-suite-1"});

140

// Creates: /tmp/tests/test-suite-1

141

142

// Multiple sandboxes with same root (for parallel tests)

143

const sandbox4 = new TestSandbox("/tmp/tests"); // /tmp/tests/{pid}{random1}

144

const sandbox5 = new TestSandbox("/tmp/tests"); // /tmp/tests/{pid}{random2}

145

```

146

147

### File Operations

148

149

Comprehensive file and directory operations within the sandbox.

150

151

**Usage Examples:**

152

153

```typescript

154

import { TestSandbox, expect } from "@loopback/testlab";

155

import path from "path";

156

157

const sandbox = new TestSandbox("/tmp/file-ops-test");

158

159

// Write different types of files

160

await sandbox.writeTextFile("README.md", "# Test Project\n\nThis is a test.");

161

162

await sandbox.writeJsonFile("config.json", {

163

database: {

164

host: "localhost",

165

port: 5432

166

},

167

features: ["auth", "logging"]

168

});

169

170

// Create directory structure

171

await sandbox.mkdir("src");

172

await sandbox.mkdir("src/models");

173

await sandbox.mkdir("test");

174

175

// Copy external files

176

const sourceFile = "/path/to/template.js";

177

await sandbox.copyFile(sourceFile); // Uses original filename

178

await sandbox.copyFile(sourceFile, "custom-name.js"); // Custom filename

179

180

// Copy with transformation

181

await sandbox.copyFile(sourceFile, "modified.js", (content) => {

182

return content.replace(/{{PROJECT_NAME}}/g, "my-test-project");

183

});

184

185

// Verify file structure

186

const files = fs.readdirSync(sandbox.path);

187

expect(files).to.containEql("README.md");

188

expect(files).to.containEql("config.json");

189

expect(files).to.containEql("src");

190

191

// Read created files

192

const configPath = path.join(sandbox.path, "config.json");

193

const config = JSON.parse(fs.readFileSync(configPath, "utf8"));

194

expect(config.database.host).to.equal("localhost");

195

```

196

197

### Test Lifecycle Management

198

199

Proper sandbox management in test suites.

200

201

**Usage Examples:**

202

203

```typescript

204

import { TestSandbox, expect } from "@loopback/testlab";

205

206

describe("File processing tests", () => {

207

let sandbox: TestSandbox;

208

209

beforeEach(async () => {

210

sandbox = new TestSandbox("/tmp/test-processing");

211

});

212

213

afterEach(async () => {

214

await sandbox.delete();

215

});

216

217

it("should process configuration files", async () => {

218

// Setup test data

219

await sandbox.writeJsonFile("input.json", {

220

name: "test",

221

version: "1.0.0"

222

});

223

224

// Run the function being tested

225

const result = await processConfigFile(

226

path.join(sandbox.path, "input.json")

227

);

228

229

// Verify results

230

expect(result.name).to.equal("test");

231

expect(result.version).to.equal("1.0.0");

232

});

233

234

it("should handle missing files gracefully", async () => {

235

const missingFile = path.join(sandbox.path, "nonexistent.json");

236

237

await expect(processConfigFile(missingFile))

238

.to.be.rejectedWith(/File not found/);

239

});

240

});

241

242

// Alternative: Reset instead of delete for better performance

243

describe("Multiple test cases", () => {

244

let sandbox: TestSandbox;

245

246

before(async () => {

247

sandbox = new TestSandbox("/tmp/multi-tests");

248

});

249

250

beforeEach(async () => {

251

await sandbox.reset(); // Clear files but keep directory

252

});

253

254

after(async () => {

255

await sandbox.delete(); // Final cleanup

256

});

257

258

// ... tests

259

});

260

```

261

262

### Advanced Patterns

263

264

Complex usage patterns and integration scenarios.

265

266

**Usage Examples:**

267

268

```typescript

269

import { TestSandbox, expect } from "@loopback/testlab";

270

import { spawn } from "child_process";

271

import util from "util";

272

273

const execFile = util.promisify(require("child_process").execFile);

274

275

// Testing CLI tools

276

async function testCliTool() {

277

const sandbox = new TestSandbox("/tmp/cli-test");

278

279

// Create input files

280

await sandbox.writeJsonFile("package.json", {

281

name: "test-package",

282

scripts: {

283

build: "echo 'Building...'"

284

}

285

});

286

287

// Run CLI tool in sandbox

288

const result = await execFile("npm", ["run", "build"], {

289

cwd: sandbox.path

290

});

291

292

expect(result.stdout).to.match(/Building/);

293

294

await sandbox.delete();

295

}

296

297

// Testing file transformations

298

async function testFileTransformation() {

299

const sandbox = new TestSandbox("/tmp/transform-test");

300

301

// Create source files

302

await sandbox.writeTextFile("template.html", `

303

<html>

304

<title>{{TITLE}}</title>

305

<body>{{CONTENT}}</body>

306

</html>

307

`);

308

309

// Transform file

310

const templatePath = path.join(sandbox.path, "template.html");

311

const template = fs.readFileSync(templatePath, "utf8");

312

const rendered = template

313

.replace("{{TITLE}}", "Test Page")

314

.replace("{{CONTENT}}", "<h1>Hello World</h1>");

315

316

await sandbox.writeTextFile("output.html", rendered);

317

318

// Verify transformation

319

const outputPath = path.join(sandbox.path, "output.html");

320

const output = fs.readFileSync(outputPath, "utf8");

321

expect(output).to.match(/<title>Test Page<\/title>/);

322

expect(output).to.match(/<h1>Hello World<\/h1>/);

323

324

await sandbox.delete();

325

}

326

327

// Testing module loading and require cache

328

async function testModuleLoading() {

329

const sandbox = new TestSandbox("/tmp/module-test");

330

331

// Create a module

332

await sandbox.writeTextFile("calculator.js", `

333

exports.add = (a, b) => a + b;

334

exports.multiply = (a, b) => a * b;

335

`);

336

337

// Load and test module

338

const calculatorPath = path.join(sandbox.path, "calculator.js");

339

const calculator = require(calculatorPath);

340

341

expect(calculator.add(2, 3)).to.equal(5);

342

expect(calculator.multiply(4, 5)).to.equal(20);

343

344

// Modify module

345

await sandbox.writeTextFile("calculator.js", `

346

exports.add = (a, b) => a + b + 1; // Modified

347

exports.multiply = (a, b) => a * b;

348

`);

349

350

// Reset clears require cache automatically

351

await sandbox.reset();

352

353

// Recreate and reload

354

await sandbox.writeTextFile("calculator.js", `

355

exports.add = (a, b) => a + b + 1;

356

exports.multiply = (a, b) => a * b;

357

`);

358

359

const newCalculator = require(calculatorPath);

360

expect(newCalculator.add(2, 3)).to.equal(6); // Uses new implementation

361

362

await sandbox.delete();

363

}

364

```

365

366

### Error Handling

367

368

Proper error handling with sandbox operations.

369

370

**Usage Examples:**

371

372

```typescript

373

import { TestSandbox, expect } from "@loopback/testlab";

374

375

// Handle deleted sandbox

376

const sandbox = new TestSandbox("/tmp/error-test");

377

await sandbox.delete();

378

379

try {

380

const path = sandbox.path; // Throws error

381

} catch (error) {

382

expect(error.message).to.match(/TestSandbox instance was deleted/);

383

}

384

385

// Handle file operation errors

386

const validSandbox = new TestSandbox("/tmp/valid-test");

387

388

try {

389

await validSandbox.copyFile("/nonexistent/file.txt");

390

} catch (error) {

391

expect(error.code).to.equal("ENOENT");

392

}

393

394

// Handle permission errors

395

try {

396

const restrictedSandbox = new TestSandbox("/root/restricted");

397

} catch (error) {

398

expect(error.code).to.equal("EACCES");

399

}

400

401

await validSandbox.delete();

402

```