From 94a02c3c0209283a721fc81ce458b31d003eb155 Mon Sep 17 00:00:00 2001 From: Jonas Amundsen Date: Wed, 10 May 2023 12:39:00 +0200 Subject: [PATCH] Add support for CT using Vite as bundler This currently relies on a small patch of @cypress/vite-dev-server [1]. Hopefully this patch can be submitted and accepted upstream. This fixes #698 [2]. [2] https://github.com/cypress-io/cypress/tree/develop/npm/vite-dev-server [1] https://github.com/badeball/cypress-cucumber-preprocessor/issues/698 --- CHANGELOG.md | 2 ++ examples/ct-vite-react-ts/.npmrc | 1 + examples/ct-vite-react-ts/cypress.config.ts | 32 +++++++++++++++++++ .../cypress/component/App.feature | 4 +++ .../cypress/component/App.tsx | 15 +++++++++ .../cypress/support/component-index.html | 14 ++++++++ .../cypress/support/component.ts | 0 examples/ct-vite-react-ts/package.json | 16 ++++++++++ .../@cypress+vite-dev-server+5.0.5.patch | 13 ++++++++ examples/ct-vite-react-ts/src/App.tsx | 3 ++ examples/ct-vite-react-ts/tsconfig.json | 7 ++++ examples/readme.md | 6 +++- lib/bundler-utils/rollup.ts | 21 ++++++++++++ lib/helpers/prepare-registry.ts | 10 ++++++ lib/template.ts | 18 ++++------- package.json | 1 + 16 files changed, 151 insertions(+), 12 deletions(-) create mode 100644 examples/ct-vite-react-ts/.npmrc create mode 100644 examples/ct-vite-react-ts/cypress.config.ts create mode 100644 examples/ct-vite-react-ts/cypress/component/App.feature create mode 100644 examples/ct-vite-react-ts/cypress/component/App.tsx create mode 100644 examples/ct-vite-react-ts/cypress/support/component-index.html create mode 100644 examples/ct-vite-react-ts/cypress/support/component.ts create mode 100644 examples/ct-vite-react-ts/package.json create mode 100644 examples/ct-vite-react-ts/patches/@cypress+vite-dev-server+5.0.5.patch create mode 100644 examples/ct-vite-react-ts/src/App.tsx create mode 100644 examples/ct-vite-react-ts/tsconfig.json create mode 100644 lib/bundler-utils/rollup.ts create mode 100644 lib/helpers/prepare-registry.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e77c873..51cffdde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file. - Add support for (testing) type-specific configuration, fixes [#700](https://github.com/badeball/cypress-cucumber-preprocessor/issues/700). +- Add support for component testing using Vite as bundler, fixes [#698](https://github.com/badeball/cypress-cucumber-preprocessor/issues/698). + ## v17.0.0 Breaking changes: diff --git a/examples/ct-vite-react-ts/.npmrc b/examples/ct-vite-react-ts/.npmrc new file mode 100644 index 00000000..43c97e71 --- /dev/null +++ b/examples/ct-vite-react-ts/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/examples/ct-vite-react-ts/cypress.config.ts b/examples/ct-vite-react-ts/cypress.config.ts new file mode 100644 index 00000000..b331fb84 --- /dev/null +++ b/examples/ct-vite-react-ts/cypress.config.ts @@ -0,0 +1,32 @@ +import { defineConfig } from "cypress"; +import { devServer } from "@cypress/vite-dev-server"; +import react from "@vitejs/plugin-react"; +import { viteCommonjs } from "@originjs/vite-plugin-commonjs"; +import { addCucumberPreprocessorPlugin } from "@badeball/cypress-cucumber-preprocessor"; +import { createRollupPlugin } from "@badeball/cypress-cucumber-preprocessor/rollup"; + +export default defineConfig({ + component: { + specPattern: "**/*.feature", + devServer(devServerConfig) { + return devServer({ + ...devServerConfig, + framework: "react", + viteConfig: { + plugins: [ + react(), + createRollupPlugin(devServerConfig.cypressConfig), + viteCommonjs(), + ], + }, + }); + }, + async setupNodeEvents(on, config) { + // This is required for the preprocessor to be able to generate JSON reports after each run, and more. + await addCucumberPreprocessorPlugin(on, config); + + // Make sure to return the config object as it might have been modified by the plugin. + return config; + }, + }, +}); diff --git a/examples/ct-vite-react-ts/cypress/component/App.feature b/examples/ct-vite-react-ts/cypress/component/App.feature new file mode 100644 index 00000000..f9382e1c --- /dev/null +++ b/examples/ct-vite-react-ts/cypress/component/App.feature @@ -0,0 +1,4 @@ +Feature: App + Scenario: render + Given I render the component + Then I should see the text "Hello world!" diff --git a/examples/ct-vite-react-ts/cypress/component/App.tsx b/examples/ct-vite-react-ts/cypress/component/App.tsx new file mode 100644 index 00000000..61a57f94 --- /dev/null +++ b/examples/ct-vite-react-ts/cypress/component/App.tsx @@ -0,0 +1,15 @@ +import * as preprocessor from "@badeball/cypress-cucumber-preprocessor"; + +import { mount } from "cypress/react18" + +import App from "../../src/App"; + +const { Given, Then } = preprocessor + +Given("I render the component", () => { + mount(); +}); + +Then("I should see the text {string}", (text: string) => { + cy.contains(text).should("exist"); +}); diff --git a/examples/ct-vite-react-ts/cypress/support/component-index.html b/examples/ct-vite-react-ts/cypress/support/component-index.html new file mode 100644 index 00000000..1af394f6 --- /dev/null +++ b/examples/ct-vite-react-ts/cypress/support/component-index.html @@ -0,0 +1,14 @@ + + + + + + + Components App + + + + +
+ + diff --git a/examples/ct-vite-react-ts/cypress/support/component.ts b/examples/ct-vite-react-ts/cypress/support/component.ts new file mode 100644 index 00000000..e69de29b diff --git a/examples/ct-vite-react-ts/package.json b/examples/ct-vite-react-ts/package.json new file mode 100644 index 00000000..1941809c --- /dev/null +++ b/examples/ct-vite-react-ts/package.json @@ -0,0 +1,16 @@ +{ + "scripts": { + "postinstall": "patch-package" + }, + "dependencies": { + "@badeball/cypress-cucumber-preprocessor": "latest", + "@cypress/vite-dev-server": "latest", + "@originjs/vite-plugin-commonjs": "latest", + "@vitejs/plugin-react": "latest", + "cypress": "latest", + "patch-package": "latest", + "react": "latest", + "react-dom": "latest", + "typescript": "latest" + } +} diff --git a/examples/ct-vite-react-ts/patches/@cypress+vite-dev-server+5.0.5.patch b/examples/ct-vite-react-ts/patches/@cypress+vite-dev-server+5.0.5.patch new file mode 100644 index 00000000..532c84b1 --- /dev/null +++ b/examples/ct-vite-react-ts/patches/@cypress+vite-dev-server+5.0.5.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/@cypress/vite-dev-server/client/initCypressTests.js b/node_modules/@cypress/vite-dev-server/client/initCypressTests.js +index f0602c1..d7f05a0 100644 +--- a/node_modules/@cypress/vite-dev-server/client/initCypressTests.js ++++ b/node_modules/@cypress/vite-dev-server/client/initCypressTests.js +@@ -36,7 +36,7 @@ if (supportFile) { + // So we use the "@fs" bit to load the test file using its absolute path + // Normalize path to not include a leading slash (different on Win32 vs Unix) + const normalizedAbsolutePath = CypressInstance.spec.absolute.replace(/^\//, '') +-const testFileAbsolutePathRoute = `${devServerPublicPathRoute}/@fs/${normalizedAbsolutePath}` ++const testFileAbsolutePathRoute = `${devServerPublicPathRoute}/@fs/${normalizedAbsolutePath}?import` + + /* Spec file import logic */ + // We need a slash before /src/my-spec.js, this does not happen by default. diff --git a/examples/ct-vite-react-ts/src/App.tsx b/examples/ct-vite-react-ts/src/App.tsx new file mode 100644 index 00000000..36f31293 --- /dev/null +++ b/examples/ct-vite-react-ts/src/App.tsx @@ -0,0 +1,3 @@ +export default function App () { + return

Hello world!

+} diff --git a/examples/ct-vite-react-ts/tsconfig.json b/examples/ct-vite-react-ts/tsconfig.json new file mode 100644 index 00000000..61ea7288 --- /dev/null +++ b/examples/ct-vite-react-ts/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "moduleResolution": "node16", + "jsx": "react-jsx" + } +} diff --git a/examples/readme.md b/examples/readme.md index 825f0375..d3a02ee1 100644 --- a/examples/readme.md +++ b/examples/readme.md @@ -14,8 +14,12 @@ The examples illustrates using each bundler in each language flavor. ## Component testing -Only a single example illustrating component testing exist so far. +Component testing works with both Webpack and Vite[^1] as a bundler. | | CJS | ESM | TS | |-----------------|------------------------|------------------------|-----------------------------| | React + Webpack | | | [Link](ct-webpack-react-ts) | +| React + Vite | | | [Link](ct-vite-react-ts) | + +[patch-package]: https://github.com/ds300/patch-package +[^1]: Using Vite requires patching `@cypress/vite-dev-server`, something which is easily achieved using [`patch-package`][patch-package] as the example illustrates. diff --git a/lib/bundler-utils/rollup.ts b/lib/bundler-utils/rollup.ts new file mode 100644 index 00000000..0a39d0c8 --- /dev/null +++ b/lib/bundler-utils/rollup.ts @@ -0,0 +1,21 @@ +import { Plugin } from "rollup"; + +import { ICypressConfiguration } from "@badeball/cypress-configuration"; + +import { compile } from "../template"; + +export function createRollupPlugin(config: ICypressConfiguration): Plugin { + return { + name: "transform-feature", + async transform(src: string, id: string) { + if (/\.feature$/.test(id)) { + return { + code: await compile(config, src, id), + map: null, + }; + } + }, + }; +} + +export default createRollupPlugin; diff --git a/lib/helpers/prepare-registry.ts b/lib/helpers/prepare-registry.ts new file mode 100644 index 00000000..e5752769 --- /dev/null +++ b/lib/helpers/prepare-registry.ts @@ -0,0 +1,10 @@ +import { Registry, assignRegistry, freeRegistry } from "../registry"; + +const registry = new Registry(false); + +assignRegistry(registry); + +export function getAndFreeRegistry() { + freeRegistry(); + return registry; +} diff --git a/lib/template.ts b/lib/template.ts index 4b75c42b..778eacd7 100644 --- a/lib/template.ts +++ b/lib/template.ts @@ -131,7 +131,7 @@ export async function compile( const createTestsPath = prepareLibPath("browser-runtime"); - const registryPath = prepareLibPath("registry"); + const prepareRegistryPath = prepareLibPath("helpers", "prepare-registry"); const ensureRelativeToProjectRoot = (path: string) => ensureIsRelative(configuration.projectRoot, path); @@ -152,17 +152,13 @@ export async function compile( ]; return ` + const { getAndFreeRegistry } = require(${prepareRegistryPath}); const { default: createTests } = require(${createTestsPath}); - const { withRegistry } = require(${registryPath}); - - const registry = withRegistry( - false, - () => { - ${stepDefinitionPaths - .map((stepDefintion) => `require(${stringify(stepDefintion)});`) - .join("\n ")} - } - ); + ${stepDefinitionPaths + .map((stepDefintion) => `require(${stringify(stepDefintion)});`) + .join("\n ")} + + const registry = getAndFreeRegistry(); registry.finalize(); diff --git a/package.json b/package.json index 504027ed..cd91e686 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "pngjs": "^7.0.0", "prettier": "^2.8.8", "recast": "^0.22.0", + "rollup": "^3.21.5", "stream-buffers": "^3.0.2", "strip-ansi": "^6.0.1", "strip-indent": "^3.0.0",