CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-loopback--testlab

A collection of test utilities specifically designed for LoopBack 4 applications and TypeScript testing

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

test-sandbox.mddocs/

Test Sandbox

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

Capabilities

TestSandbox Class

Main class for creating and managing isolated test directories.

/**
 * TestSandbox class provides a convenient way to get a reference to a
 * sandbox folder in which you can perform operations for testing purposes
 */
class TestSandbox {
  /**
   * Create a new TestSandbox instance
   * @param rootPath - Root path for the sandbox (resolved against current directory if relative)
   * @param options - Configuration options for sandbox creation
   */
  constructor(rootPath: string, options?: TestSandboxOptions);

  /**
   * Get the absolute path to the sandbox directory
   * @throws Error if the sandbox has been deleted
   */
  get path(): string;

  /**
   * Reset the TestSandbox by removing all files in it
   * Also clears require cache for any files in the sandbox path
   */
  reset(): Promise<void>;

  /**
   * Delete the TestSandbox completely
   * After deletion, the sandbox instance becomes unusable
   */
  delete(): Promise<void>;

  /**
   * Create a directory in the TestSandbox
   * @param dir - Directory path relative to sandbox root
   */
  mkdir(dir: string): Promise<void>;

  /**
   * Copy a file from source to the TestSandbox
   * Handles source maps for .js files automatically
   * @param src - Absolute path of source file
   * @param dest - Destination filename (relative to sandbox), defaults to original filename
   * @param transform - Optional function to transform file content
   */
  copyFile(
    src: string,
    dest?: string,
    transform?: (content: string) => string
  ): Promise<void>;

  /**
   * Create a JSON file in the TestSandbox
   * @param dest - Destination filename (relative to sandbox)
   * @param data - Data to serialize as JSON
   */
  writeJsonFile(dest: string, data: unknown): Promise<void>;

  /**
   * Create a text file in the TestSandbox
   * @param dest - Destination filename (relative to sandbox)
   * @param data - Text content to write
   */
  writeTextFile(dest: string, data: string): Promise<void>;
}

/**
 * Configuration options for TestSandbox
 */
interface TestSandboxOptions {
  /**
   * Controls subdirectory creation:
   * - true: Creates unique temporary subdirectory (default)
   * - false: Uses root path directly
   * - string: Creates named subdirectory
   */
  subdir: boolean | string;
}

Usage Examples:

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

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

// Create test files
await sandbox.writeTextFile("config.txt", "debug=true\nport=3000");
await sandbox.writeJsonFile("package.json", {
  name: "test-app",
  version: "1.0.0"
});

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

// Work with files
const configPath = path.join(sandbox.path, "config.txt");
const content = fs.readFileSync(configPath, "utf8");
expect(content).to.equal("debug=true\nport=3000");

// Clean up
await sandbox.reset(); // Remove all files but keep sandbox
// or
await sandbox.delete(); // Remove entire sandbox

Sandbox Creation Options

Different ways to create and configure sandboxes.

Usage Examples:

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

// Default: Creates unique temporary subdirectory
const sandbox1 = new TestSandbox("/tmp/tests");
// Creates: /tmp/tests/{process.pid}{random}

// Use root path directly
const sandbox2 = new TestSandbox("/tmp/tests", {subdir: false});
// Uses: /tmp/tests

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

// Multiple sandboxes with same root (for parallel tests)
const sandbox4 = new TestSandbox("/tmp/tests"); // /tmp/tests/{pid}{random1}
const sandbox5 = new TestSandbox("/tmp/tests"); // /tmp/tests/{pid}{random2}

File Operations

Comprehensive file and directory operations within the sandbox.

Usage Examples:

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

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

// Write different types of files
await sandbox.writeTextFile("README.md", "# Test Project\n\nThis is a test.");

await sandbox.writeJsonFile("config.json", {
  database: {
    host: "localhost",
    port: 5432
  },
  features: ["auth", "logging"]
});

// Create directory structure
await sandbox.mkdir("src");
await sandbox.mkdir("src/models");
await sandbox.mkdir("test");

// Copy external files
const sourceFile = "/path/to/template.js";
await sandbox.copyFile(sourceFile); // Uses original filename
await sandbox.copyFile(sourceFile, "custom-name.js"); // Custom filename

// Copy with transformation
await sandbox.copyFile(sourceFile, "modified.js", (content) => {
  return content.replace(/{{PROJECT_NAME}}/g, "my-test-project");
});

// Verify file structure
const files = fs.readdirSync(sandbox.path);
expect(files).to.containEql("README.md");
expect(files).to.containEql("config.json");
expect(files).to.containEql("src");

// Read created files
const configPath = path.join(sandbox.path, "config.json");
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
expect(config.database.host).to.equal("localhost");

Test Lifecycle Management

Proper sandbox management in test suites.

