{
  "doc": {
    "id": "api/node-events/after-spec-api",
    "title": "after:spec event",
    "description": "The after:spec event fires after a spec file is run in Cypress.",
    "section": "api",
    "source_path": "/llm/markdown/api/node-events/after-spec-api.md",
    "version": "f60251bf707b0077a9535579c9f90d615440cc35",
    "updated_at": "2026-10-16T10:56:58.670Z",
    "headings": [
      {
        "id": "api/node-events/after-spec-api#after-spec-event",
        "text": "After Spec Event",
        "level": 1
      },
      {
        "id": "api/node-events/after-spec-api#syntax",
        "text": "Syntax",
        "level": 2
      },
      {
        "id": "api/node-events/after-spec-api#usage",
        "text": "Usage",
        "level": 2
      },
      {
        "id": "api/node-events/after-spec-api#log-the-relative-spec-path-to-stdout-after-the-spec-is-run",
        "text": "Log the relative spec path to stdout after the spec is run",
        "level": 3
      },
      {
        "id": "api/node-events/after-spec-api#examples",
        "text": "Examples",
        "level": 2
      },
      {
        "id": "api/node-events/after-spec-api#delete-the-recorded-video-if-the-spec-passed",
        "text": "Delete the recorded video if the spec passed",
        "level": 3
      },
      {
        "id": "api/node-events/after-spec-api#delete-the-recorded-video-if-no-tests-retried",
        "text": "Delete the recorded video if no tests retried",
        "level": 3
      },
      {
        "id": "api/node-events/after-spec-api#see-also",
        "text": "See also",
        "level": 2
      }
    ]
  },
  "chunks": [
    {
      "id": "api/node-events/after-spec-api#syntax",
      "doc_id": "api/node-events/after-spec-api",
      "heading": "Syntax",
      "heading_level": 2,
      "content_markdown": "## Syntax\n\n⚠️ This code is part of the [setupNodeEvents](/llm/markdown/app/plugins/plugins-guide.md#Using-a-plugin) function and thus executes in the Node environment. You cannot call `Cypress` or `cy` commands in this function, but you do have the direct access to the file system and the rest of the operating system.\n\n⚠️ When running via `cypress open`, the `after:spec` event only fires if the [experimentalInteractiveRunEvents flag](/llm/markdown/app/references/configuration.md#Experiments) is enabled.\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\n\nmodule.exports = defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on('after:spec', (spec, results) => {\n        /* ... */\n      })\n    },\n  },\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\n\nexport default defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on('after:spec', (spec, results) => {\n        /* ... */\n      })\n    },\n  },\n})\n```\n\n**spec _(Object)_**\n\nDetails of the spec file, including the following properties:\n\n| Property | Description |\n| --- | --- |\n| `name` | The base name of the spec file (e.g. `login.cy.js`) |\n| `relative` | The path to the spec file, relative to the project root (e.g. `cypress/e2e/login.cy.js`) |\n| `absolute` | The absolute path to the spec file (e.g. `/Users/janelane/my-app/cypress/e2e/login.cy.js`) |\n\n**results _(Object)_**\n\nDetails of the spec file's results, including numbers of passes/failures/etc and details on the tests themselves.\n\nResults are only provided when running via `cypress run`. When running via `cypress open`, the results will be undefined.\n",
      "section": "api",
      "anchors": [
        "syntax"
      ],
      "path": "/llm/json/chunked/api/node-events/after-spec-api.json",
      "token_estimate": 333
    },
    {
      "id": "api/node-events/after-spec-api#usage",
      "doc_id": "api/node-events/after-spec-api",
      "heading": "Usage",
      "heading_level": 2,
      "content_markdown": "## Usage\n\nYou can return a promise from the `after:spec` event handler and it will be awaited before Cypress proceeds with processing the spec's video or moving on to further specs if there are any.\n\n### Log the relative spec path to stdout after the spec is run\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\n\nmodule.exports = defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on('after:spec', (spec, results) => {\n        // spec will look something like this:\n        // {\n        //   name: 'login.cy.js',\n        //   relative: 'cypress/e2e/login.cy.js',\n        //   absolute: '/Users/janelane/my-app/cypress/e2e/login.cy.js',\n        // }\n        // results will look something like this:\n        // {\n        //   stats: {\n        //     suites: 0,\n        //     tests: 1,\n        //     passes: 1,\n        //     pending: 0,\n        //     skipped: 0,\n        //     failures: 0,\n        //     // ...more properties\n        //   }\n        //   reporter: 'spec',\n        //   tests: [\n        //     {\n        //       title: ['login', 'logs user in'],\n        //       state: 'passed',\n        //       // ...more properties...\n        //     }\n        //   ],\n        //   error: null,\n        //   video: '/Users/janelane/my-app/cypress/videos/login.cy.js.mp4',\n        //   screenshots: [],\n        //   // ...more properties...\n        // }\n        console.log('Finished running', spec.relative)\n      })\n    },\n  },\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\n\nexport default defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on('after:spec', (spec, results) => {\n        // spec will look something like this:\n        // {\n        //   name: 'login.cy.js',\n        //   relative: 'cypress/e2e/login.cy.js',\n        //   absolute: '/Users/janelane/my-app/cypress/e2e/login.cy.js',\n        // }\n        // results will look something like this:\n        // {\n        //   stats: {\n        //     suites: 0,\n        //     tests: 1,\n        //     passes: 1,\n        //     pending: 0,\n        //     skipped: 0,\n        //     failures: 0,\n        //     // ...more properties\n        //   }\n        //   reporter: 'spec',\n        //   tests: [\n        //     {\n        //       title: ['login', 'logs user in'],\n        //       state: 'passed',\n        //       // ...more properties...\n        //     }\n        //   ],\n        //   error: null,\n        //   video: '/Users/janelane/my-app/cypress/videos/login.cy.js.mp4',\n        //   screenshots: [],\n        //   // ...more properties...\n        // }\n        console.log('Finished running', spec.relative)\n      })\n    },\n  },\n})\n```\n",
      "section": "api",
      "anchors": [
        "usage"
      ],
      "path": "/llm/json/chunked/api/node-events/after-spec-api.json",
      "token_estimate": 435
    },
    {
      "id": "api/node-events/after-spec-api#log-the-relative-spec-path-to-stdout-after-the-spec-is-run",
      "doc_id": "api/node-events/after-spec-api",
      "heading": "Log the relative spec path to stdout after the spec is run",
      "heading_level": 3,
      "content_markdown": "### Log the relative spec path to stdout after the spec is run\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\n\nmodule.exports = defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on('after:spec', (spec, results) => {\n        // spec will look something like this:\n        // {\n        //   name: 'login.cy.js',\n        //   relative: 'cypress/e2e/login.cy.js',\n        //   absolute: '/Users/janelane/my-app/cypress/e2e/login.cy.js',\n        // }\n        // results will look something like this:\n        // {\n        //   stats: {\n        //     suites: 0,\n        //     tests: 1,\n        //     passes: 1,\n        //     pending: 0,\n        //     skipped: 0,\n        //     failures: 0,\n        //     // ...more properties\n        //   }\n        //   reporter: 'spec',\n        //   tests: [\n        //     {\n        //       title: ['login', 'logs user in'],\n        //       state: 'passed',\n        //       // ...more properties...\n        //     }\n        //   ],\n        //   error: null,\n        //   video: '/Users/janelane/my-app/cypress/videos/login.cy.js.mp4',\n        //   screenshots: [],\n        //   // ...more properties...\n        // }\n        console.log('Finished running', spec.relative)\n      })\n    },\n  },\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\n\nexport default defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on('after:spec', (spec, results) => {\n        // spec will look something like this:\n        // {\n        //   name: 'login.cy.js',\n        //   relative: 'cypress/e2e/login.cy.js',\n        //   absolute: '/Users/janelane/my-app/cypress/e2e/login.cy.js',\n        // }\n        // results will look something like this:\n        // {\n        //   stats: {\n        //     suites: 0,\n        //     tests: 1,\n        //     passes: 1,\n        //     pending: 0,\n        //     skipped: 0,\n        //     failures: 0,\n        //     // ...more properties\n        //   }\n        //   reporter: 'spec',\n        //   tests: [\n        //     {\n        //       title: ['login', 'logs user in'],\n        //       state: 'passed',\n        //       // ...more properties...\n        //     }\n        //   ],\n        //   error: null,\n        //   video: '/Users/janelane/my-app/cypress/videos/login.cy.js.mp4',\n        //   screenshots: [],\n        //   // ...more properties...\n        // }\n        console.log('Finished running', spec.relative)\n      })\n    },\n  },\n})\n```\n",
      "section": "api",
      "anchors": [
        "log-the-relative-spec-path-to-stdout-after-the-spec-is-run"
      ],
      "path": "/llm/json/chunked/api/node-events/after-spec-api.json",
      "token_estimate": 388
    },
    {
      "id": "api/node-events/after-spec-api#examples",
      "doc_id": "api/node-events/after-spec-api",
      "heading": "Examples",
      "heading_level": 2,
      "content_markdown": "## Examples\n\n### Delete the recorded video if the spec passed\n\nYou can delete the recorded video for a spec when certain conditions are met. This will skip the compression and uploading of the video when recording to Cypress Cloud.\n\nThe example below shows how to delete the recorded video for specs with no failing tests.\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\nconst fs = require('fs')\n\nmodule.exports = defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on('after:spec', (spec, results) => {\n        // Do we have failures?\n        if (results && results.video && results.stats.failures === 0) {\n          // delete the video if the spec passed\n          fs.unlinkSync(results.video)\n        }\n      })\n    },\n  },\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\nimport fs from 'fs'\n\nexport default defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on(\n        'after:spec',\n        (spec: Cypress.Spec, results: CypressCommandLine.RunResult) => {\n          // Do we have failures?\n          if (results && results.video && results.stats.failures === 0) {\n            // delete the video if the spec passed\n            fs.unlinkSync(results.video)\n          }\n        }\n      )\n    },\n  },\n})\n```\n\n### Delete the recorded video if no tests retried\n\nYou can delete the recorded video for a spec when certain conditions are met. This will skip the compression and uploading of the video when recording to Cypress Cloud.\n\nThe example below shows how to delete the recorded video for specs that had no retry attempts when using Cypress [test retries](/llm/markdown/app/guides/test-retries.md).\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\nconst fs = require('fs')\n\nmodule.exports = defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on('after:spec', (spec, results) => {\n        if (results && results.video) {\n          // Do we have failures for any retry attempts?\n          const failures = results.tests.some((test) =>\n            test.attempts.some((attempt) => attempt.state === 'failed')\n          )\n          if (!failures) {\n            // delete the video if the spec passed and no tests retried\n            fs.unlinkSync(results.video)\n          }\n        }\n      })\n    },\n  },\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\nimport fs from 'fs'\n\nexport default defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on(\n        'after:spec',\n        (spec: Cypress.Spec, results: CypressCommandLine.RunResult) => {\n          if (results && results.video) {\n            // Do we have failures for any retry attempts?\n            const failures = results.tests.some((test) =>\n              test.attempts.some((attempt) => attempt.state === 'failed')\n            )\n            if (!failures) {\n              // delete the video if the spec passed and no tests retried\n              fs.unlinkSync(results.video)\n            }\n          }\n        }\n      )\n    },\n  },\n})\n```\n",
      "section": "api",
      "anchors": [
        "examples"
      ],
      "path": "/llm/json/chunked/api/node-events/after-spec-api.json",
      "token_estimate": 579
    },
    {
      "id": "api/node-events/after-spec-api#delete-the-recorded-video-if-the-spec-passed",
      "doc_id": "api/node-events/after-spec-api",
      "heading": "Delete the recorded video if the spec passed",
      "heading_level": 3,
      "content_markdown": "### Delete the recorded video if the spec passed\n\nYou can delete the recorded video for a spec when certain conditions are met. This will skip the compression and uploading of the video when recording to Cypress Cloud.\n\nThe example below shows how to delete the recorded video for specs with no failing tests.\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\nconst fs = require('fs')\n\nmodule.exports = defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on('after:spec', (spec, results) => {\n        // Do we have failures?\n        if (results && results.video && results.stats.failures === 0) {\n          // delete the video if the spec passed\n          fs.unlinkSync(results.video)\n        }\n      })\n    },\n  },\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\nimport fs from 'fs'\n\nexport default defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on(\n        'after:spec',\n        (spec: Cypress.Spec, results: CypressCommandLine.RunResult) => {\n          // Do we have failures?\n          if (results && results.video && results.stats.failures === 0) {\n            // delete the video if the spec passed\n            fs.unlinkSync(results.video)\n          }\n        }\n      )\n    },\n  },\n})\n```\n",
      "section": "api",
      "anchors": [
        "delete-the-recorded-video-if-the-spec-passed"
      ],
      "path": "/llm/json/chunked/api/node-events/after-spec-api.json",
      "token_estimate": 259
    },
    {
      "id": "api/node-events/after-spec-api#delete-the-recorded-video-if-no-tests-retried",
      "doc_id": "api/node-events/after-spec-api",
      "heading": "Delete the recorded video if no tests retried",
      "heading_level": 3,
      "content_markdown": "### Delete the recorded video if no tests retried\n\nYou can delete the recorded video for a spec when certain conditions are met. This will skip the compression and uploading of the video when recording to Cypress Cloud.\n\nThe example below shows how to delete the recorded video for specs that had no retry attempts when using Cypress [test retries](/llm/markdown/app/guides/test-retries.md).\n\n*   cypress.config.js\n*   cypress.config.ts\n\n```\nconst { defineConfig } = require('cypress')\nconst fs = require('fs')\n\nmodule.exports = defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on('after:spec', (spec, results) => {\n        if (results && results.video) {\n          // Do we have failures for any retry attempts?\n          const failures = results.tests.some((test) =>\n            test.attempts.some((attempt) => attempt.state === 'failed')\n          )\n          if (!failures) {\n            // delete the video if the spec passed and no tests retried\n            fs.unlinkSync(results.video)\n          }\n        }\n      })\n    },\n  },\n})\n```\n\n```\nimport { defineConfig } from 'cypress'\nimport fs from 'fs'\n\nexport default defineConfig({\n  // setupNodeEvents can be defined in either\n  // the e2e or component configuration\n  e2e: {\n    setupNodeEvents(on, config) {\n      on(\n        'after:spec',\n        (spec: Cypress.Spec, results: CypressCommandLine.RunResult) => {\n          if (results && results.video) {\n            // Do we have failures for any retry attempts?\n            const failures = results.tests.some((test) =>\n              test.attempts.some((attempt) => attempt.state === 'failed')\n            )\n            if (!failures) {\n              // delete the video if the spec passed and no tests retried\n              fs.unlinkSync(results.video)\n            }\n          }\n        }\n      )\n    },\n  },\n})\n```\n",
      "section": "api",
      "anchors": [
        "delete-the-recorded-video-if-no-tests-retried"
      ],
      "path": "/llm/json/chunked/api/node-events/after-spec-api.json",
      "token_estimate": 317
    }
  ]
}