{"id":15383,"library":"socket-vcr-test","title":"Socket VCR Test","description":"socket-vcr-test is a TypeScript-first library (current version 2.0.1) for recording and replaying HTTP interactions in your test suite, ensuring fast, deterministic, and accurate tests. It functions as a fork of `epignosisx/vcr-test`, aiming to provide enhanced features or maintenance. The library offers flexible recording modes (e.g., `once`, `none`, `update`, `all`) to control cassette generation and playback behavior. Key differentiators include built-in support for request masking to handle sensitive data, and a highly extensible request matching system that allows custom logic for comparing URLs, headers, and bodies. It primarily targets modern JavaScript and TypeScript environments for development testing workflows. It's a useful tool for isolating tests from external network dependencies and ensuring consistent test results without relying on live APIs.","status":"active","version":"2.0.1","language":"javascript","source_language":"en","source_url":"https://github.com/SocketDev/vcr-test","tags":["javascript","http","mocking","record","vcr","testing","typescript"],"install":[{"cmd":"npm install socket-vcr-test","lang":"bash","label":"npm"},{"cmd":"yarn add socket-vcr-test","lang":"bash","label":"yarn"},{"cmd":"pnpm add socket-vcr-test","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"The library is primarily designed for ESM usage in TypeScript or modern JavaScript environments. For CommonJS, named imports are still possible but less idiomatic for newer projects.","wrong":"const { VCR } = require('socket-vcr-test');","symbol":"VCR","correct":"import { VCR } from 'socket-vcr-test';"},{"note":"FileStorage is a named export, not a default export or submodule. Ensure correct named import syntax.","wrong":"import FileStorage from 'socket-vcr-test/FileStorage';","symbol":"FileStorage","correct":"import { FileStorage } from 'socket-vcr-test';"},{"note":"The enum for recording behavior is named `RecordMode`, not `RecordingMode`.","wrong":"import { RecordingMode } from 'socket-vcr-test';","symbol":"RecordMode","correct":"import { RecordMode } from 'socket-vcr-test';"},{"note":"Used for customizing how VCR matches incoming requests against recorded interactions.","symbol":"DefaultRequestMatcher","correct":"import { DefaultRequestMatcher } from 'socket-vcr-test';"}],"quickstart":{"code":"import { join } from 'node:path';\nimport { VCR, FileStorage, RecordMode } from 'socket-vcr-test';\nimport { readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from 'node:fs';\n\n// Mock an external API call for demonstration purposes\nconst mockApi = {\n  myAwesomeApiCall: async () => {\n    console.log('    Making a simulated API call...');\n    return new Promise(resolve => setTimeout(() => resolve({ data: 'hello from real API', timestamp: Date.now() }), 50));\n  }\n};\n\n// Basic mocks for a runnable 'test' context\nconst expect = (value: any) => ({\n  toBeDefined: () => {\n    if (value === undefined || value === null) throw new Error('Expected value to be defined');\n  },\n  toEqual: (expected: any) => {\n    if (JSON.stringify(value) !== JSON.stringify(expected)) throw new Error(`Expected ${JSON.stringify(value)} to equal ${JSON.stringify(expected)}`);\n  }\n});\nconst describe = (name: string, fn: () => void) => { console.log(`\\n-- Running Suite: ${name} --`); fn(); };\nconst it = (name: string, fn: () => Promise<void>) => {\n  console.log(`  Test: ${name}`);\n  fn().then(() => console.log(`  ✓ Passed: ${name}`)).catch(e => console.error(`  ✗ Failed: ${name}\\n`, e));\n};\n\n// Setup a dummy directory for cassettes\nconst CASSETTES_DIR = join(process.cwd(), '__quickstart_cassettes__');\nif (!existsSync(CASSETTES_DIR)) {\n  mkdirSync(CASSETTES_DIR, { recursive: true });\n} else {\n  // Clean up previous run's cassettes for a fresh start\n  rmSync(CASSETTES_DIR, { recursive: true, force: true });\n  mkdirSync(CASSETTES_DIR, { recursive: true });\n}\n\ndescribe('socket-vcr-test quickstart', () => {\n  it('should record and replay an API call using VCR', async () => {\n    const cassetteName = 'quickstart_api_call';\n    const cassettePath = join(CASSETTES_DIR, cassetteName + '.yml');\n\n    // 1. Initial run: Configure VCR to record (default mode is 'once')\n    let vcr = new VCR(new FileStorage(CASSETTES_DIR));\n    vcr.mode = RecordMode.once; // Explicitly set, though it's the default\n    console.log(`    First run (Recording mode: ${vcr.mode}): Cassette ${cassetteName}.yml should be created.`);\n\n    await vcr.useCassette(cassetteName, async () => {\n      const result = await mockApi.myAwesomeApiCall();\n      expect(result).toBeDefined();\n      expect((result as any).data).toEqual('hello from real API');\n    });\n    expect(existsSync(cassettePath)).toEqual(true);\n    console.log(`    Cassette '${cassetteName}.yml' now exists.`);\n\n    // 2. Second run: VCR should replay from the cassette\n    // Re-initialize VCR to clear any internal state from the previous run\n    vcr = new VCR(new FileStorage(CASSETTES_DIR));\n    vcr.mode = RecordMode.once; // Still 'once', but cassette exists\n    console.log(`\\n    Second run (Replay mode: ${vcr.mode}): Cassette ${cassetteName}.yml should be replayed.`);\n    \n    const startTime = Date.now();\n    await vcr.useCassette(cassetteName, async () => {\n      const result = await mockApi.myAwesomeApiCall();\n      expect(result).toBeDefined();\n      expect((result as any).data).toEqual('hello from real API');\n    });\n    const endTime = Date.now();\n    console.log(`    API call completed in ${endTime - startTime}ms (expected very fast replay).`);\n    \n    // Demonstrating `update` mode\n    vcr = new VCR(new FileStorage(CASSETTES_DIR));\n    vcr.mode = RecordMode.update;\n    console.log(`\\n    Third run (Update mode: ${vcr.mode}): Simulating change to trigger re-recording if needed.`);\n    await vcr.useCassette(cassetteName, async () => {\n      // Even if mockApi changes, 'update' would re-record or update existing entries\n      const result = await mockApi.myAwesomeApiCall();\n      expect(result).toBeDefined();\n    });\n  });\n});\n\n// To run this example: save as `quickstart.ts`, compile with `tsc quickstart.ts`, then `node quickstart.js`","lang":"typescript","description":"This quickstart demonstrates how to initialize `socket-vcr-test` with `FileStorage`, use `useCassette` to wrap an asynchronous API call, and verifies that the HTTP interactions are recorded and then replayed. It shows both recording (first run) and replaying (subsequent runs) behavior, as well as an example of `RecordMode.update`."},"warnings":[{"fix":"Review the GitHub repository's commit history and issue tracker for details on changes from the upstream `epignosisx/vcr-test`. Thoroughly test your application when migrating or using this fork.","message":"As a fork of `epignosisx/vcr-test`, this package may introduce breaking changes or behavioral differences compared to the original project. Users migrating or expecting parity should consult the source code and test thoroughly. The README explicitly states a 'TODO' regarding deviations, indicating potential differences.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Implement a `requestMasker` function to redact or replace sensitive information within request headers or bodies before they are written to the cassette. For example: `vcr.requestMasker = (req) => { req.headers['authorization'] = 'masked'; };`","message":"Sensitive data (e.g., API keys, bearer tokens) can be recorded into cassettes if not explicitly masked. Storing sensitive data in your test fixtures poses a security risk, especially in version control systems.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Use `RecordMode.once` (the default) for most development, which records if no cassette exists and replays otherwise. For updating existing cassettes, use `RecordMode.update` temporarily. Ensure `RecordMode.none` is used in CI to prevent unintended network access.","message":"Misusing `RecordMode` can lead to stale cassettes or unexpected network calls. For instance, `RecordMode.all` always makes live HTTP calls, which defeats the purpose of fast, deterministic tests in CI/CD environments. Conversely, `RecordMode.none` will fail if a cassette doesn't exist.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Ensure that HTTP-making code is tightly scoped within the `vcr.useCassette` block. Consider using `requestMatch` customization to ignore specific URLs or request patterns that are irrelevant to the test but might occur within the same context. Clear VCR state between tests if necessary.","message":"VCR intercepts *all* HTTP traffic made during the `useCassette` block. Unintended HTTP requests from other parts of your test setup or third-party libraries can pollute your cassettes, leading to larger files, irrelevant interactions, or even non-deterministic behavior if those extra requests vary.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"If the request has genuinely changed, update the cassette by running the test with `vcr.mode = RecordMode.update` or by deleting the cassette file and letting `RecordMode.once` re-record it. If the difference is in dynamic headers (e.g., `Date`, `Authorization`), use `vcr.matcher.ignoreHeaders.add('header-name')` or implement a custom request masker.","cause":"An HTTP request made during the test run did not exactly match any recorded interactions in the specified cassette. This can happen if the request URL, method, headers, or body changed.","error":"Error: No matching HTTP interaction found in cassette 'cassette_name'"},{"fix":"Verify that `vcr.useCassette()` properly wraps all code that makes HTTP requests. Ensure the `VCR` instance is correctly initialized with a `FileStorage` or custom storage. If using a custom HTTP client, ensure it's compatible with `socket-vcr-test`'s interception mechanism.","cause":"This usually indicates that HTTP interception is not active or correctly configured, or that a request was made outside of the `vcr.useCassette` block.","error":"TypeError: Cannot read properties of undefined (reading 'headers')"},{"fix":"Ensure you are using correct ESM import syntax: `import { VCR } from 'socket-vcr-test';`. For CommonJS environments that require this package, this specific error usually means the CJS compatibility isn't set up, or the project is trying to import an ESM-only package incorrectly.","cause":"Attempting to use `require()` syntax (`const { VCR } = require('socket-vcr-test');`) in a pure ESM project, or an incorrect import statement in TypeScript/ESM.","error":"SyntaxError: Named export 'VCR' not found. The requested module 'socket-vcr-test' does not provide an export named 'VCR'"}],"ecosystem":"npm"}