Usage Examples:

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

describe("File processing tests", () => {
  let sandbox: TestSandbox;

  beforeEach(async () => {
    sandbox = new TestSandbox("/tmp/test-processing");
  });

  afterEach(async () => {
    await sandbox.delete();
  });

  it("should process configuration files", async () => {
    // Setup test data
    await sandbox.writeJsonFile("input.json", {
      name: "test",
      version: "1.0.0"
    });

    // Run the function being tested
    const result = await processConfigFile(
      path.join(sandbox.path, "input.json")
    );

    // Verify results
    expect(result.name).to.equal("test");
    expect(result.version).to.equal("1.0.0");
  });

  it("should handle missing files gracefully", async () => {
    const missingFile = path.join(sandbox.path, "nonexistent.json");
    
    await expect(processConfigFile(missingFile))
      .to.be.rejectedWith(/File not found/);
  });
});

// Alternative: Reset instead of delete for better performance
describe("Multiple test cases", () => {
  let sandbox: TestSandbox;

  before(async () => {
    sandbox = new TestSandbox("/tmp/multi-tests");
  });

  beforeEach(async () => {
    await sandbox.reset(); // Clear files but keep directory
  });

  after(async () => {
    await sandbox.delete(); // Final cleanup
  });

  // ... tests
});

Advanced Patterns

Complex usage patterns and integration scenarios.

Usage Examples:

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

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

// Testing CLI tools
async function testCliTool() {
  const sandbox = new TestSandbox("/tmp/cli-test");
  
  // Create input files
  await sandbox.writeJsonFile("package.json", {
    name: "test-package",
    scripts: {
      build: "echo 'Building...'"
    }
  });

  // Run CLI tool in sandbox
  const result = await execFile("npm", ["run", "build"], {
    cwd: sandbox.path
  });

  expect(result.stdout).to.match(/Building/);
  
  await sandbox.delete();
}

// Testing file transformations
async function testFileTransformation() {
  const sandbox = new TestSandbox("/tmp/transform-test");
  
  // Create source files
  await sandbox.writeTextFile("template.html", `
    <html>
      <title>{{TITLE}}</title>
      <body>{{CONTENT}}</body>
    </html>
  `);

  // Transform file
  const templatePath = path.join(sandbox.path, "template.html");
  const template = fs.readFileSync(templatePath, "utf8");
  const rendered = template
    .replace("{{TITLE}}", "Test Page")
    .replace("{{CONTENT}}", "<h1>Hello World</h1>");

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

  // Verify transformation
  const outputPath = path.join(sandbox.path, "output.html");
  const output = fs.readFileSync(outputPath, "utf8");
  expect(output).to.match(/<title>Test Page<\/title>/);
  expect(output).to.match(/<h1>Hello World<\/h1>/);

  await sandbox.delete();
}

// Testing module loading and require cache
async function testModuleLoading() {
  const sandbox = new TestSandbox("/tmp/module-test");
  
  // Create a module
  await sandbox.writeTextFile("calculator.js", `
    exports.add = (a, b) => a + b;
    exports.multiply = (a, b) => a * b;
  `);

  // Load and test module
  const calculatorPath = path.join(sandbox.path, "calculator.js");
  const calculator = require(calculatorPath);
  
  expect(calculator.add(2, 3)).to.equal(5);
  expect(calculator.multiply(4, 5)).to.equal(20);

  // Modify module
  await sandbox.writeTextFile("calculator.js", `
    exports.add = (a, b) => a + b + 1; // Modified
    exports.multiply = (a, b) => a * b;
  `);

  // Reset clears require cache automatically
  await sandbox.reset();
  
  // Recreate and reload
  await sandbox.writeTextFile("calculator.js", `
    exports.add = (a, b) => a + b + 1;
    exports.multiply = (a, b) => a * b;
  `);

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

  await sandbox.delete();
}

Error Handling

Proper error handling with sandbox operations.

Usage Examples:

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

// Handle deleted sandbox
const sandbox = new TestSandbox("/tmp/error-test");
await sandbox.delete();

try {
  const path = sandbox.path; // Throws error
} catch (error) {
  expect(error.message).to.match(/TestSandbox instance was deleted/);
}

// Handle file operation errors
const validSandbox = new TestSandbox("/tmp/valid-test");

try {
  await validSandbox.copyFile("/nonexistent/file.txt");
} catch (error) {
  expect(error.code).to.equal("ENOENT");
}

// Handle permission errors
try {
  const restrictedSandbox = new TestSandbox("/root/restricted");
} catch (error) {
  expect(error.code).to.equal("EACCES");
}

await validSandbox.delete();

docs

assertions.md

http-client.md

http-utilities.md

index.md

request-response-mocking.md

test-doubles.md

test-sandbox.md

validation-helpers.md

tile.json