Full Code of Nanciee/cypress-autorecord for AI

master 31408755adcb cached
16 files
29.5 KB
7.7k tokens
1 requests
Download .txt
Repository: Nanciee/cypress-autorecord
Branch: master
Commit: 31408755adcb
Files: 16
Total size: 29.5 KB

Directory structure:
gitextract_43ru3h6s/

├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── README.md
├── cypress/
│   ├── integration/
│   │   ├── fixture.spec.js
│   │   ├── index.html
│   │   └── spec.js
│   ├── plugins/
│   │   └── index.js
│   └── support/
│       ├── commands.js
│       └── index.js
├── cypress.json
├── index.d.ts
├── index.js
├── package.json
├── plugin.js
└── util.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.idea
node_modules
cypress/mocks
cypress/fixtures

================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
  - 10
addons:
  apt:
    packages:
      # Ubuntu 16+ does not install this dependency by default, so we need to install it ourselves
      - libgconf-2-4
cache:
  # Caches $HOME/.npm when npm ci is default script command
  # Caches node_modules in all other cases
  npm: true
  directories:
    # we also need to cache folder with Cypress binary
    - ~/.cache
install:
  - npm ci
script:
  - npm test


================================================
FILE: CHANGELOG.md
================================================
# Changelog
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.1.2] - 2021-09-13
### Fixed
- `interceptPattern` doesn't work with regex, resolving issue [#56](https://github.com/Nanciee/cypress-autorecord/issues/56) [[ERuseckas](https://github.com/ERuseckas)]
- FixtureId is not working with `req.reply`, resolving issues [#32](https://github.com/Nanciee/cypress-autorecord/issues/32), [#54](https://github.com/Nanciee/cypress-autorecord/issues/54), [#55](https://github.com/Nanciee/cypress-autorecord/issues/55) [[mhssmnn](https://github.com/mhssmnn)]
## [3.1.1] - 2021-06-10
### Fixed
- Fix crash when loading request stored in a fixture [[lefta](https://github.com/lefta)]
## [3.1.0] - 2021-05-26
### Added
- Config `interceptPattern` which allows you to specify which endpoints you want to listen and mock [[Ika-x](https://github.com/Ika-x)]
### Fixed
- Sending request to actual server instead of mocking, resolving issues [#51](https://github.com/Nanciee/cypress-autorecord/issues/51) [[Ika-x](https://github.com/Ika-x)]
## [3.0.0] - 2021-05-05
### Changed
- __[BREAKING CHANGE]__ Replace the underlying mechanism for stubbing and mocking to align with Cypress' new API in v6.x and v7.x  [[mhssmnn](https://github.com/mhssmnn)]
### Fixed
- Restore cy.clock in the beforeEach to allow specs to continue running [[ha404](https://github.com/ha404)]

## [2.0.1] - 2021-01-14
### Fixed
- `Fetch()` 'get' requests no longer breaks tests when running with the recorded mocks, resolving issue [#40](https://github.com/Nanciee/cypress-autorecord/issues/40)

## [2.0.0] - 2020-09-18

### Added
- __[BREAKING CHANGE]__ Organize fixtures by spec [[bautistaaa](https://github.com/bautistaaa)]
- Use cy.now(...) to insert new stubbed route [[bebop23](https://github.com/bebop23)]
- Add timestamp to tests so browser is brought back to the time where the mocks live [[bebop23](https://github.com/bebop23)]
- Add new stub when response data changes [[bebop23](https://github.com/bebop23)]
### Fixed
- Convert blob responses to plain text so it can be properly recorded, resolving issues [#7](https://github.com/Nanciee/cypress-autorecord/issues/7) and [#22](https://github.com/Nanciee/cypress-autorecord/issues/22) [[lcnandre](https://github.com/lcnandre)]
- Resolve cypress 5.x incompatibility, resolving issue [#34](https://github.com/Nanciee/cypress-autorecord/issues/34) [[jrocketfingers](https://github.com/jrocketfingers)]

## [1.1.1] - 2019-07-22
### Fixed
- Only record if the request coming in doesn't have a matching URL, METHOD, and BODY, resolving issue [#5](https://github.com/Nanciee/cypress-autorecord/issues/5) [[bautistaaa](https://github.com/bautistaaa)]

## [1.1.0] - 2019-07-22
### Added
- Feature for recording HEAD requests [[alejo90](https://github.com/alejo90)]
- `index.d.ts` to remove Typescript compiler warning [[alejo90](https://github.com/alejo90)]
- Feature for storing a set of whitelisted headers [[alejo90](https://github.com/alejo90)]
- CHANGELOG.md
### Changed
- README.md is updated to reflect changes
## Removed
- Remove the need to pass in `__filename` when calling `autoRecord()` [[alejo90](https://github.com/alejo90)]
## Fixed
- All requests that has the same url and methods but different request bodies no longer just return the last request body

## [1.0.13] - 2019-07-20
### Added
- Feature for recording PUT requests [[chauey](https://github.com/chauey)]
### Changed
- README.md now includes the "Known Issues" section

## [1.0.12] - 2019-04-21
### Fixed
- Filename for mocks copies entire test name minus the extension [[fraserxu](https://github.com/fraserxu)]

## [1.0.11] - 2019-04-11
### Fixed
- Test cases using global asserts

## [1.0.10] - 2019-04-01
### Changed
- README.md now include the "How It Works" section

## [1.0.9] - 2019-03-31
### Added
- README.md explaining current features
### Fixed
- POST requests that has the same url but different request bodies no longer just return the last request body 

## [1.0.8] - 2019-03-27
### Added
- Feature to auto record and stub xhr requests
- Feature to update mocks by inserting [r] in the name of the test
- Feature to clean mocks
- Feature to blacklist routes and prevent it from being recorded




================================================
FILE: README.md
================================================
# Cypress Autorecord

Cypress Autorecord is a plugin built to be used with Cypress.io. It simplifies mocking by auto-recording/stubbing HTTP interactions and automating the process of updating/deleting recordings. Spend more time writing integration tests instead of managing your mock data. Refer to the [changelog](https://github.com/Nanciee/cypress-autorecord/blob/master/CHANGELOG.md) for more details on all the changes.

## v3.0.0 is now live!
Version 3 is now compatible with Cypress 6 and 7 and includes a few fixes. If you are using an earlier cypress version, you will need to use cypress-autorecord v2.x.

## Getting Started

Install from npm

```
npm install --save-dev cypress-autorecord
```

Add this snippet in your project's `/cypress/plugins/index.js`

```js
const fs = require('fs');
const autoRecord = require('cypress-autorecord/plugin');

module.exports = (on, config) => {
  autoRecord(on, config, fs);
};
```
To allow for auto-recording and stubbing to work, require cypress-autorecord in each of your test file and call the function at the beginning of your parent `describe` block.

```js
const autoRecord = require('cypress-autorecord'); // Require the autorecord function
  
describe('Home Page', function() { // Do not use arrow functions
  autoRecord(); // Call the autoRecord function at the beginning of your describe block
  
  // Your hooks (beforeEach, afterEach, etc) goes here
  
  it('...', function() { // Do not use arrow functions
    // Your test goes here
  });
});
```

**_NOTE: Do not use ES6 arrow functions for your describe or it callback. This will cause the recording function to break._**

That is it! Now, just run your tests and the auto-record will take care of the rest!

## Updating Mocks

In the case you need to update your mocks for a particular test:
```js
const autoRecord = require('cypress-autorecord');
  
describe('Home Page', function() {
  autoRecord();
  
  it('[r] my awesome test', function() { // Insert [r] at the beginning of your test name
    // ...
  });
});
```
This will force the test to record over your existent mocks for **ONLY** this test on your next run.

This can also be done through the configurations by adding the test name in the file `cypress.json`:

```json
{
  "autorecord": {
    "recordTests": ["my awesome test"]
  }
}
```

Alternatively, you can update recordings for all tests by setting `forceRecord` to `true` before rerunning your tests:

```json
{
  "autorecord": {
    "forceRecord": true
  }
}
```

## Removing Stale Mocks

Stale mocks that are no longer being used can be automatically removed when you run your tests by setting `cleanMocks` to `true` in the file `cypress.json`:

```json
{
  "autorecord": {
    "cleanMocks": true
  }
}
```

**_NOTE: Only mocks that are used during the run are considered "active". Make sure to only set `cleanMocks` to `true` when you are running ALL your tests. Remove any unintentional `.only` or `.skip`._**

## Set Recording Pattern For Cypress Intercept

By default autorecorder is recording all outgoing requests but if you want to record only specific calls based on pattern(Ex. just record api calls on backend), you can set `interceptPattern` in `cypress.json`. it can be string, regex or glob

```json
{
  "autorecord": {
    "interceptPattern": "http://localhost:3000/api/*"
  }
}
```

## How It Works

### How does the recording and stubbing work?
Cypress Autorecord uses Cypress' built-in `cy.intercept` to hook into every request, including GET, POST, DELETE and PUT. If mocks doesn't exist for a test, the http calls (requests and responses) are captured and automatically written to a local file. If mocks exist for a test, each http call will be stubbed in the `beforeEach` hook.

### Where are the mocks saved?
The mocks will be automatically generated and saved in the `/cypress/mocks/` folder. Mocks are grouped by test name and test file name. You will find mock files matching the name of your test files. Within your mock files, mocks are organized by test names in the order that they were called. Changing the test file name or test name will result to a disconnection to the mocks and trigger a recording on your next run.

### Can I manually update the mocks?
Mocks are saved as a simple json object and can be updated manually. This is **not** recommended since any manual change you make will be overwritten when you automatically update the mocks. Leave the data management to cypress-autorecord. Make any modifications to the http calls inside your test so that it will be consistent across recordings.

```js
it('should display an error message when send message fails', function() {
  cy.route({
    url: '/message',
    method: 'POST',
    status: 404,
    response: { error: 'It did not work' },
  });

  cy.get('[data-cy="msgInput"]').type('Hello World!');
  cy.get('[data-cy="msgSend"]').click();
  cy.get('[data-cy="errorMessage"]').should('contain', 'Looks like we ran into a problem. Please try again.');
});
```

## Known Issues

#### Only XMLHttpRequests will be recorded and stubbed
Cypress-autorecord leverages Cypress' built in `cy.route` to handle stubbing, which means that it inherits some limitations as well. This is the disclaimer on the `cy.route` documentation page with some potential workarounds:
>Please be aware that Cypress only currently supports intercepting XMLHttpRequests. Requests using the Fetch API and other types of network requests like page loads and <script> tags will not be intercepted or visible in the Command Log. See [#95](https://github.com/cypress-io/cypress/issues/95) for more details and temporary workarounds.

## Contributions
I would really appreciate any help with bug fixes or any new features you think might be relevant! Feel free to submit a PR!

================================================
FILE: cypress/integration/fixture.spec.js
================================================
// Set config before importing the autorecord file
Cypress.config('autorecord', { maxInlineResponseSize: 0.00001 });

const autoRecord = require('../../index');
const testName = 'records a mock after the test has finished';

// Ensures the next test doesn't load fixtures before they're
// deleted!
describe('beforeSetup', function () {
  beforeEach(function () {
    cy.task('removeAllMocks');
  });

  it('deletes the mocks', function () {
    cy.readFile('../mocks/fixture.spec.json').should('not.exist');
  });
});

describe('setup', function () {
  autoRecord();

  beforeEach(function () {
    cy.visit('cypress/integration/index.html');
  });

  it(testName, function () {
    cy.readFile('../mocks/fixture.spec.json').should('not.exist');
    cy.readFile('../fixtures/fixture-spec').should('not.exist');
    // Ensure the http request has finished
    cy.contains(/"userId":1/i);
  });
});

describe('test', function () {
  context('the generated mock file', function () {
    it('should contain the fixtureId', function () {
      cy.readFile('cypress/mocks/fixture.spec.json').then((mock) => {
        cy.wrap(mock).its(testName).should('exist');

        const { routes } = mock[testName];
        const [route] = routes;

        expect(route).to.have.property('fixtureId');
      });
    });
  });
});


================================================
FILE: cypress/integration/index.html
================================================
<body>
  <main>
    <h1>cypress-autorecord</h1>
    <div>
      <pre id="json"></pre>
    </div>
  </main>
  <script>
    // var oReq = new XMLHttpRequest();
    // oReq.addEventListener('load', function () {
    //   var json = JSON.parse(this.responseText);
    //   document.getElementById('json').innerText = JSON.stringify(json);
    // });
    // oReq.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');
    // oReq.send();

    fetch('https://jsonplaceholder.typicode.com/todos/1')
      .then((response) => response.json())
      .then((json) => {
        document.getElementById('json').innerText = JSON.stringify(json);
      });
  </script>
</body>


================================================
FILE: cypress/integration/spec.js
================================================
const autoRecord = require('../../index');
const testName = 'records a mock after the test has finished';

describe('setup', function () {
  autoRecord();

  beforeEach(function () {
    cy.task('removeAllMocks');
    cy.visit('cypress/integration/index.html');
  });

  it(testName, function () {
    cy.readFile('../mocks/spec.json').should('not.exist');
    // Ensure the http request has finished
    cy.contains(/"userId":1/i);
  });
});

describe('test', function () {
  context('the generated mock file', function () {
    it('should contain the json response', function () {
      cy.readFile('cypress/mocks/spec.json').then((mock) => {
        cy.wrap(mock).its(testName).should('exist');

        const { routes } = mock[testName];
        const [{ response }] = routes;

        expect(response).to.include({ userId: 1, id: 1 });
      });
    });
  });
});


================================================
FILE: cypress/plugins/index.js
================================================
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

/**
 * @type {Cypress.PluginConfig}
 */

const fs = require('fs');
const autoRecord = require('../../plugin');

module.exports = (on, config) => {
  autoRecord(on, config, fs);
};


================================================
FILE: cypress/support/commands.js
================================================
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })


================================================
FILE: cypress/support/index.js
================================================
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands';

// Alternatively you can use CommonJS syntax:
// require('./commands')


================================================
FILE: cypress.json
================================================
{
  "video": false,
  "screenshotOnRunFailure": false,
  "testFiles": "**/*spec.{js,jsx,ts,tsx}"
}


================================================
FILE: index.d.ts
================================================
declare function autoRecord(): void;

export = autoRecord;


================================================
FILE: index.js
================================================
'use strict';
const path = require('path');
const util = require('./util');

const guidGenerator = util.guidGenerator;
const sizeInMbytes = util.sizeInMbytes;
const blobToPlain = util.blobToPlain;

const cypressConfig = Cypress.config('autorecord') || {};
const isCleanMocks = cypressConfig.cleanMocks || false;
const isForceRecord = cypressConfig.forceRecord || false;
const recordTests = cypressConfig.recordTests || [];
const blacklistRoutes = cypressConfig.blacklistRoutes || [];

let interceptPattern = cypressConfig.interceptPattern || '*';
const interceptPatternFragments =
  interceptPattern.match(/\/(.*?)\/([a-z]*)?$/i);
if (interceptPatternFragments) {
  interceptPattern = new RegExp(
    interceptPatternFragments[1],
    interceptPatternFragments[2] || ""
  );
}

const whitelistHeaders = cypressConfig.whitelistHeaders || [];
const maxInlineResponseSize = cypressConfig.maxInlineResponseSize || 70;
const supportedMethods = ['get', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'];

const fileName = path.basename(
    Cypress.spec.name,
    path.extname(Cypress.spec.name),
);
// The replace fixes Windows path handling
const fixturesFolder = Cypress.config('fixturesFolder').replace(/\\/g, '/');
const fixturesFolderSubDirectory = fileName.replace(/\./, '-');
const mocksFolder = path.join(fixturesFolder, '../mocks');

before(function() {
  if (isCleanMocks) {
    cy.task('cleanMocks');
  }

  if (isForceRecord) {
    cy.task('removeAllMocks');
  }
});

module.exports = function autoRecord() {
  const whitelistHeaderRegexes = whitelistHeaders.map((str) => RegExp(str));

  // For cleaning, to store the test names that are active per file
  const testNames = [];
  // For cleaning, to store the clean mocks per file
  const cleanMockData = {};
  // Locally stores all mock data for this spec file
  let routesByTestId = {};
  // For recording, stores data recorded from hitting the real endpoints
  let routes = [];
  // Stores any fixtures that need to be added
  const addFixture = {};
  // Stores any fixtures that need to be removed
  const removeFixture = [];
  // For force recording, check to see if [r] is present in the test title
  let isTestForceRecord = false;
  // Timestamp for when this test was executed
  let timestamp = null;

  before(function() {
    // Get mock data that relates to this spec file
    cy.task('readFile', path.join(mocksFolder, `${fileName}.json`)).then((data) => {
      routesByTestId = data === null ? {} : data;
    });
  });

  beforeEach(function() {
    // Reset routes before each test case
    routes = [];

    cy.intercept(interceptPattern, (req) => {
      // This is cypress loading the page
      if (
        Object.keys(req.headers).some((k) => k === 'x-cypress-authorization')
      ) {
        return;
      }

      req.reply((res) => {
        const url = req.url;
        const status = res.statusCode;
        const method = req.method;
        const data =
          res.body.constructor.name === 'Blob'
            ? blobToPlain(res.body)
            : res.body;
        const body = req.body;
        const headers = Object.entries(res.headers)
          .filter(([key]) =>
            whitelistHeaderRegexes.some((regex) => regex.test(key)),
          )
          .reduce((obj, [key, value]) => ({ ...obj, [key]: value }), {});

        // We push a new entry into the routes array
        // Do not rerecord duplicate requests
        if (
          !routes.some(
            (route) =>
              route.url === url &&
              route.body === body &&
              route.method === method &&
              // when the response has changed for an identical request signature
              // add this entry as well.  This is useful for polling-oriented endpoints
              // that can have varying responses.
              route.response === data,
          )
        ) {
          routes.push({ url, method, status, data, body, headers });
        }
      });
    });

    // check to see if test is being force recorded
    // TODO: change this to regex so it only reads from the beginning of the string
    isTestForceRecord = this.currentTest.title.includes('[r]');
    this.currentTest.title = isTestForceRecord ? this.currentTest.title.split('[r]')[1].trim() : this.currentTest.title;

    // Load stubbed data from local JSON file
    // Do not stub if...
    // This test is being force recorded
    // there are no mock data for this test
    if (
      !recordTests.includes(this.currentTest.title)
      && !isTestForceRecord
      && routesByTestId[this.currentTest.title]
    ) {
      // This is used to group routes by method type and url (e.g. { GET: { '/api/messages': {...} }})
      const sortedRoutes = {};
      supportedMethods.forEach((method) => {
        sortedRoutes[method] = {};
      });

      // set the browser's Date to the timestamp at which this spec's endpoints were recorded.
      cy.clock(routesByTestId[this.currentTest.title].timestamp, ['Date']);

      routesByTestId[this.currentTest.title].routes.forEach((request) => {
        if (!sortedRoutes[request.method][request.url]) {
          sortedRoutes[request.method][request.url] = [];
        }

        sortedRoutes[request.method][request.url].push(request);
      });

      const createStubbedRoute = (method, url) => {
        let index = 0;
        const response = sortedRoutes[method][url][index];

        cy.intercept(
          {
            url,
            method,
          },
          (req) => {
            req.reply((res) => {
              const newResponse = sortedRoutes[method][url][index];
              res.send(
                newResponse.status,
                newResponse.fixtureId
                  ? {
                      fixture: `${fixturesFolderSubDirectory}/${newResponse.fixtureId}.json`,
                    }
                  : newResponse.response,
                newResponse.headers,
              );

              if (sortedRoutes[method][url].length > index + 1) {
                index++;
              }
            });
          },
        );
      };

      // Stub all recorded routes
      Object.keys(sortedRoutes).forEach((method) => {
        Object.keys(sortedRoutes[method]).forEach((url) => createStubbedRoute(method, url));
      });
    } else {
      // lock the browser's timestamp in place so that there is no variation with the
      // timestamp REST APIs use as an argument due to undeterministic page load times
      // which will cause varying timestamps.  `cy.clock` locks the timestamp.
      timestamp = Date.now();
      cy.clock(timestamp, ['Date']);
    }

    // Store test name if isCleanMocks is true
    if (isCleanMocks) {
      testNames.push(this.currentTest.title);
    }

    cy.clock().invoke('restore');
  });

  afterEach(function() {
    // Check to see if the current test already has mock data or if forceRecord is on
    if (
      (!routesByTestId[this.currentTest.title]
      || isTestForceRecord
      || recordTests.includes(this.currentTest.title))
      && !isCleanMocks
    ) {
      // Construct endpoint to be saved locally
      const endpoints = routes.map((request) => {
        // Check to see of mock data is too large for request header
        const isFileOversized = sizeInMbytes(request.data) > maxInlineResponseSize;
        let fixtureId;

        // If the mock data is too large, store it in a separate json
        if (isFileOversized) {
          fixtureId = guidGenerator();
          addFixture[path.join(fixturesFolder, fixturesFolderSubDirectory, `${fixtureId}.json`)] = request.data;
        }

        return {
          fixtureId: fixtureId,
          url: request.url,
          method: request.method,
          status: request.status,
          headers: request.headers,
          body: request.body,
          response: isFileOversized ? undefined : request.data
        };
      });

      // Delete fixtures if we are overwriting mock data
      if (routesByTestId[this.currentTest.title]) {
        routesByTestId[this.currentTest.title].routes.forEach((route) => {
          // If fixtureId exist, delete the json
          if (route.fixtureId) {
            removeFixture.push(path.join(fixturesFolder, fixturesFolderSubDirectory, `${route.fixtureId}.json`));
          }
        });
      }

      // Store the endpoint for this test in the mock data object for this file if there are endpoints for this test
      if (endpoints.length > 0) {
        routesByTestId[this.currentTest.title] = {
          // since REST APIs can pass a timestamp argument, we need to keep track
          // of the time at which this spec was recorded so we can set the browser's Date
          // to that specific time so that the endpoints can be properly stubbed as the
          // the timestamp is part of many of the APIs' signature as well as POST body and uniquely identifies it.
          timestamp,
          routes: endpoints
        };
      }
    }
  });

  after(function() {
    // Transfer used mock data to new object to be stored locally
    if (isCleanMocks) {
      Object.keys(routesByTestId).forEach((testName) => {
        if (testNames.includes(testName)) {
          cleanMockData[testName] = routesByTestId[testName];
        } else {
          routesByTestId[testName].routes.forEach((route) => {
            if (route.fixtureId) {
              cy.task('deleteFile', path.join(fixturesFolder, fixturesFolderSubDirectory, `${route.fixtureId}.json`));
            }
          });
        }
      });
    }

    removeFixture.forEach((fixtureName) => cy.task('deleteFile', fixtureName));
    cy.writeFile(path.join(mocksFolder, `${fileName}.json`), isCleanMocks ? cleanMockData : routesByTestId);
    Object.keys(addFixture).forEach((fixtureName) => {
      cy.writeFile(fixtureName, addFixture[fixtureName]);
    });
  });
};


================================================
FILE: package.json
================================================
{
  "name": "cypress-autorecord",
  "version": "3.1.2",
  "description": "It simplifies mocking by auto-recording/stubbing HTTP interactions and automate the process of updating/deleting recordings.",
  "main": "index.js",
  "types": "index.d.ts",
  "scripts": {
    "test": "cypress run --headless",
    "test:watch": "cypress open"
  },
  "author": "Nancy Du",
  "keywords": [
    "Cypress",
    "http",
    "https",
    "record",
    "playback",
    "mock",
    "vcr"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/Nanciee/cypress-autorecord"
  },
  "license": "MIT",
  "devDependencies": {
    "cypress": "6.1.0"
  }
}


================================================
FILE: plugin.js
================================================
const path = require('path');

module.exports = (on, config, fs) => {
  // `on` is used to hook into various events Cypress emits
  // `config` is the resolved Cypress config
  const mocksFolder = path.resolve(config.fixturesFolder, '../mocks');

  const readFile = (filePath) => {
    if (fs.existsSync(filePath)) {
      return JSON.parse(fs.readFileSync(filePath, 'utf8'));
    }

    return null;
  };

  const deleteFile = (filePath) => {
    if (fs.existsSync(filePath)) {
      fs.unlinkSync(filePath);
      return true;
    }

    return null;
  };

  const deleteFolder = (directoryPath) => {
    if (fs.existsSync(directoryPath)) {
      fs.readdirSync(directoryPath).forEach((file, index) => {
        const curPath = path.join(directoryPath, file);
        if (fs.lstatSync(curPath).isDirectory()) {
          // recurse
          deleteFolder(curPath);
        } else {
          // delete file
          fs.unlinkSync(curPath);
        }
      });
      fs.rmdirSync(directoryPath);
    }
  }

  const cleanMocks = () => {
    // TODO: create error handling
    const specFiles = fs.readdirSync(config.integrationFolder);
    const mockFiles = fs.readdirSync(mocksFolder);
    mockFiles.forEach((mockName) => {
      const isMockUsed = specFiles.find((specName) => specName.split('.')[0] === mockName.split('.')[0]);
      if (!isMockUsed) {
        const mockData = readFile(path.join(mocksFolder, mockName));
        Object.keys(mockData).forEach((testName) => {
          mockData[testName].forEach((route) => {
            if (route.fixtureId) {
              deleteFile(path.join(config.fixturesFolder, `${route.fixtureId}.json`));
            }
          });
        });

        deleteFile(path.join(mocksFolder, mockName));
      }
    });

    return null;
  };

  const removeAllMocks = () => {
    
    if (fs.existsSync(config.fixturesFolder)) {
      const fixtureFiles = fs.readdirSync(config.fixturesFolder);
      fixtureFiles.forEach((fileName) => {
        const file = path.join(config.fixturesFolder, fileName);
        if (fs.lstatSync(file).isDirectory()) {
          deleteFolder(file);
        } else {
          deleteFile(file);
        }
      });
    }

    if (fs.existsSync(mocksFolder)) {
      const mockFiles = fs.readdirSync(mocksFolder);
      mockFiles.forEach((fileName) => {
        deleteFile(path.join(mocksFolder, fileName));
      });
    }

    return null;
  };

  on('task', {
    readFile,
    deleteFile,
    cleanMocks,
    removeAllMocks
  });
};


================================================
FILE: util.js
================================================
const sizeInMbytes = (obj) => {
  let bytes = 0;

  const sizeOf = (obj) => {
    let objClass;
    if (obj !== null && obj !== undefined) {
      switch (typeof obj) {
        case 'number':
          bytes += 8;
          break;
        case 'string':
          bytes += obj.length * 2;
          break;
        case 'boolean':
          bytes += 4;
          break;
        case 'object':
          objClass = Object.prototype.toString.call(obj).slice(8, -1);
          if (objClass === 'Object' || objClass === 'Array') {
            for (const key in obj) {
              if (!obj.hasOwnProperty(key)) continue;
              sizeOf(obj[key]);
            }
          } else bytes += obj.toString().length * 2;
          break;
      }
    }
    return bytes;
  };

  return sizeOf(obj) / 1024;
};

const guidGenerator = () => {
  const s4 = () => (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
  return (s4() + s4() + s4() + s4() + s4() + s4() + s4() + s4());
};

const blobToPlain = (blob) => {
  let uri = URL.createObjectURL(blob);
  let xhr = new XMLHttpRequest();

  xhr.open('GET', uri, false);
  xhr.send();

  URL.revokeObjectURL(uri);

  return blob.type === 'application/json'
    ? JSON.parse(xhr.response)
    : xhr.response;
}

module.exports = {
  sizeInMbytes,
  guidGenerator,
  blobToPlain
};
Download .txt
gitextract_43ru3h6s/

├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── README.md
├── cypress/
│   ├── integration/
│   │   ├── fixture.spec.js
│   │   ├── index.html
│   │   └── spec.js
│   ├── plugins/
│   │   └── index.js
│   └── support/
│       ├── commands.js
│       └── index.js
├── cypress.json
├── index.d.ts
├── index.js
├── package.json
├── plugin.js
└── util.js
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (32K chars).
[
  {
    "path": ".gitignore",
    "chars": 49,
    "preview": ".idea\nnode_modules\ncypress/mocks\ncypress/fixtures"
  },
  {
    "path": ".travis.yml",
    "chars": 431,
    "preview": "language: node_js\nnode_js:\n  - 10\naddons:\n  apt:\n    packages:\n      # Ubuntu 16+ does not install this dependency by de"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 4321,
    "preview": "# Changelog\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to"
  },
  {
    "path": "README.md",
    "chars": 5776,
    "preview": "# Cypress Autorecord\n\nCypress Autorecord is a plugin built to be used with Cypress.io. It simplifies mocking by auto-rec"
  },
  {
    "path": "cypress/integration/fixture.spec.js",
    "chars": 1315,
    "preview": "// Set config before importing the autorecord file\nCypress.config('autorecord', { maxInlineResponseSize: 0.00001 });\n\nco"
  },
  {
    "path": "cypress/integration/index.html",
    "chars": 669,
    "preview": "<body>\n  <main>\n    <h1>cypress-autorecord</h1>\n    <div>\n      <pre id=\"json\"></pre>\n    </div>\n  </main>\n  <script>\n  "
  },
  {
    "path": "cypress/integration/spec.js",
    "chars": 869,
    "preview": "const autoRecord = require('../../index');\nconst testName = 'records a mock after the test has finished';\n\ndescribe('set"
  },
  {
    "path": "cypress/plugins/index.js",
    "chars": 716,
    "preview": "/// <reference types=\"cypress\" />\n// ***********************************************************\n// This example plugins"
  },
  {
    "path": "cypress/support/commands.js",
    "chars": 838,
    "preview": "// ***********************************************\n// This example commands.js shows you how to\n// create various custom"
  },
  {
    "path": "cypress/support/index.js",
    "chars": 671,
    "preview": "// ***********************************************************\n// This example support/index.js is processed and\n// load"
  },
  {
    "path": "cypress.json",
    "chars": 99,
    "preview": "{\n  \"video\": false,\n  \"screenshotOnRunFailure\": false,\n  \"testFiles\": \"**/*spec.{js,jsx,ts,tsx}\"\n}\n"
  },
  {
    "path": "index.d.ts",
    "chars": 59,
    "preview": "declare function autoRecord(): void;\n\nexport = autoRecord;\n"
  },
  {
    "path": "index.js",
    "chars": 9861,
    "preview": "'use strict';\nconst path = require('path');\nconst util = require('./util');\n\nconst guidGenerator = util.guidGenerator;\nc"
  },
  {
    "path": "package.json",
    "chars": 649,
    "preview": "{\n  \"name\": \"cypress-autorecord\",\n  \"version\": \"3.1.2\",\n  \"description\": \"It simplifies mocking by auto-recording/stubbi"
  },
  {
    "path": "plugin.js",
    "chars": 2511,
    "preview": "const path = require('path');\n\nmodule.exports = (on, config, fs) => {\n  // `on` is used to hook into various events Cypr"
  },
  {
    "path": "util.js",
    "chars": 1334,
    "preview": "const sizeInMbytes = (obj) => {\n  let bytes = 0;\n\n  const sizeOf = (obj) => {\n    let objClass;\n    if (obj !== null && "
  }
]

About this extraction

This page contains the full source code of the Nanciee/cypress-autorecord GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (29.5 KB), approximately 7.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!