diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index 00adf40f7230..9f8c7c1c0c0c 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -2134,13 +2134,6 @@ jobs: path: npm/grep/test_results - store-npm-logs - npm-create-cypress-tests: - <<: *defaults - resource_class: small - steps: - - restore_cached_workspace - - run: yarn lerna run build --scope create-cypress-tests - npm-eslint-plugin-dev: <<: *defaults steps: @@ -2798,9 +2791,6 @@ linux-x64-workflow: &linux-x64-workflow - npm-mount-utils: requires: - build - - npm-create-cypress-tests: - requires: - - build - npm-eslint-plugin-dev: requires: - build @@ -2820,7 +2810,6 @@ linux-x64-workflow: &linux-x64-workflow - check-ts - npm-angular - npm-eslint-plugin-dev - - npm-create-cypress-tests - npm-react - npm-mount-utils - npm-vue @@ -2876,7 +2865,6 @@ linux-x64-workflow: &linux-x64-workflow - check-ts - npm-angular - npm-eslint-plugin-dev - - npm-create-cypress-tests - npm-react - npm-mount-utils - npm-vue @@ -3194,9 +3182,6 @@ linux-x64-contributor-workflow: &linux-x64-contributor-workflow - npm-mount-utils: requires: - build - - npm-create-cypress-tests: - requires: - - build - npm-eslint-plugin-dev: requires: - build @@ -3216,7 +3201,6 @@ linux-x64-contributor-workflow: &linux-x64-contributor-workflow - check-ts - npm-angular - npm-eslint-plugin-dev - - npm-create-cypress-tests - npm-puppeteer-unit-tests - npm-puppeteer-cypress-tests - npm-react @@ -3272,7 +3256,6 @@ linux-x64-contributor-workflow: &linux-x64-contributor-workflow - check-ts - npm-angular - npm-eslint-plugin-dev - - npm-create-cypress-tests - npm-react - npm-mount-utils - npm-vue diff --git a/.eslintrc.js b/.eslintrc.js index 0f6bc16d6bd2..999d42521ef8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -53,8 +53,6 @@ module.exports = { 'tooling/**', 'packages/{app,driver,frontend-shared,launchpad}/cypress/**', '*.test.ts', - // ignore in packages that don't run in the Cypress process - 'npm/create-cypress-tests/**', ], rules: { 'no-restricted-properties': 'off', diff --git a/.gitignore b/.gitignore index b6583eab0c39..cb6f213a1c87 100644 --- a/.gitignore +++ b/.gitignore @@ -92,7 +92,7 @@ system-tests/lib/fixtureDirs.ts /packages/frontend-shared/src/generated /packages/frontend-shared/cypress/e2e/support/e2eProjectDirs.ts -# from npm/create-cypress-tests +# from old npm/create-cypress-tests /npm/create-cypress-tests/initial-template /npm/create-cypress-tests/src/test-output diff --git a/CHANGELOG.md b/CHANGELOG.md index becc1f915c87..be435cb6b72d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,6 @@ - [Cypress App](https://on.cypress.io/changelog) - [`@cypress/angular`](https://github.com/cypress-io/cypress/blob/develop/npm/angular/CHANGELOG.md) -- [`@cypress/create-cypress-tests`](https://github.com/cypress-io/cypress/blob/develop/npm/create-cypress-tests/CHANGELOG.md) - [`@cypress/eslint-plugin-dev`](https://github.com/cypress-io/cypress/blob/develop/npm/eslint-plugin-dev/CHANGELOG.md) - [`@cypress/mount-utils`](https://github.com/cypress-io/cypress/blob/develop/npm/mount-utils/CHANGELOG.md) - [`@cypress/react`](https://github.com/cypress-io/cypress/blob/develop/npm/react/CHANGELOG.md) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index db769e1eedd8..9acb46686376 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -183,7 +183,6 @@ Here is a list of the npm packages in this repository: | Folder Name | Package Name | Purpose | | :----------------------------------------------------- | :--------------------------------- | :--------------------------------------------------------------------------- | | [angular](./npm/angular) | `@cypress/angular` | Cypress component testing for Angular. | - | [create-cypress-tests](./npm/create-cypress-tests) | `@cypress/create-cypress-tests` | Tooling to scaffold Cypress configuration and demo test files. | | [eslint-plugin-dev](./npm/eslint-plugin-dev) | `@cypress/eslint-plugin-dev` | Eslint plugin for internal development. | | [grep](./npm/grep) | `@cypress/grep` | Filter tests using substring | | [mount-utils](./npm/mount-utils) | `@cypress/mount-utils` | Common functionality for Vue/React/Angular adapters. | diff --git a/npm/create-cypress-tests/.eslintignore b/npm/create-cypress-tests/.eslintignore deleted file mode 100644 index 341078756065..000000000000 --- a/npm/create-cypress-tests/.eslintignore +++ /dev/null @@ -1,9 +0,0 @@ -**/dist -**/*.d.ts -**/package-lock.json -**/tsconfig.json -**/cypress/fixtures -**/test/fixtures -**/__snapshots__ -/initial-template -/**/*.template.* \ No newline at end of file diff --git a/npm/create-cypress-tests/.eslintrc b/npm/create-cypress-tests/.eslintrc deleted file mode 100644 index d1ad4a04ebbf..000000000000 --- a/npm/create-cypress-tests/.eslintrc +++ /dev/null @@ -1,16 +0,0 @@ -{ - "plugins": [ - "cypress", - "@cypress/dev" - ], - "extends": [ - "plugin:@cypress/dev/general", - "plugin:@cypress/dev/tests" - ], - "env": { - "cypress/globals": true - }, - "rules": { - "no-console": "off" - } -} diff --git a/npm/create-cypress-tests/.mocharc.json b/npm/create-cypress-tests/.mocharc.json deleted file mode 100644 index 05d782fced5a..000000000000 --- a/npm/create-cypress-tests/.mocharc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "watch-ignore": [ - "node_modules" - ], - "require": "ts-node/register", - "exit": true -} diff --git a/npm/create-cypress-tests/.npmignore b/npm/create-cypress-tests/.npmignore deleted file mode 100644 index c9cfa44194a1..000000000000 --- a/npm/create-cypress-tests/.npmignore +++ /dev/null @@ -1,4 +0,0 @@ -./src/ -./initial-template/ -scripts/ -__snapshots__/ \ No newline at end of file diff --git a/npm/create-cypress-tests/CHANGELOG.md b/npm/create-cypress-tests/CHANGELOG.md deleted file mode 100644 index 720d4a52aab4..000000000000 --- a/npm/create-cypress-tests/CHANGELOG.md +++ /dev/null @@ -1,118 +0,0 @@ -# [create-cypress-tests-v2.0.4](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v2.0.3...create-cypress-tests-v2.0.4) (2023-10-16) - -# [create-cypress-tests-v2.0.3](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v2.0.2...create-cypress-tests-v2.0.3) (2023-09-07) - -# [create-cypress-tests-v2.0.2](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v2.0.1...create-cypress-tests-v2.0.2) (2023-04-07) - -# [create-cypress-tests-v2.0.1](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v2.0.0...create-cypress-tests-v2.0.1) (2023-01-03) - - -### Bug Fixes - -* change wording for spec creation ([#25271](https://github.com/cypress-io/cypress/issues/25271)) ([c12a7e3](https://github.com/cypress-io/cypress/commit/c12a7e37c73d972eb0514e4b602940df210d86c7)) - -# [create-cypress-tests-v2.0.0](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v1.3.0...create-cypress-tests-v2.0.0) (2022-06-13) - - -### Bug Fixes - -* scope config to current testing type ([#20677](https://github.com/cypress-io/cypress/issues/20677)) ([61f7cfc](https://github.com/cypress-io/cypress/commit/61f7cfc59284a2938e0a1c15d74ee75215ba5f8b)) -* support using create-cypress-tests as part of build process ([#18714](https://github.com/cypress-io/cypress/issues/18714)) ([0501452](https://github.com/cypress-io/cypress/commit/0501452fb9e2df954ee871171052ab9f01367b25)) -* **unified-desktop-gui branch:** initial installation on windows ([#18247](https://github.com/cypress-io/cypress/issues/18247)) ([8614e97](https://github.com/cypress-io/cypress/commit/8614e978029bcbf7155b7ae98ac54feb11f2e7f3)) - - -### chore - -* prep npm packages for use with Cypress v10 ([b924d08](https://github.com/cypress-io/cypress/commit/b924d086ee2e2ccc93303731e001b2c9e9d0af17)) - - -### Features - -* Add vue2 package from npm/vue/v2 branch ([#21026](https://github.com/cypress-io/cypress/issues/21026)) ([3aa69e2](https://github.com/cypress-io/cypress/commit/3aa69e2538aae5702bfc48789c54f37263ce08fc)) -* Deprecate run-ct / open-ct, and update all examples to use --ct instead ([#18422](https://github.com/cypress-io/cypress/issues/18422)) ([196e8f6](https://github.com/cypress-io/cypress/commit/196e8f62cc6d27974f235945cb5700624b3dae41)) -* remove testFiles reference ([#20565](https://github.com/cypress-io/cypress/issues/20565)) ([5670344](https://github.com/cypress-io/cypress/commit/567034459089d9d53dfab5556cb9369fb335c3db)) -* update on-links ([#19235](https://github.com/cypress-io/cypress/issues/19235)) ([cc2d734](https://github.com/cypress-io/cypress/commit/cc2d7348185e2a090c60d92d9319ab460d8c7827)) -* Use .config files ([#18578](https://github.com/cypress-io/cypress/issues/18578)) ([081dd19](https://github.com/cypress-io/cypress/commit/081dd19cc6da3da229a7af9c84f62730c85a5cd6)) -* use supportFile by testingType ([#19364](https://github.com/cypress-io/cypress/issues/19364)) ([0366d4f](https://github.com/cypress-io/cypress/commit/0366d4fa8971e5e5189c6fd6450cc3c8d72dcfe1)) - - -### BREAKING CHANGES - -* new version of packages for Cypress v10 - -# [create-cypress-tests-v1.3.0](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v1.2.0...create-cypress-tests-v1.3.0) (2021-12-16) - - -### Bug Fixes - -* Restore broken gif ([#18987](https://github.com/cypress-io/cypress/issues/18987)) ([f251681](https://github.com/cypress-io/cypress/commit/f251681b814b102ca374abdef148b777c4e72c67)) - - -### Features - -* use hoisted yarn install in binary build ([#17285](https://github.com/cypress-io/cypress/issues/17285)) ([e4f5b10](https://github.com/cypress-io/cypress/commit/e4f5b106d49d6ac0857c5fdac886f83b99558c88)) - -# [create-cypress-tests-v1.2.0](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v1.1.3...create-cypress-tests-v1.2.0) (2021-11-10) - - -### Features - -* **deps:** update dependency electron to v15 🌟 ([#18317](https://github.com/cypress-io/cypress/issues/18317)) ([3095d73](https://github.com/cypress-io/cypress/commit/3095d733e92527ffd67344c6899211e058ceefa3)) - -# [create-cypress-tests-v1.1.3](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v1.1.2...create-cypress-tests-v1.1.3) (2021-10-29) - - -### Bug Fixes - -* revive type checker ([#18172](https://github.com/cypress-io/cypress/issues/18172)) ([af472b6](https://github.com/cypress-io/cypress/commit/af472b6419ecb2aec1abdb09df99b2fa5f56e033)) - -# [create-cypress-tests-v1.1.2](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v1.1.1...create-cypress-tests-v1.1.2) (2021-06-17) - - -### Bug Fixes - -* case issue create cypress tests with `react/plugins/load-webpack` ([#16961](https://github.com/cypress-io/cypress/issues/16961)) ([c37ecea](https://github.com/cypress-io/cypress/commit/c37ecea3ca462015637515b331d1c9828ac1ed29)), closes [#16960](https://github.com/cypress-io/cypress/issues/16960) - -# [create-cypress-tests-v1.1.1](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v1.1.0...create-cypress-tests-v1.1.1) (2021-05-10) - - -### Bug Fixes - -* add return config for vitejs templates ([69d9de5](https://github.com/cypress-io/cypress/commit/69d9de581a03dce8e3535917a4cdcea8fa4eb6e9)) -* add return config for vueCli and vueWebpack ([9c12ee6](https://github.com/cypress-io/cypress/commit/9c12ee6d8467c65414ab2d413a9c45b2bbec64e9)) -* remove all of rollup, not supported anymore ([f8a71e7](https://github.com/cypress-io/cypress/commit/f8a71e75ae8208dc628d342cb1054c12f98338e9)) -* typo in the final message (run vs run-ct) ([294db04](https://github.com/cypress-io/cypress/commit/294db04f042dba86b69bb15d847c80a2c4202e80)) -* vueCli and webpack key vue@2 fix when guessing ([89f1bb9](https://github.com/cypress-io/cypress/commit/89f1bb9bc6bd987fbf6679a9d955c3587e69aa61)) - -# [create-cypress-tests-v1.1.0](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v1.0.1...create-cypress-tests-v1.1.0) (2021-04-05) - - -### Bug Fixes - -* **component-testing:** Fix webpack-dev-server deps validation crash ([#15708](https://github.com/cypress-io/cypress/issues/15708)) ([254eb47](https://github.com/cypress-io/cypress/commit/254eb47d91c75a9f56162e7493ab83e5be169935)) - - -### Features - -* support ct/e2e specific overrides in cypress.json ([#15526](https://github.com/cypress-io/cypress/issues/15526)) ([43c8ae2](https://github.com/cypress-io/cypress/commit/43c8ae2a7c20ba70a0bb0b45b8f6a086e2782f29)) - -# [create-cypress-tests-v1.0.1](https://github.com/cypress-io/cypress/compare/create-cypress-tests-v1.0.0...create-cypress-tests-v1.0.1) (2021-03-16) - - -### Bug Fixes - -* add missing script for building wizard ([#15502](https://github.com/cypress-io/cypress/issues/15502)) ([393a8ca](https://github.com/cypress-io/cypress/commit/393a8ca9cac905e0f6d8623bff889b041dd076b6)) - -# create-cypress-tests-v1.0.0 (2021-03-15) - - -### Bug Fixes - -* **runner-ct:** open link in external browser ([#15420](https://github.com/cypress-io/cypress/issues/15420)) ([d291157](https://github.com/cypress-io/cypress/commit/d291157f07ffebe961527fdd85c7ec51056801e7)) - - -### Features - -* **@cypress/react:** Make correct plugins for different adapters/bundlers ([#15337](https://github.com/cypress-io/cypress/issues/15337)) ([fc30118](https://github.com/cypress-io/cypress/commit/fc301182523f0a645bfb17ea3b541644b9732dd0)), closes [#9116](https://github.com/cypress-io/cypress/issues/9116) -* create-cypress-tests installation wizard ([#9563](https://github.com/cypress-io/cypress/issues/9563)) ([c405ee8](https://github.com/cypress-io/cypress/commit/c405ee89ef5321df6151fdeec1e917ac952c0d38)), closes [#9116](https://github.com/cypress-io/cypress/issues/9116) -* create-cypress-tests wizard ([#8857](https://github.com/cypress-io/cypress/issues/8857)) ([21ee591](https://github.com/cypress-io/cypress/commit/21ee591d1e9c4083a0c67f2062ced92708c0cedd)) diff --git a/npm/create-cypress-tests/README.md b/npm/create-cypress-tests/README.md deleted file mode 100644 index 528987627d36..000000000000 --- a/npm/create-cypress-tests/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Create Cypress Tests - -Installs and injects all the required configuration to run cypress tests. - -## Quick overview - -``` -cd my-app -npx create-cypress-tests -npx cypress open -``` - -![demo](./demo.gif) - -## Package manager - -This wizard will automatically determine which package do you use. If `yarn` available as global dependency it will use yarn to install dependencies and create lock file. - -If you need to use `npm` over `yarn` you can do the following - -``` -npx create-cypress-tests --use-npm -``` - -By the way you can use yarn to run the installation wizard 😉 - -``` -yarn create cypress-tests -``` - -## Typescript - -This package will also automatically determine if typescript if available in this project and inject the required typescript configuration for cypress. If you are starting a new project and want to create typescript configuration, please do the following: - -``` -npm init -npm install typescript -npx create-cypress-tests -``` - -## Configuration - -Here is a list of available configuration options: - -`--use-npm` – use npm if yarn available -`--ignore-typescript` – will not create typescript configuration if available -`--ignore-examples` – will create a 1 template spec file (`cypress/integration/spec.js`) to start with -`--component-tests` – will not ask should setup component testing or not - -## License - -The project is licensed under the terms of [MIT license](../../LICENSE) - -## Changelog - -[Changelog](./CHANGELOG.md) diff --git a/npm/create-cypress-tests/__snapshots__/babel.test.ts.js b/npm/create-cypress-tests/__snapshots__/babel.test.ts.js deleted file mode 100644 index 182ba10a2fb4..000000000000 --- a/npm/create-cypress-tests/__snapshots__/babel.test.ts.js +++ /dev/null @@ -1,13 +0,0 @@ -exports['babel installation template correctly generates plugins config 1'] = ` -const injectDevServer = require('@cypress/react/plugins/babel'); - -const something = require("something"); - -module.exports = (on, config) => { - if (config.testingType === "component") { - injectDevServer(on, config); - } - - return config; // IMPORTANT to return a config -}; -` diff --git a/npm/create-cypress-tests/__snapshots__/init-component-testing.test.ts.js b/npm/create-cypress-tests/__snapshots__/init-component-testing.test.ts.js deleted file mode 100644 index f1ff8aa8b366..000000000000 --- a/npm/create-cypress-tests/__snapshots__/init-component-testing.test.ts.js +++ /dev/null @@ -1,43 +0,0 @@ -exports['injects guessed next.js template cypress.config.ts'] = ` -export default { - specPattern: "src/**/*.spec.{js,ts,jsx,tsx}" -}; - -` - -exports['injects guessed next.js template plugins/index.js'] = ` -const injectDevServer = require("@cypress/react/plugins/next"); - -module.exports = (on, config) => { - if (config.testingType === "component") { - injectDevServer(on, config); - } - - return config; // IMPORTANT to return a config -}; - -` - -exports['Injected overridden webpack template cypress.config.ts'] = ` -export default { - specPattern: "cypress/component/**/*.spec.{js,ts,jsx,tsx}" -}; - -` - -exports['Injected overridden webpack template plugins/index.js'] = ` -const injectDevServer = require("@cypress/react/plugins/react-scripts"); - -module.exports = (on, config) => { - if (config.testingType === "component") { - injectDevServer(on, config); - } - - return config; // IMPORTANT to return a config -}; - -` - -exports['Injected overridden webpack template support/component.js'] = ` -import "./commands.js"; -` diff --git a/npm/create-cypress-tests/__snapshots__/next.test.ts.js b/npm/create-cypress-tests/__snapshots__/next.test.ts.js deleted file mode 100644 index 5d8602de1bbd..000000000000 --- a/npm/create-cypress-tests/__snapshots__/next.test.ts.js +++ /dev/null @@ -1,13 +0,0 @@ -exports['next.js install template correctly generates plugins config 1'] = ` -const injectDevServer = require('@cypress/react/plugins/next'); - -const something = require("something"); - -module.exports = (on, config) => { - if (config.testingType === "component") { - injectDevServer(on, config); - } - - return config; // IMPORTANT to return a config -}; -` diff --git a/npm/create-cypress-tests/__snapshots__/react-scripts.test.ts.js b/npm/create-cypress-tests/__snapshots__/react-scripts.test.ts.js deleted file mode 100644 index b960b4be6f6a..000000000000 --- a/npm/create-cypress-tests/__snapshots__/react-scripts.test.ts.js +++ /dev/null @@ -1,13 +0,0 @@ -exports['create-react-app install template correctly generates plugins config 1'] = ` -const injectDevServer = require('@cypress/react/plugins/react-scripts'); - -const something = require("something"); - -module.exports = (on, config) => { - if (config.testingType === "component") { - injectDevServer(on, config); - } - - return config; // IMPORTANT to return a config -}; -` diff --git a/npm/create-cypress-tests/__snapshots__/reactWebpackFile.test.ts.js b/npm/create-cypress-tests/__snapshots__/reactWebpackFile.test.ts.js deleted file mode 100644 index 5e36dd80347c..000000000000 --- a/npm/create-cypress-tests/__snapshots__/reactWebpackFile.test.ts.js +++ /dev/null @@ -1,32 +0,0 @@ -exports['webpack-file install template correctly generates plugins config when webpack config path is missing 1'] = ` -const injectDevServer = require("@cypress/react/plugins/load-webpack"); - -const something = require("something"); - -module.exports = (on, config) => { - if (config.testingType === "component") { - injectDevServer(on, config, { - // TODO replace with valid webpack config path - webpackFilename: './webpack.config.js' - }); - } - - return config; // IMPORTANT to return a config -}; -` - -exports['webpack-file install template correctly generates plugins config when webpack config path is provided 1'] = ` -const injectDevServer = require("@cypress/react/plugins/load-webpack"); - -const something = require("something"); - -module.exports = (on, config) => { - if (config.testingType === "component") { - injectDevServer(on, config, { - webpackFilename: 'config/webpack.config.js' - }); - } - - return config; // IMPORTANT to return a config -}; -` diff --git a/npm/create-cypress-tests/__snapshots__/vite.test.ts.js b/npm/create-cypress-tests/__snapshots__/vite.test.ts.js deleted file mode 100644 index 5fc7bc31a4fb..000000000000 --- a/npm/create-cypress-tests/__snapshots__/vite.test.ts.js +++ /dev/null @@ -1,17 +0,0 @@ -exports['vue: vite template correctly generates plugins config 1'] = ` -const { - startDevServer -} = require("@cypress/vite-dev-server"); - -const something = require("something"); - -module.exports = (on, config) => { - if (config.testingType === "component") { - on("dev-server:start", async options => startDevServer({ - options - })); - } - - return config; // IMPORTANT to return a config -}; -` diff --git a/npm/create-cypress-tests/__snapshots__/vueCli.test.ts.js b/npm/create-cypress-tests/__snapshots__/vueCli.test.ts.js deleted file mode 100644 index fa70c0ef2db7..000000000000 --- a/npm/create-cypress-tests/__snapshots__/vueCli.test.ts.js +++ /dev/null @@ -1,20 +0,0 @@ -exports['vue webpack-file install template correctly generates plugins for vue-cli-service 1'] = ` -const { - startDevServer -} = require("@cypress/webpack-dev-server"); - -const webpackConfig = require("@vue/cli-service/webpack.config.js"); - -const something = require("something"); - -module.exports = (on, config) => { - if (config.testingType === "component") { - on('dev-server:start', options => startDevServer({ - options, - webpackConfig - })); - } - - return config; // IMPORTANT to return a config -}; -` diff --git a/npm/create-cypress-tests/__snapshots__/vueWebpackFile.test.ts.js b/npm/create-cypress-tests/__snapshots__/vueWebpackFile.test.ts.js deleted file mode 100644 index b686b719276e..000000000000 --- a/npm/create-cypress-tests/__snapshots__/vueWebpackFile.test.ts.js +++ /dev/null @@ -1,42 +0,0 @@ -exports['vue webpack-file install template correctly generates plugins config when webpack config path is missing 1'] = ` -const { - startDevServer -} = require("@cypress/webpack-dev-server"); - -const webpackConfig = require("./webpack.config.js"); // TODO replace with valid webpack config path - - -const something = require("something"); - -module.exports = (on, config) => { - if (config.testingType === "component") { - on('dev-server:start', options => startDevServer({ - options, - webpackConfig - })); - } - - return config; // IMPORTANT to return a config -}; -` - -exports['vue webpack-file install template correctly generates plugins config when webpack config path is provided 1'] = ` -const { - startDevServer -} = require("@cypress/webpack-dev-server"); - -const webpackConfig = require("build/webpack.config.js"); - -const something = require("something"); - -module.exports = (on, config) => { - if (config.testingType === "component") { - on('dev-server:start', options => startDevServer({ - options, - webpackConfig - })); - } - - return config; // IMPORTANT to return a config -}; -` diff --git a/npm/create-cypress-tests/__snapshots__/webpackOptions.test.ts.js b/npm/create-cypress-tests/__snapshots__/webpackOptions.test.ts.js deleted file mode 100644 index f384e4b912a1..000000000000 --- a/npm/create-cypress-tests/__snapshots__/webpackOptions.test.ts.js +++ /dev/null @@ -1,40 +0,0 @@ -exports['webpack-options template correctly generates plugins config 1'] = ` -const path = require("path"); - -const { - startDevServer -} = require("@cypress/webpack-dev-Server"); - -const something = require("something"); - -module.exports = (on, config) => { - if (config.testingType === "component") { - /** @type import("webpack").Configuration */ - const webpackConfig = { - resolve: { - extensions: ['.js', '.ts', '.jsx', '.tsx'] - }, - mode: 'development', - devtool: false, - output: { - publicPath: '/', - chunkFilename: '[name].bundle.js' - }, - // TODO: update with valid configuration for your components - module: { - rules: [{ - test: /\\.(js|jsx|mjs|ts|tsx)$/, - loader: 'babel-loader', - options: { - cacheDirectory: path.resolve(__dirname, '.babel-cache') - } - }] - } - }; - on('dev-server:start', options => startDevServer({ - options, - webpackConfig - })); - } -}; -` diff --git a/npm/create-cypress-tests/cypress.config.js b/npm/create-cypress-tests/cypress.config.js deleted file mode 100644 index 4ba52ba2c8df..000000000000 --- a/npm/create-cypress-tests/cypress.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {} diff --git a/npm/create-cypress-tests/demo.gif b/npm/create-cypress-tests/demo.gif deleted file mode 100644 index 344fc47d3faa..000000000000 Binary files a/npm/create-cypress-tests/demo.gif and /dev/null differ diff --git a/npm/create-cypress-tests/package.json b/npm/create-cypress-tests/package.json deleted file mode 100644 index 695a20a2695a..000000000000 --- a/npm/create-cypress-tests/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "create-cypress-tests", - "version": "0.0.0-development", - "description": "Cypress smart installation wizard", - "main": "dist/src/main.js", - "scripts": { - "build": "yarn prepare-example && tsc -p ./tsconfig.json && node scripts/example copy-to ./dist/initial-template && yarn prepare-copy-templates", - "prepare-example": "node scripts/example copy-to ./initial-template", - "prepare-copy-templates": "node scripts/copy-templates copy-to ./dist/src", - "test": "cross-env TS_NODE_PROJECT=./tsconfig.test.json mocha --config .mocharc.json './src/**/*.test.ts'", - "test:watch": "yarn test -w", - "lint": "eslint --ext .js,.ts,.json, ." - }, - "dependencies": { - "@babel/core": "^7.5.4", - "@babel/plugin-transform-typescript": "^7.2.0", - "@babel/template": "^7.5.4", - "@babel/types": "^7.5.0", - "bluebird": "3.7.2", - "chalk": "4.1.0", - "cli-highlight": "2.1.10", - "commander": "6.2.1", - "find-up": "5.0.0", - "fs-extra": "^9.1.0", - "glob": "^7.1.6", - "inquirer": "8.2.4", - "ora": "^5.1.0", - "recast": "0.20.4", - "semver": "7.3.7" - }, - "devDependencies": { - "@types/babel__core": "^7.1.2", - "@types/inquirer": "8.2.4", - "@types/mock-fs": "4.10.0", - "@types/node": "18.17.5", - "@types/ora": "^3.2.0", - "@types/semver": "7.5.0", - "copy": "0.3.2", - "mocha": "7.1.1", - "mock-fs": "5.2.0", - "snap-shot-it": "7.9.3", - "typescript": "^4.7.4" - }, - "files": [ - "dist", - "bin" - ], - "bin": { - "create-cypress-tests": "dist/src/index.js" - }, - "license": "MIT", - "repository": "https://github.com/cypress-io/cypress.git", - "homepage": "https://github.com/cypress-io/cypress/blob/develop/npm/create-cypress-tests/#readme", - "nx": { - "implicitDependencies": [ - "@packages/example" - ] - } -} diff --git a/npm/create-cypress-tests/scripts/copy-templates.js b/npm/create-cypress-tests/scripts/copy-templates.js deleted file mode 100644 index 583538450087..000000000000 --- a/npm/create-cypress-tests/scripts/copy-templates.js +++ /dev/null @@ -1,40 +0,0 @@ -const globby = require('globby') -const fs = require('fs-extra') -const chalk = require('chalk') -const path = require('path') -const program = require('commander') - -program -.command('copy-to [destination]') -.description('copy ./src/**/*.template.js into destination') -.action(async (destination) => { - const srcPath = path.resolve(__dirname, '..', 'src') - const destinationPath = path.resolve(process.cwd(), destination) - - const templates = await globby('**/*.template.js', { - cwd: srcPath, - onlyFiles: true, - unique: true, - }) - - const srcOutput = './src/' - let destinationOutput = destination.replace('/\\/g', '/') - - if (!destinationOutput.endsWith('/')) { - destinationOutput += '/' - } - - const relOutput = (template, forSource) => { - return `${forSource ? srcOutput : destinationOutput}${template}` - } - - const result = await Promise.all(templates.map(async (template) => { - await fs.copy(path.join(srcPath, template), path.join(destinationPath, template)) - - return () => console.log(`✅ ${relOutput(template, true)} successfully copied to ${chalk.cyan(relOutput(template, false))}`) - })) - - result.forEach((r) => r()) -}) - -program.parse(process.argv) diff --git a/npm/create-cypress-tests/scripts/example.js b/npm/create-cypress-tests/scripts/example.js deleted file mode 100644 index 0925901447ab..000000000000 --- a/npm/create-cypress-tests/scripts/example.js +++ /dev/null @@ -1,27 +0,0 @@ -const fs = require('fs-extra') -const chalk = require('chalk') -const path = require('path') -const program = require('commander') - -program -.command('copy-to [destination]') -.description('copy cypress/packages/example into destination') -.action(async (destination) => { - const exampleFolder = path.resolve(__dirname, '..', '..', '..', 'packages', 'example', 'cypress') - const destinationPath = path.resolve(process.cwd(), destination) - - await fs.remove(destinationPath) - await fs.copy(exampleFolder, destinationPath, { recursive: true }) - - console.log(`✅ E2E Examples were successfully created at ${chalk.cyan(destination)}`) - - await fs.copy(path.join(__dirname, 'examples', 'cypress'), path.join(destination)) - - console.log(`✅ Cypress Setup was successfully created at ${chalk.cyan(destination)}`) - - await fs.copy(path.join(__dirname, 'examples', 'tsconfig.json'), path.join(destination, 'tsconfig.json')) - - console.log(`✅ tsconfig.json was created for ${chalk.cyan(destination)}`) -}) - -program.parse(process.argv) diff --git a/npm/create-cypress-tests/scripts/examples/cypress/fixtures/example.json b/npm/create-cypress-tests/scripts/examples/cypress/fixtures/example.json deleted file mode 100644 index 02e4254378e9..000000000000 --- a/npm/create-cypress-tests/scripts/examples/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} diff --git a/npm/create-cypress-tests/scripts/examples/cypress/support/commands.js b/npm/create-cypress-tests/scripts/examples/cypress/support/commands.js deleted file mode 100644 index 119ab03f7cda..000000000000 --- a/npm/create-cypress-tests/scripts/examples/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// 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) => { ... }) diff --git a/npm/create-cypress-tests/scripts/examples/cypress/support/component.js b/npm/create-cypress-tests/scripts/examples/cypress/support/component.js deleted file mode 100644 index 5e450a7b1a4a..000000000000 --- a/npm/create-cypress-tests/scripts/examples/cypress/support/component.js +++ /dev/null @@ -1,20 +0,0 @@ -// *********************************************************** -// This example support/component.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') diff --git a/npm/create-cypress-tests/scripts/examples/cypress/support/e2e.js b/npm/create-cypress-tests/scripts/examples/cypress/support/e2e.js deleted file mode 100644 index d1dd1353e812..000000000000 --- a/npm/create-cypress-tests/scripts/examples/cypress/support/e2e.js +++ /dev/null @@ -1,20 +0,0 @@ -// *********************************************************** -// This example support/e2e.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') diff --git a/npm/create-cypress-tests/scripts/examples/tsconfig.json b/npm/create-cypress-tests/scripts/examples/tsconfig.json deleted file mode 100644 index 08c029d44419..000000000000 --- a/npm/create-cypress-tests/scripts/examples/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "es5", - "dom" - ], - "types": [ - "cypress" - ] - }, - "include": [ - "**/*.ts*" - ] -} \ No newline at end of file diff --git a/npm/create-cypress-tests/src/component-testing/babel/babelTransform.test.ts b/npm/create-cypress-tests/src/component-testing/babel/babelTransform.test.ts deleted file mode 100644 index 5bc9e1ed33dc..000000000000 --- a/npm/create-cypress-tests/src/component-testing/babel/babelTransform.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -/// - -import * as babel from '@babel/core' -import { expect } from 'chai' -import { createTransformPluginsFileBabelPlugin } from './babelTransform' - -describe('babel transform utils', () => { - context('Plugins config babel plugin', () => { - it('injects code into the plugins file based on ast', () => { - const plugin = createTransformPluginsFileBabelPlugin({ - RequireAst: babel.template.ast('require("something")'), - IfComponentTestingPluginsAst: babel.template.ast('yey()'), - }) - - const output = babel.transformSync([ - 'module.exports = (on, config) => {', - 'on("do")', - '}', - ].join('\n'), { - plugins: [plugin], - })?.code - - expect(output).to.equal([ - 'require("something");', - '', - 'module.exports = (on, config) => {', - ' on("do");', - '', - ' if (config.testingType === "component") {', - ' yey();', - ' }', - '};', - ].join(`\n`)) - }) - }) -}) diff --git a/npm/create-cypress-tests/src/component-testing/babel/babelTransform.ts b/npm/create-cypress-tests/src/component-testing/babel/babelTransform.ts deleted file mode 100644 index 0c06f49ca80c..000000000000 --- a/npm/create-cypress-tests/src/component-testing/babel/babelTransform.ts +++ /dev/null @@ -1,138 +0,0 @@ -import path from 'path' -import * as fs from 'fs-extra' -import * as babel from '@babel/core' -import * as babelTypes from '@babel/types' -import { prettifyCode } from '../../utils' - -type AST = ReturnType - -export type PluginsConfigAst = { - RequireAst: AST - IfComponentTestingPluginsAst: AST - requiresReturnConfig?: true -} - -const sharedBabelOptions = { - // disable user config - configFile: false, - babelrc: false, - presets: [], - root: process.env.BABEL_TEST_ROOT, // for testing -} - -async function transformFileViaPlugin (filePath: string, babelPlugin: babel.PluginObj) { - try { - const initialCode = await fs.readFile(filePath, { encoding: 'utf-8' }) - - const updatedResult = await babel.transformAsync(initialCode, { - filename: path.basename(filePath), - filenameRelative: path.relative(process.cwd(), filePath), - plugins: [babelPlugin], - ...sharedBabelOptions, - }) - - if (!updatedResult) { - return false - } - - let finalCode = updatedResult.code - - if (finalCode === initialCode) { - return false - } - - finalCode = await prettifyCode(finalCode) - - await fs.writeFile(filePath, finalCode) - - return true - } catch (e) { - return false - } -} - -const returnConfigAst = babel.template.ast('return config; // IMPORTANT to return a config', { preserveComments: true }) - -export function createTransformPluginsFileBabelPlugin (ast: PluginsConfigAst): babel.PluginObj { - return { - visitor: { - Program: (path) => { - path.unshiftContainer('body', ast.RequireAst) - }, - Function: (path) => { - if (!babelTypes.isAssignmentExpression(path.parent)) { - return - } - - const assignment = path.parent.left - - const isModuleExports = - babelTypes.isMemberExpression(assignment) - && babelTypes.isIdentifier(assignment.object) - && assignment.object.name === 'module' - && babelTypes.isIdentifier(assignment.property) - && assignment.property.name === 'exports' - - if (isModuleExports && babelTypes.isFunction(path.parent.right)) { - const paramsLength = path.parent.right.params.length - - if (paramsLength === 0) { - path.parent.right.params.push(babelTypes.identifier('on')) - path.parent.right.params.push(babelTypes.identifier('config')) - } - - if (paramsLength === 1) { - path.parent.right.params.push(babelTypes.identifier('config')) - } - - const statementToInject = Array.isArray(ast.IfComponentTestingPluginsAst) - ? ast.IfComponentTestingPluginsAst - : [ast.IfComponentTestingPluginsAst] - - const ifComponentMode = babelTypes.ifStatement( - babelTypes.binaryExpression( - '===', - babelTypes.identifier('config.testingType'), - babelTypes.stringLiteral('component'), - ), - babelTypes.blockStatement(statementToInject as babelTypes.Statement[] | babelTypes.Statement[]), - ) - - path.get('body').pushContainer('body' as never, ifComponentMode as never) - - if (ast.requiresReturnConfig) { - path.get('body').pushContainer('body' as never, returnConfigAst as never) - } - } - }, - }, - } -} - -export async function injectPluginsCode (pluginsFilePath: string, ast: PluginsConfigAst) { - return transformFileViaPlugin(pluginsFilePath, createTransformPluginsFileBabelPlugin(ast)) -} - -export async function getPluginsSourceExample (ast: PluginsConfigAst) { - const exampleCode = [ - 'module.exports = (on, config) => {', - '', - '}', - ].join('\n') - - try { - const babelResult = await babel.transformAsync(exampleCode, { - filename: 'nothing.js', - plugins: [createTransformPluginsFileBabelPlugin(ast)], - ...sharedBabelOptions, - }) - - if (!babelResult?.code) { - throw new Error() - } - - return babelResult.code - } catch (e) { - throw new Error('Can not generate code example for plugins file because of unhandled error. Please update the plugins file manually.') - } -} diff --git a/npm/create-cypress-tests/src/component-testing/config-file-updater/configFileUpdater.test.ts b/npm/create-cypress-tests/src/component-testing/config-file-updater/configFileUpdater.test.ts deleted file mode 100644 index 8a2dee76b5ff..000000000000 --- a/npm/create-cypress-tests/src/component-testing/config-file-updater/configFileUpdater.test.ts +++ /dev/null @@ -1,405 +0,0 @@ -/// - -import { expect } from 'chai' -import { insertValueInJSString } from './configFileUpdater' - -// Test util - if needed outside the tests we can move it to utils -const stripIndent = (strings: any, ...args: any) => { - const parts = [] - - for (let i = 0; i < strings.length; i++) { - parts.push(strings[i]) - - if (i < strings.length - 1) { - parts.push(`<<${i}>>`) - } - } - - const lines = parts.join('').split('\n') - const firstLine = lines[0].length === 0 ? lines[1] : lines[0] - let indentSize = 0 - - for (let i = 0; i < firstLine.length; i++) { - if (firstLine[i] === ' ') { - indentSize++ - continue - } - - break - } - - const strippedLines = lines.map((line) => line.substring(indentSize)) - - let result = strippedLines.join('\n').trimLeft() - - args.forEach((arg: any, i: any) => { - result = result.replace(`<<${i}>>`, `${arg}`) - }) - - return result -} - -describe('lib/util/config-file-updater', () => { - context('with js files', () => { - describe('#insertValueInJSString', () => { - describe('es6 vs es5', () => { - it('finds the object literal and adds the values to it es6', async () => { - const src = stripIndent`\ - export default { - foo: 42, - } - ` - - const expectedOutput = stripIndent`\ - export default { - projectId: "id1234", - viewportWidth: 400, - foo: 42, - } - ` - - const output = await insertValueInJSString(src, { projectId: 'id1234', viewportWidth: 400 }) - - expect(output).to.equal(expectedOutput) - }) - - it('finds the object literal and adds the values to it es5', async () => { - const src = stripIndent`\ - module.exports = { - foo: 42, - } - ` - - const expectedOutput = stripIndent`\ - module.exports = { - projectId: "id1234", - viewportWidth: 400, - foo: 42, - } - ` - - const output = await insertValueInJSString(src, { projectId: 'id1234', viewportWidth: 400 }) - - expect(output).to.equal(expectedOutput) - }) - - it('works with and without the quotes around keys', async () => { - const src = stripIndent`\ - export default { - "foo": 42, - } - ` - - const expectedOutput = stripIndent`\ - export default { - projectId: "id1234", - viewportWidth: 400, - "foo": 42, - } - ` - - const output = await insertValueInJSString(src, { projectId: 'id1234', viewportWidth: 400 }) - - expect(output).to.equal(expectedOutput) - }) - }) - - describe('defineConfig', () => { - it('skips defineConfig and add to the object inside', async () => { - const src = stripIndent`\ - import { defineConfig } from "cypress" - export default defineConfig({ - foo: 42, - }) - ` - - const expectedOutput = stripIndent`\ - import { defineConfig } from "cypress" - export default defineConfig({ - projectId: "id1234", - viewportWidth: 400, - foo: 42, - }) - ` - - const output = await insertValueInJSString(src, { projectId: 'id1234', viewportWidth: 400 }) - - expect(output).to.equal(expectedOutput) - }) - - it('skips defineConfig even if it renamed in an import (es6)', async () => { - const src = stripIndent`\ - import { defineConfig as cy_defineConfig } from "cypress" - export default cy_defineConfig({ - foo: 42, - }) - ` - - const expectedOutput = stripIndent`\ - import { defineConfig as cy_defineConfig } from "cypress" - export default cy_defineConfig({ - projectId: "id1234", - viewportWidth: 400, - foo: 42, - }) - ` - - const output = await insertValueInJSString(src, { projectId: 'id1234', viewportWidth: 400 }) - - expect(output).to.equal(expectedOutput) - }) - - it('skips defineConfig even if it renamed in a require (es5)', async () => { - const src = stripIndent`\ - const { defineConfig: cy_defineConfig } = require("cypress") - module.exports = cy_defineConfig({ - foo: 42, - }) - ` - - const expectedOutput = stripIndent`\ - const { defineConfig: cy_defineConfig } = require("cypress") - module.exports = cy_defineConfig({ - projectId: "id1234", - viewportWidth: 400, - foo: 42, - }) - ` - - const output = await insertValueInJSString(src, { projectId: 'id1234', viewportWidth: 400 }) - - expect(output).to.equal(expectedOutput) - }) - }) - - describe('updates', () => { - it('updates a value if the same value is found in resolved config', async () => { - const src = stripIndent`\ - export default { - foo: 42, - } - ` - const expectedOutput = stripIndent`\ - export default { - foo: 1000, - } - ` - - const output = await insertValueInJSString(src, { foo: 1000 }) - - expect(output).to.equal(expectedOutput) - }) - - it('accepts inline comments', async () => { - const src = stripIndent`\ - export default { - foo: 12, // will do this later - viewportWidth: 800, - } - ` - const expectedOutput = stripIndent`\ - export default { - foo: 1000, // will do this later - viewportWidth: 800, - } - ` - - const output = await insertValueInJSString(src, { foo: 1000 }) - - expect(output).to.equal(expectedOutput) - }) - - it('updates a value even when this value is explicitely undefined', async () => { - const src = stripIndent`\ - export default { - foo: undefined, // will do this later - viewportWidth: 800, - } - ` - const expectedOutput = stripIndent`\ - export default { - foo: 1000, // will do this later - viewportWidth: 800, - } - ` - - const output = await insertValueInJSString(src, { foo: 1000 }) - - expect(output).to.equal(expectedOutput) - }) - - it('updates values and inserts config', async () => { - const src = stripIndent`\ - export default { - foo: 42, - bar: 84, - component: { - devServer() { - return null - } - } - } - ` - - const expectedOutput = stripIndent`\ - export default { - projectId: "id1234", - foo: 1000, - bar: 3000, - component: { - devServer() { - return null - } - } - } - ` - - const output = await insertValueInJSString(src, { foo: 1000, bar: 3000, projectId: 'id1234' }) - - expect(output).to.equal(expectedOutput) - }) - }) - - describe('subkeys', () => { - it('inserts nested values', async () => { - const src = stripIndent`\ - module.exports = { - foo: 42 - } - ` - - const output = await insertValueInJSString(src, { component: { specPattern: 'src/**/*.spec.cy.js' } }) - - const expectedOutput = stripIndent`\ - module.exports = { - component: { - specPattern: "src/**/*.spec.cy.js", - }, - foo: 42 - } - ` - - expect(output).to.equal(expectedOutput) - }) - - it('inserts nested values into existing keys', async () => { - const src = stripIndent`\ - module.exports = { - component: { - viewportWidth: 800 - }, - foo: 42 - } - ` - - const output = await insertValueInJSString(src, { component: { specPattern: 'src/**/*.spec.cy.js' } }) - - const expectedOutput = stripIndent`\ - module.exports = { - component: { - specPattern: "src/**/*.spec.cy.js", - viewportWidth: 800 - }, - foo: 42 - } - ` - - expect(output).to.equal(expectedOutput) - }) - - it('updates nested values', async () => { - const src = stripIndent`\ - module.exports = { - foo: 42, - component: { - specPattern: 'components/**/*.spec.cy.js', - foo: 82 - } - }` - - const output = await insertValueInJSString(src, { component: { specPattern: 'src/**/*.spec.cy.js' } }) - - const expectedOutput = stripIndent`\ - module.exports = { - foo: 42, - component: { - specPattern: "src/**/*.spec.cy.js", - foo: 82 - } - }` - - expect(output).to.equal(expectedOutput) - }) - }) - - describe('failures', () => { - it('fails if not an object literal', () => { - const src = [ - 'const foo = {}', - 'export default foo', - ].join('\n') - - return insertValueInJSString(src, { bar: 10 }) - .then(() => { - throw Error('this should not succeed') - }) - .catch((err) => { - expect(err.message).to.equal('Cypress was unable to add/update values in your configuration file.') - }) - }) - - it('fails if one of the values to update is not a literal', () => { - const src = [ - 'const bar = 12', - 'export default {', - ' foo: bar', - '}', - ].join('\n') - - return insertValueInJSString(src, { foo: 10 }) - .then(() => { - throw Error('this should not succeed') - }) - .catch((err) => { - expect(err.message).to.equal('Cypress was unable to add/update values in your configuration file.') - }) - }) - - it('fails with inlined values', () => { - const src = stripIndent`\ - const foo = 12 - export default { - foo - } - ` - - return insertValueInJSString(src, { foo: 10 }) - .then(() => { - throw Error('this should not succeed') - }) - .catch((err) => { - expect(err.message).to.equal('Cypress was unable to add/update values in your configuration file.') - }) - }) - - it('fails if there is a spread', () => { - const src = stripIndent`\ - const foo = { bar: 12 } - export default { - bar: 8, - ...foo - } - ` - - return insertValueInJSString(src, { bar: 10 }) - .then(() => { - throw Error('this should not succeed') - }) - .catch((err) => { - expect(err.message).to.equal('Cypress was unable to add/update values in your configuration file.') - }) - }) - }) - }) - }) -}) diff --git a/npm/create-cypress-tests/src/component-testing/config-file-updater/configFileUpdater.ts b/npm/create-cypress-tests/src/component-testing/config-file-updater/configFileUpdater.ts deleted file mode 100644 index b2e587f9ee3c..000000000000 --- a/npm/create-cypress-tests/src/component-testing/config-file-updater/configFileUpdater.ts +++ /dev/null @@ -1,294 +0,0 @@ -import { parse } from '@babel/parser' -import type { File } from '@babel/types' -import type { NodePath } from 'ast-types/lib/node-path' -import { visit } from 'recast' -import type { namedTypes } from 'ast-types' -import * as fs from 'fs-extra' -import { prettifyCode } from '../../utils' - -export async function insertValuesInConfigFile (filePath: string, obj: Record = {}) { - await insertValuesInJavaScript(filePath, obj) - - return true -} - -export async function insertValuesInJavaScript (filePath: string, obj: Record) { - const fileContents = await fs.readFile(filePath, { encoding: 'utf8' }) - - let finalCode = await insertValueInJSString(fileContents, obj) - - const prettifiedCode = await prettifyCode(finalCode) - - if (prettifiedCode) { - finalCode = prettifiedCode - } - - await fs.writeFile(filePath, finalCode) -} - -export async function insertValueInJSString (fileContents: string, obj: Record): Promise { - const ast = parse(fileContents, { plugins: ['typescript'], sourceType: 'module' }) - - let objectLiteralNode: namedTypes.ObjectExpression | undefined - - function handleExport (nodePath: NodePath | NodePath): void { - if (nodePath.node.type === 'CallExpression' - && nodePath.node.callee.type === 'Identifier') { - const functionName = nodePath.node.callee.name - - if (isDefineConfigFunction(ast, functionName)) { - return handleExport(nodePath.get('arguments', 0)) - } - } - - if (nodePath.node.type === 'ObjectExpression' && !nodePath.node.properties.find((prop) => prop.type !== 'ObjectProperty')) { - objectLiteralNode = nodePath.node - - return - } - - throw new Error('Cypress was unable to add/update values in your configuration file.') - } - - visit(ast, { - visitAssignmentExpression (nodePath) { - if (nodePath.node.left.type === 'MemberExpression') { - if (nodePath.node.left.object.type === 'Identifier' && nodePath.node.left.object.name === 'module' - && nodePath.node.left.property.type === 'Identifier' && nodePath.node.left.property.name === 'exports') { - handleExport(nodePath.get('right')) - } - } - - return false - }, - visitExportDefaultDeclaration (nodePath) { - handleExport(nodePath.get('declaration')) - - return false - }, - }) - - const splicers: Splicer[] = [] - - if (!objectLiteralNode) { - // if the export is no object literal - throw new Error('Cypress was unable to add/update values in your configuration file.') - } - - setRootKeysSplicers(splicers, obj, objectLiteralNode!, ' ') - setSubKeysSplicers(splicers, obj, objectLiteralNode!, ' ', ' ') - - // sort splicers to keep the order of the original file - const sortedSplicers = splicers.sort((a, b) => a.start === b.start ? 0 : a.start > b.start ? 1 : -1) - - if (!sortedSplicers.length) return fileContents - - let nextStartingIndex = 0 - let resultCode = '' - - sortedSplicers.forEach((splicer) => { - resultCode += fileContents.slice(nextStartingIndex, splicer.start) + splicer.replaceString - nextStartingIndex = splicer.end - }) - - return resultCode + fileContents.slice(nextStartingIndex) -} - -export function isDefineConfigFunction (ast: File, functionName: string): boolean { - let value = false - - visit(ast, { - visitVariableDeclarator (nodePath) { - // if this is a require of cypress - if (nodePath.node.init?.type === 'CallExpression' - && nodePath.node.init.callee.type === 'Identifier' - && nodePath.node.init.callee.name === 'require' - && nodePath.node.init.arguments[0].type === 'StringLiteral' - && nodePath.node.init.arguments[0].value === 'cypress') { - if (nodePath.node.id?.type === 'ObjectPattern') { - const defineConfigFunctionNode = nodePath.node.id.properties.find((prop) => { - return prop.type === 'ObjectProperty' - && prop.key.type === 'Identifier' - && prop.key.name === 'defineConfig' - }) - - if (defineConfigFunctionNode) { - value = (defineConfigFunctionNode as any).value?.name === functionName - } - } - } - - return false - }, - visitImportDeclaration (nodePath) { - if (nodePath.node.source.type === 'StringLiteral' - && nodePath.node.source.value === 'cypress') { - const defineConfigFunctionNode = nodePath.node.specifiers?.find((specifier) => { - return specifier.type === 'ImportSpecifier' - && specifier.imported.type === 'Identifier' - && specifier.imported.name === 'defineConfig' - }) - - if (defineConfigFunctionNode) { - value = (defineConfigFunctionNode as any).local?.name === functionName - } - } - - return false - }, - }) - - return value -} - -function setRootKeysSplicers ( - splicers: Splicer[], - obj: Record, - objectLiteralNode: namedTypes.ObjectExpression, - lineStartSpacer: string, -) { - const objectLiteralStartIndex = (objectLiteralNode as any).start + 1 - // add values - const objKeys = Object.keys(obj).filter((key) => ['boolean', 'number', 'string'].includes(typeof obj[key])) - - // update values - const keysToUpdate = objKeys.filter((key) => { - return objectLiteralNode.properties.find((prop) => { - return prop.type === 'ObjectProperty' - && prop.key.type === 'Identifier' - && prop.key.name === key - }) - }) - - keysToUpdate.forEach( - (key) => { - const propertyToUpdate = propertyFromKey(objectLiteralNode, key) - - if (propertyToUpdate) { - setSplicerToUpdateProperty(splicers, propertyToUpdate, obj[key], key, obj) - } - }, - ) - - const keysToInsert = objKeys.filter((key) => !keysToUpdate.includes(key)) - - if (keysToInsert.length) { - const valuesInserted = `\n${lineStartSpacer}${ keysToInsert.map((key) => `${key}: ${JSON.stringify(obj[key])},`).join(`\n${lineStartSpacer}`)}` - - splicers.push({ - start: objectLiteralStartIndex, - end: objectLiteralStartIndex, - replaceString: valuesInserted, - }) - } -} - -function setSubKeysSplicers ( - splicers: Splicer[], - obj: Record, - objectLiteralNode: namedTypes.ObjectExpression, - lineStartSpacer: string, - parentLineStartSpacer: string, -) { - const objectLiteralStartIndex = (objectLiteralNode as any).start + 1 - - const keysToUpdateWithObjects: string[] = [] - - const objSubkeys = Object.keys(obj).filter((key) => typeof obj[key] === 'object').reduce((acc: Array<{parent: string, subkey: string}>, key) => { - keysToUpdateWithObjects.push(key) - Object.entries(obj[key]).forEach(([subkey, value]) => { - if (['boolean', 'number', 'string'].includes(typeof value)) { - acc.push({ parent: key, subkey }) - } - }) - - return acc - }, []) - - // add values where the parent key needs to be created - const subkeysToInsertWithoutKey = objSubkeys.filter(({ parent }) => { - return !objectLiteralNode.properties.find((prop) => { - return prop.type === 'ObjectProperty' - && prop.key.type === 'Identifier' - && prop.key.name === parent - }) - }) - const keysToInsertForSubKeys: Record = {} - - subkeysToInsertWithoutKey.forEach((keyTuple) => { - const subkeyList = keysToInsertForSubKeys[keyTuple.parent] || [] - - subkeyList.push(keyTuple.subkey) - keysToInsertForSubKeys[keyTuple.parent] = subkeyList - }) - - let subvaluesInserted = '' - - for (const key in keysToInsertForSubKeys) { - subvaluesInserted += `\n${parentLineStartSpacer}${key}: {` - keysToInsertForSubKeys[key].forEach((subkey) => { - subvaluesInserted += `\n${parentLineStartSpacer}${lineStartSpacer}${subkey}: ${JSON.stringify(obj[key][subkey])},` - }) - - subvaluesInserted += `\n${parentLineStartSpacer}},` - } - - if (subkeysToInsertWithoutKey.length) { - splicers.push({ - start: objectLiteralStartIndex, - end: objectLiteralStartIndex, - replaceString: subvaluesInserted, - }) - } - - // add/update values where parent key already exists - keysToUpdateWithObjects.filter((parent) => { - return objectLiteralNode.properties.find((prop) => { - return prop.type === 'ObjectProperty' - && prop.key.type === 'Identifier' - && prop.key.name === parent - }) - }).forEach((key) => { - const propertyToUpdate = propertyFromKey(objectLiteralNode, key) - - if (propertyToUpdate?.value.type === 'ObjectExpression') { - setRootKeysSplicers(splicers, obj[key], propertyToUpdate.value, parentLineStartSpacer + lineStartSpacer) - } - }) -} - -function setSplicerToUpdateProperty (splicers: Splicer[], - propertyToUpdate: namedTypes.ObjectProperty, - updatedValue: any, - key: string, - obj: Record) { - if (propertyToUpdate && (isPrimitive(propertyToUpdate.value) || isUndefinedOrNull(propertyToUpdate.value))) { - splicers.push({ - start: (propertyToUpdate.value as any).start, - end: (propertyToUpdate.value as any).end, - replaceString: JSON.stringify(updatedValue), - }) - } else { - throw new Error('Cypress was unable to add/update values in your configuration file.') - } -} - -function propertyFromKey (objectLiteralNode: namedTypes.ObjectExpression | undefined, key: string): namedTypes.ObjectProperty | undefined { - return objectLiteralNode?.properties.find((prop) => { - return prop.type === 'ObjectProperty' && prop.key.type === 'Identifier' && prop.key.name === key - }) as namedTypes.ObjectProperty -} - -function isPrimitive (value: NodePath['node']): value is namedTypes.NumericLiteral | namedTypes.StringLiteral | namedTypes.BooleanLiteral { - return value.type === 'NumericLiteral' || value.type === 'StringLiteral' || value.type === 'BooleanLiteral' -} - -function isUndefinedOrNull (value: NodePath['node']): value is namedTypes.Identifier { - return value.type === 'Identifier' && ['undefined', 'null'].includes(value.name) -} - -interface Splicer{ - start: number - end: number - replaceString: string -} diff --git a/npm/create-cypress-tests/src/component-testing/init-component-testing.test.ts b/npm/create-cypress-tests/src/component-testing/init-component-testing.test.ts deleted file mode 100644 index 2db6b20baf13..000000000000 --- a/npm/create-cypress-tests/src/component-testing/init-component-testing.test.ts +++ /dev/null @@ -1,340 +0,0 @@ -import path from 'path' -import fs from 'fs-extra' -import snapshot from 'snap-shot-it' -import { expect, use } from 'chai' -import sinon, { SinonStub, SinonSpy } from 'sinon' -import chalk from 'chalk' -import mockFs from 'mock-fs' -import { initComponentTesting } from './init-component-testing' -import inquirer from 'inquirer' -import sinonChai from 'sinon-chai' -import childProcess from 'child_process' -import { someOfSpyCallsIncludes } from '../test-utils' - -use(sinonChai) - -describe('init component tests script', () => { - let promptSpy: SinonStub | null = null - let logSpy: SinonSpy | null = null - let processExitStub: SinonStub | null = null - let execStub: SinonStub | null = null - - const e2eTestOutputPath = path.resolve(__dirname, '..', 'test-output') - const cypressConfigPath = path.join(e2eTestOutputPath, 'cypress.config.ts') - - beforeEach(async () => { - logSpy = sinon.spy(global.console, 'log') - // @ts-ignores - execStub = sinon.stub(childProcess, 'exec').callsFake((command, callback) => callback()) - processExitStub = sinon.stub(process, 'exit').callsFake(() => { - throw new Error(`${chalk.red('process.exit')} should not be called`) - }) - - await fs.remove(e2eTestOutputPath) - await fs.mkdir(e2eTestOutputPath) - - process.env.BABEL_TEST_ROOT = e2eTestOutputPath - }) - - afterEach(() => { - mockFs.restore() - logSpy?.restore() - promptSpy?.restore() - processExitStub?.restore() - execStub?.restore() - }) - - function createTempFiles (tempFiles: Record) { - Object.entries(tempFiles).forEach(([fileName, content]) => { - fs.outputFileSync( - path.join(e2eTestOutputPath, fileName), - content, - ) - }) - } - - function snapshotGeneratedFiles (name: string) { - snapshot( - `${name} cypress.config.ts`, - fs.readFileSync( - path.join(e2eTestOutputPath, 'cypress.config.ts'), - { encoding: 'utf-8' }, - ), - ) - - snapshot( - `${name} plugins/index.js`, - fs.readFileSync( - path.join(e2eTestOutputPath, 'cypress', 'plugins', 'index.js'), - { encoding: 'utf-8' }, - ), - ) - - const supportFile = fs.readFileSync( - path.join(e2eTestOutputPath, 'cypress', 'support', 'component.js'), - { encoding: 'utf-8' }, - ) - - // Comparing empty snapshot errors. - if (supportFile.length === 0) { - return - } - - snapshot( - `${name} support/component.js`, - fs.readFileSync( - path.join(e2eTestOutputPath, 'cypress', 'support', 'component.js'), - { encoding: 'utf-8' }, - ), - ) - } - - it('determines more presumable configuration to suggest', async () => { - createTempFiles({ - '/cypress.config.ts': 'export default {}', - '/cypress/support/component.js': '', - '/cypress/plugins/index.js': 'module.exports = (on, config) => {}', - // For next.js user will have babel config, but we want to suggest to use the closest config for the application code - '/babel.config.js': 'module.exports = { }', - '/package.json': JSON.stringify({ dependencies: { react: '^17.x', next: '^9.2.0' } }), - }) - - promptSpy = sinon.stub(inquirer, 'prompt').returns(Promise.resolve({ - chosenTemplateName: 'next.js', - componentFolder: 'src', - }) as any) - - await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true }) - - const [{ choices }] = (inquirer.prompt as any).args[0][0] - - expect(choices[0]).to.equal('next.js') - snapshotGeneratedFiles('injects guessed next.js template') - }) - - it('automatically suggests to the user which config to use', async () => { - createTempFiles({ - '/cypress.config.ts': 'export default {}', - '/cypress/support/component.js': 'import "./commands.js";', - '/cypress/plugins/index.js': 'module.exports = () => {}', - '/package.json': JSON.stringify({ - dependencies: { - react: '^16.10.0', - }, - }), - '/webpack.config.js': 'module.exports = { }', - }) - - promptSpy = sinon.stub(inquirer, 'prompt').returns(Promise.resolve({ - chosenTemplateName: 'create-react-app', - componentFolder: 'cypress/component', - }) as any) - - await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true }) - const [{ choices, message }] = (inquirer.prompt as any).args[0][0] - - expect(choices[0]).to.equal('webpack') - expect(message).to.contain( - `Press ${chalk.inverse(' Enter ')} to continue with ${chalk.green( - 'webpack', - )} configuration`, - ) - - snapshotGeneratedFiles('Injected overridden webpack template') - }) - - it('Asks for preferred bundling tool if can not determine the right one', async () => { - createTempFiles({ - '/cypress.config.ts': 'export default {}', - '/webpack.config.js': 'module.exports = { }', - '/package.json': JSON.stringify({ dependencies: { } }), - }) - - promptSpy = sinon.stub(inquirer, 'prompt') - .onCall(0) - .returns(Promise.resolve({ - framework: 'vue@2', - }) as any) - .onCall(1) - .returns(Promise.resolve({ - chosenTemplateName: 'webpack', - componentFolder: 'src', - }) as any) - - await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true }) - - expect( - someOfSpyCallsIncludes(global.console.log, 'We were unable to automatically determine your framework 😿'), - ).to.be.true - }) - - it('Asks for framework if more than 1 option was auto detected', async () => { - createTempFiles({ - '/cypress.config.ts': 'export default {}', - '/webpack.config.js': 'module.exports = { }', - '/package.json': JSON.stringify({ dependencies: { react: '*', vue: '^2.4.5' } }), - }) - - promptSpy = sinon.stub(inquirer, 'prompt') - .onCall(0) - .returns(Promise.resolve({ - framework: 'vue@3', - }) as any) - .onCall(1) - .returns(Promise.resolve({ - chosenTemplateName: 'webpack', - componentFolder: 'src', - }) as any) - - await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true }) - - expect( - someOfSpyCallsIncludes(global.console.log, `It looks like all these frameworks: ${chalk.yellow('react, vue@2')} are available from this directory.`), - ).to.be.true - }) - - it('installs the right adapter', async () => { - createTempFiles({ - '/cypress.config.ts': 'export default {}', - '/webpack.config.js': 'module.exports = { }', - '/package.json': JSON.stringify({ dependencies: { react: '16.4.5' } }), - }) - - promptSpy = sinon.stub(inquirer, 'prompt') - .onCall(0) - .returns(Promise.resolve({ - chosenTemplateName: 'vite', - componentFolder: 'src', - }) as any) - - await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true }) - expect(execStub).to.be.calledWith('yarn add @cypress/react --dev') - }) - - it('installs the right adapter for vue 3', async () => { - createTempFiles({ - '/cypress.config.ts': 'export default {}', - '/vite.config.js': 'module.exports = { }', - '/package.json': JSON.stringify({ dependencies: { vue: '^3.0.0' } }), - }) - - promptSpy = sinon.stub(inquirer, 'prompt') - .onCall(0) - .returns(Promise.resolve({ - chosenTemplateName: 'vite', - componentFolder: 'src', - }) as any) - - await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true }) - expect(execStub).to.be.calledWith('yarn add @cypress/vue --dev') - }) - - it('suggest the right instruction based on user template choice', async () => { - createTempFiles({ - '/package.json': JSON.stringify({ - dependencies: { - react: '^16.0.0', - }, - }), - '/cypress.config.ts': 'export default {}', - }) - - promptSpy = sinon.stub(inquirer, 'prompt').returns(Promise.resolve({ - chosenTemplateName: 'create-react-app', - componentFolder: 'src', - }) as any) - - await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true }) - expect( - someOfSpyCallsIncludes( - global.console.log, - 'https://github.com/cypress-io/cypress/tree/develop/npm/react/examples/react-scripts', - ), - ).to.be.true - }) - - it('suggests right docs example and cypress.config.ts config based on the `componentFolder` answer', async () => { - createTempFiles({ - '/cypress.config.ts': 'export default {}', - '/package.json': JSON.stringify({ - dependencies: { - react: '^16.0.0', - }, - }), - }) - - sinon.stub(inquirer, 'prompt').returns(Promise.resolve({ - chosenTemplateName: 'create-react-app', - componentFolder: 'cypress/component', - }) as any) - - await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true }) - - const injectedCode = require(path.join(e2eTestOutputPath, 'cypress.config.ts')) - - expect(JSON.stringify(injectedCode.default, null, 2)).to.equal(JSON.stringify( - { - specPattern: 'cypress/component/**/*.spec.{js,ts,jsx,tsx}', - }, - null, - 2, - )) - }) - - it('Shows help message if cypress files are not created', async () => { - createTempFiles({ - '/cypress.config.ts': 'export default {}', - '/package.json': JSON.stringify({ - dependencies: { - react: '^16.0.0', - }, - }), - }) - - sinon.stub(inquirer, 'prompt').returns(Promise.resolve({ - chosenTemplateName: 'create-react-app', - componentFolder: 'cypress/component', - }) as any) - - await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true }) - - expect( - someOfSpyCallsIncludes( - global.console.log, - 'was not updated automatically. Please add the following config manually:', - ), - ).to.be.true - }) - - it(`Doesn't affect injected code if user has custom babel.config.js`, async () => { - createTempFiles({ - '/cypress/plugins/index.js': 'module.exports = (on, config) => {}', - '/cypress.config.ts': 'export default {}', - 'babel.config.js': `module.exports = ${JSON.stringify({ - presets: [ - '@babel/preset-env', - ], - })}`, - '/package.json': JSON.stringify({ - dependencies: { - babel: '*', - react: '^16.0.0', - }, - }), - }) - - sinon.stub(inquirer, 'prompt').returns(Promise.resolve({ - chosenTemplateName: 'create-react-app', - componentFolder: 'cypress/component', - }) as any) - - await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true }) - const babelPluginsOutput = await fs.readFile( - path.join(e2eTestOutputPath, 'cypress', 'plugins', 'index.js'), - 'utf-8', - ) - - expect(babelPluginsOutput).not.to.contain('use strict') - expect(babelPluginsOutput).to.contain('module.exports = (on, config) => {') - }) -}) diff --git a/npm/create-cypress-tests/src/component-testing/init-component-testing.ts b/npm/create-cypress-tests/src/component-testing/init-component-testing.ts deleted file mode 100644 index a79908088fef..000000000000 --- a/npm/create-cypress-tests/src/component-testing/init-component-testing.ts +++ /dev/null @@ -1,194 +0,0 @@ -import fs from 'fs-extra' -import path from 'path' -import chalk from 'chalk' -import inquirer from 'inquirer' -import highlight from 'cli-highlight' -import { Template } from './templates/Template' -import { guessTemplate } from './templates/guessTemplate' -import { installFrameworkAdapter } from './installFrameworkAdapter' -import { injectPluginsCode, getPluginsSourceExample } from './babel/babelTransform' -import { installDependency } from '../utils' -import { insertValuesInConfigFile } from './config-file-updater/configFileUpdater' - -async function injectOrShowConfigCode (injectFn: () => Promise, { - code, - filePath, - fallbackFileMessage, - language, -}: { - code: string - filePath: string - language: string - fallbackFileMessage: string -}) { - const fileExists = fs.existsSync(filePath) - const readableFilePath = fileExists ? path.relative(process.cwd(), filePath) : fallbackFileMessage - - const printCode = () => { - console.log() - console.log(highlight(code, { language })) - console.log() - } - - const printSuccess = () => { - console.log(`✅ ${chalk.bold.green(readableFilePath)} was updated with the following config:`) - printCode() - } - - const printFailure = () => { - console.log(`❌ ${chalk.bold.red(readableFilePath)} was not updated automatically. Please add the following config manually: `) - printCode() - } - - if (!fileExists) { - printFailure() - - return - } - - // something get completely wrong when using babel or something. Print error message. - const injected = await injectFn().catch(() => false) - - injected ? printSuccess() : printFailure() -} - -async function injectAndShowCypressConfig ( - cypressJsonPath: string, - componentFolder: string, -) { - const configToInject = { - specPattern: `${componentFolder}/**/*.spec.{js,ts,jsx,tsx}`, - } - - await injectOrShowConfigCode(() => insertValuesInConfigFile(cypressJsonPath, configToInject), { - code: JSON.stringify(configToInject, null, 2), - language: 'js', - filePath: cypressJsonPath, - fallbackFileMessage: 'cypress.json config file', - }) -} - -async function injectAndShowPluginConfig (template: Template, { - templatePayload, - pluginsFilePath, - cypressProjectRoot, -}: { - templatePayload: T | null - pluginsFilePath: string - cypressProjectRoot: string -}) { - const ast = template.getPluginsCodeAst(templatePayload, { cypressProjectRoot }) - - await injectOrShowConfigCode(() => injectPluginsCode(pluginsFilePath, ast), { - code: await getPluginsSourceExample(ast), - language: 'js', - filePath: pluginsFilePath, - fallbackFileMessage: 'plugins file (https://on.cypress.io/plugins-file)', - }) -} - -type InitComponentTestingOptions = { - config: Record - cypressConfigPath: string - useYarn: boolean -} - -export async function initComponentTesting ({ config, useYarn, cypressConfigPath }: InitComponentTestingOptions) { - const cypressProjectRoot = path.resolve(cypressConfigPath, '..') - - const framework = await installFrameworkAdapter(cypressProjectRoot, { useYarn }) - const { - possibleTemplates, - defaultTemplate, - defaultTemplateName, - templatePayload, - } = await guessTemplate(framework, cypressProjectRoot) - - const pluginsFilePath = path.resolve( - cypressProjectRoot, - config.pluginsFile ?? './cypress/plugins/index.js', - ) - - const templateChoices = Object.keys(possibleTemplates).sort((key) => { - return key === defaultTemplateName ? -1 : 0 - }) - - const { - chosenTemplateName, - componentFolder, - }: Record = await inquirer.prompt([ - { - type: 'list', - name: 'chosenTemplateName', - choices: templateChoices, - default: defaultTemplate ? 0 : undefined, - message: defaultTemplate?.message - ? `${defaultTemplate?.message}\n\n Press ${chalk.inverse( - ' Enter ', - )} to continue with ${chalk.green( - defaultTemplateName, - )} configuration or select another template from the list:` - : 'We were not able to automatically determine which framework or bundling tool you are using. Please choose one from the list:', - }, - { - type: 'input', - name: 'componentFolder', - filter: (input) => input.trim(), - validate: (input) => { - return input === '' || !/^[a-zA-Z].*/.test(input) - ? `Directory "${input}" is invalid` - : true - }, - message: 'Which folder would you like to use for your component tests?', - default: (answers: { chosenTemplateName: keyof typeof possibleTemplates }) => { - return possibleTemplates[answers.chosenTemplateName].recommendedComponentFolder - }, - }, - ]) - - const chosenTemplate = possibleTemplates[chosenTemplateName] as Template - - console.log() - console.log(`Installing required dependencies`) - console.log() - - for (const dependency of chosenTemplate.dependencies) { - await installDependency(dependency, { useYarn }) - } - - console.log() - console.log(`Let's setup everything for component testing with ${chalk.cyan(chosenTemplateName)}:`) - console.log() - - await injectAndShowCypressConfig(cypressConfigPath, componentFolder) - await injectAndShowPluginConfig(chosenTemplate, { - templatePayload, - pluginsFilePath, - cypressProjectRoot, - }) - - if (chosenTemplate.printHelper) { - chosenTemplate.printHelper() - } - - console.log( - `Find examples of component tests for ${chalk.green( - chosenTemplateName, - )} in ${chalk.underline(chosenTemplate.getExampleUrl({ componentFolder }))}.`, - ) - - if (framework === 'react') { - console.log() - - console.log( - `Docs for different recipes of bundling tools: ${chalk.bold.underline( - 'https://github.com/cypress-io/cypress/tree/develop/npm/react/docs/recipes.md', - )}`, - ) - } - - // render delimiter - console.log() - console.log(new Array(process.stdout.columns).fill('═').join('')) - console.log() -} diff --git a/npm/create-cypress-tests/src/component-testing/installFrameworkAdapter.ts b/npm/create-cypress-tests/src/component-testing/installFrameworkAdapter.ts deleted file mode 100644 index 18e669a02eab..000000000000 --- a/npm/create-cypress-tests/src/component-testing/installFrameworkAdapter.ts +++ /dev/null @@ -1,63 +0,0 @@ -import chalk from 'chalk' -import inquirer from 'inquirer' -import { scanFSForAvailableDependency } from '../findPackageJson' -import { installDependency } from '../utils' - -async function guessOrAskForFramework (cwd: string): Promise<'react' | 'vue@2' | 'vue@3'> { - // please sort this alphabetically - const frameworks = { - react: () => scanFSForAvailableDependency(cwd, { react: '*', 'react-dom': '*' }), - 'vue@2': () => scanFSForAvailableDependency(cwd, { vue: '2.x' }), - 'vue@3': () => scanFSForAvailableDependency(cwd, { vue: '3.x' }), - } - - const guesses = Object.keys(frameworks).filter((framework) => { - return frameworks[framework as keyof typeof frameworks]() - }) as Array<'react' | 'vue@2' | 'vue@3'> - - // found 1 precise guess. Continue - if (guesses.length === 1) { - const framework = guesses[0] - - console.log(`\nThis project is using ${chalk.bold.cyan(framework)}. Let's install the right adapter:`) - - return framework - } - - if (guesses.length === 0) { - console.log(`We were unable to automatically determine your framework 😿. ${chalk.grey('Make sure to run this command from the directory where your components located in order to make smart detection works. Or continue with manual setup:')}`) - } - - if (guesses.length > 0) { - console.log(`It looks like all these frameworks: ${chalk.yellow(guesses.join(', '))} are available from this directory. ${chalk.grey('Make sure to run this command from the directory where your components located in order to make smart detection works. Or continue with manual setup:')}`) - } - - const { framework } = await inquirer.prompt([ - { - type: 'list', - name: 'framework', - choices: Object.keys(frameworks), - message: `Which framework do you use?`, - }, - ]) - - return framework -} - -type InstallAdapterOptions = { - useYarn: boolean -} - -const frameworkDependencies = { - react: '@cypress/react', - 'vue@2': '@cypress/vue2', - 'vue@3': '@cypress/vue', -} - -export async function installFrameworkAdapter (cwd: string, options: InstallAdapterOptions) { - const framework = await guessOrAskForFramework(cwd) - - await installDependency(frameworkDependencies[framework], options) - - return framework -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/Template.ts b/npm/create-cypress-tests/src/component-testing/templates/Template.ts deleted file mode 100644 index 72ea8de09d2b..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/Template.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { PluginsConfigAst } from '../babel/babelTransform' - -export interface Template { - message: string - getExampleUrl: ({ componentFolder }: { componentFolder: string }) => string - recommendedComponentFolder: string - test(rootPath: string): { success: boolean, payload?: T } - getPluginsCodeAst: ( - payload: T | null, - options: { cypressProjectRoot: string }, - ) => PluginsConfigAst - dependencies: string[] - printHelper?: () => void -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/_shared/index.ts b/npm/create-cypress-tests/src/component-testing/templates/_shared/index.ts deleted file mode 100644 index 58f0af7bbb8f..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/_shared/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Template } from '../Template' -import { ViteTemplate } from './vite' - -export const frameworkAgnosticTemplates: Record> = { - vite: ViteTemplate, -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/_shared/vite.test.ts b/npm/create-cypress-tests/src/component-testing/templates/_shared/vite.test.ts deleted file mode 100644 index 78b557d86091..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/_shared/vite.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ViteTemplate } from './vite' -import { snapshotPluginsAstCode } from '../../../test-utils' - -describe('vue: vite template', () => { - it('correctly generates plugins config', () => snapshotPluginsAstCode(ViteTemplate)) -}) diff --git a/npm/create-cypress-tests/src/component-testing/templates/_shared/vite.ts b/npm/create-cypress-tests/src/component-testing/templates/_shared/vite.ts deleted file mode 100644 index b9b6f8f93c57..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/_shared/vite.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as babel from '@babel/core' -import { scanFSForAvailableDependency } from '../../../findPackageJson' -import { Template } from '../Template' - -export const ViteTemplate: Template = { - message: - 'It looks like you are using vitejs to run and build an application.', - getExampleUrl: () => 'https://github.com/cypress-io/cypress/tree/develop/npm/vue/examples/vite', - recommendedComponentFolder: 'src', - dependencies: ['@cypress/vite-dev-server'], - getPluginsCodeAst: () => { - return { - requiresReturnConfig: true, - RequireAst: babel.template.ast( - 'const { startDevServer } = require("@cypress/vite-dev-server");', - ), - IfComponentTestingPluginsAst: babel.template.ast([ - 'on("dev-server:start", async (options) => startDevServer({ options }))', - ].join('\n'), { preserveComments: true }), - } - }, - test: (root) => { - return { - success: scanFSForAvailableDependency(root, { vite: '*' }), - } - }, -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/guessTemplate.ts b/npm/create-cypress-tests/src/component-testing/templates/guessTemplate.ts deleted file mode 100644 index 10bcdfaa93a8..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/guessTemplate.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Template } from './Template' -import { reactTemplates } from './react' -import { vueTemplates } from './vue' -import { frameworkAgnosticTemplates } from './_shared' - -const frameworkSpecificTemplates = { - react: reactTemplates, - 'vue@2': vueTemplates, - 'vue@3': vueTemplates, -} - -export async function guessTemplate (framework: keyof typeof frameworkSpecificTemplates, cwd: string) { - const templates = { ...frameworkAgnosticTemplates, ...frameworkSpecificTemplates[framework] } - - for (const [name, template] of Object.entries(templates)) { - const typedTemplate = template as Template - const { success, payload } = typedTemplate.test(cwd) - - if (success) { - return { - defaultTemplate: typedTemplate, - defaultTemplateName: name, - templatePayload: payload ?? null, - possibleTemplates: templates, - } - } - } - - return { - templatePayload: null, - defaultTemplate: null, - defaultTemplateName: null, - possibleTemplates: templates, - } -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/react/babel.test.ts b/npm/create-cypress-tests/src/component-testing/templates/react/babel.test.ts deleted file mode 100644 index 1bff7af467af..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/react/babel.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { expect } from 'chai' -import mockFs from 'mock-fs' -import { BabelTemplate } from './babel' -import { snapshotPluginsAstCode } from '../../../test-utils' - -describe('babel installation template', () => { - beforeEach(mockFs.restore) - - it('resolves babel.config.json', () => { - mockFs({ - '/babel.config.json': JSON.stringify({ - presets: [], - plugins: [], - }), - }) - - const { success } = BabelTemplate.test('/') - - expect(success).to.equal(true) - }) - - it('resolves babel.config.js', () => { - mockFs({ - '/project/babel.config.js': - 'module.exports = { presets: [], plugins: [] };', - '/project/index/package.json': 'dev/null', - }) - - const { success } = BabelTemplate.test('/project/index') - - expect(success).to.equal(true) - }) - - it('resolves babel config from the deep folder', () => { - mockFs({ - '/some/.babelrc': JSON.stringify({ - presets: [], - plugins: [], - }), - '/some/deep/folder/text.txt': '1', - }) - - const { success } = BabelTemplate.test('/some/deep/folder') - - expect(success).to.equal(true) - }) - - it('fails if no babel config found', () => { - mockFs({ - '/some.txt': '1', - }) - - const { success } = BabelTemplate.test('/') - - expect(success).to.equal(false) - }) - - it('resolves babel.config from package.json', () => { - mockFs({ - '/package.json': JSON.stringify({ - babel: { - presets: [], - }, - }), - }) - - const { success } = BabelTemplate.test('/') - - expect(success).to.equal(true) - }) - - it('correctly generates plugins config', () => snapshotPluginsAstCode(BabelTemplate)) -}) diff --git a/npm/create-cypress-tests/src/component-testing/templates/react/babel.ts b/npm/create-cypress-tests/src/component-testing/templates/react/babel.ts deleted file mode 100644 index 0202d05b4400..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/react/babel.ts +++ /dev/null @@ -1,44 +0,0 @@ -import chalk from 'chalk' -import findUp from 'find-up' -import * as babel from '@babel/core' -import { Template } from '../Template' -import { createFindPackageJsonIterator } from '../../../findPackageJson' - -export const BabelTemplate: Template = { - message: `It looks like you have babel config defined. We can use it to transpile your components for testing.\n ${chalk.red( - '>>', - )} This is not a replacement for bundling tool. We will use ${chalk.red( - 'webpack', - )} to bundle the components for testing.`, - recommendedComponentFolder: 'cypress/component', - dependencies: ['webpack', '@cypress/webpack-dev-server'], - getExampleUrl: () => 'https://github.com/cypress-io/cypress/tree/develop/npm/react/examples/babel', - getPluginsCodeAst: () => { - return { - requiresReturnConfig: true, - RequireAst: babel.template.ast('const injectDevServer = require(\'@cypress/react/plugins/babel\')'), - IfComponentTestingPluginsAst: babel.template.ast([ - 'injectDevServer(on, config)', - ].join('\n'), { preserveComments: true }), - } - }, - test: (cwd) => { - const babelConfig = findUp.sync( - ['babel.config.js', 'babel.config.json', '.babelrc', '.babelrc.json'], - { type: 'file', cwd }, - ) - - if (babelConfig) { - return { success: true } - } - - // babel config can also be declared in package.json with `babel` key https://babeljs.io/docs/en/configuration#packagejson - const packageJsonIterator = createFindPackageJsonIterator(cwd) - - return packageJsonIterator.map(({ babel }) => { - return { - success: Boolean(babel), - } - }) - }, -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/react/index.ts b/npm/create-cypress-tests/src/component-testing/templates/react/index.ts deleted file mode 100644 index bbbfca5178fb..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/react/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Template } from '../Template' -import { NextTemplate } from './next' -import { WebpackTemplate } from './reactWebpackFile' -import { ReactScriptsTemplate } from './react-scripts' -import { BabelTemplate } from './babel' -import { WebpackOptions } from './webpack-options' - -export const reactTemplates: Record> = { - 'create-react-app': ReactScriptsTemplate, - 'next.js': NextTemplate, - webpack: WebpackTemplate, - babel: BabelTemplate, - 'default (webpack options)': WebpackOptions, -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/react/next.test.ts b/npm/create-cypress-tests/src/component-testing/templates/react/next.test.ts deleted file mode 100644 index 4701791877e3..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/react/next.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import sinon, { SinonSpy } from 'sinon' -import { expect, use } from 'chai' -import sinonChai from 'sinon-chai' -import mockFs from 'mock-fs' -import { NextTemplate } from './next' -import { snapshotPluginsAstCode } from '../../../test-utils' - -use(sinonChai) - -describe('next.js install template', () => { - let warnSpy: SinonSpy | null = null - - beforeEach(() => { - warnSpy = sinon.spy(global.console, 'warn') - }) - - afterEach(() => { - mockFs.restore() - warnSpy?.restore() - }) - - it('finds the closest package.json and checks that next is declared as dependency', () => { - mockFs({ - '/package.json': JSON.stringify({ - dependencies: { - next: '^9.2.3', - }, - scripts: { - build: 'next', - }, - }), - }) - - const { success } = NextTemplate.test('/') - - expect(success).to.equal(true) - }) - - it('works if next is declared in the devDependencies as well', () => { - mockFs({ - './package.json': JSON.stringify({ - devDependencies: { - next: '^9.2.3', - }, - scripts: { - build: 'next', - }, - }), - }) - - const { success } = NextTemplate.test(process.cwd()) - - expect(success).to.equal(true) - }) - - it('warns and fails if version is not supported', () => { - mockFs({ - './package.json': JSON.stringify({ - devDependencies: { - next: '^8.2.3', - }, - scripts: { - build: 'next', - }, - }), - }) - - const { success } = NextTemplate.test('i/am/in/some/deep/folder') - - console.log(global.console.warn) - expect(success).to.equal(false) - - expect(global.console.warn).to.be.called - }) - - it('correctly generates plugins config', () => snapshotPluginsAstCode(NextTemplate)) -}) diff --git a/npm/create-cypress-tests/src/component-testing/templates/react/next.ts b/npm/create-cypress-tests/src/component-testing/templates/react/next.ts deleted file mode 100644 index 2f7ef89adfa1..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/react/next.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as babel from '@babel/core' -import { createFindPackageJsonIterator } from '../../../findPackageJson' -import { Template } from '../Template' -import { validateSemverVersion } from '../../../utils' -import { MIN_SUPPORTED_VERSION } from '../../versions' - -export const NextTemplate: Template = { - message: 'It looks like you are using next.js.', - getExampleUrl: () => { - return 'https://github.com/cypress-io/cypress/tree/develop/npm/react/examples/nextjs' - }, - recommendedComponentFolder: 'cypress/component', - dependencies: ['@cypress/webpack-dev-server'], - getPluginsCodeAst: () => { - return { - requiresReturnConfig: true, - RequireAst: babel.template.ast('const injectDevServer = require(\'@cypress/react/plugins/next\')'), - IfComponentTestingPluginsAst: babel.template.ast([ - 'injectDevServer(on, config)', - ].join('\n'), { preserveComments: true }), - } - }, - test: (cwd) => { - const packageJsonIterator = createFindPackageJsonIterator(cwd) - - return packageJsonIterator.map(({ dependencies, devDependencies }, path) => { - if (!dependencies && !devDependencies) { - return { success: false } - } - - const allDeps = { - ...(devDependencies || {}), - ...(dependencies || {}), - } as Record - - const nextVersion = allDeps['next'] - - if (!nextVersion) { - return { success: false } - } - - if ( - !validateSemverVersion( - nextVersion, - MIN_SUPPORTED_VERSION['next'], - 'next.js', - ) - ) { - return { success: false } - } - - return { success: true } - }) - }, -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/react/react-scripts.test.ts b/npm/create-cypress-tests/src/component-testing/templates/react/react-scripts.test.ts deleted file mode 100644 index 7bf01624006a..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/react/react-scripts.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import sinon, { SinonSpy } from 'sinon' -import { expect, use } from 'chai' -import sinonChai from 'sinon-chai' -import mockFs from 'mock-fs' -import { ReactScriptsTemplate } from './react-scripts' -import { snapshotPluginsAstCode } from '../../../test-utils' - -use(sinonChai) - -describe('create-react-app install template', () => { - let warnSpy: SinonSpy | null = null - - beforeEach(() => { - warnSpy = sinon.spy(global.console, 'warn') - }) - - afterEach(() => { - mockFs.restore() - warnSpy?.restore() - }) - - it('finds the closest package.json and checks that react-scripts is declared as dependency', () => { - mockFs({ - '/package.json': JSON.stringify({ - dependencies: { - 'react-scripts': '^3.2.3', - }, - }), - }) - - const { success } = ReactScriptsTemplate.test(process.cwd()) - - expect(success).to.equal(true) - }) - - it('works if react-scripts is declared in the devDependencies as well', () => { - mockFs({ - './package.json': JSON.stringify({ - devDependencies: { - 'react-scripts': '^3.2.3', - }, - }), - }) - - const { success } = ReactScriptsTemplate.test(process.cwd()) - - expect(success).to.equal(true) - }) - - it('warns and fails if version is not supported', () => { - mockFs({ - './package.json': JSON.stringify({ - devDependencies: { - 'react-scripts': '^2.2.3', - }, - }), - }) - - const { success } = ReactScriptsTemplate.test(process.cwd()) - - expect(success).to.equal(false) - expect(global.console.warn).to.be.called - }) - - it('correctly generates plugins config', () => snapshotPluginsAstCode(ReactScriptsTemplate)) -}) diff --git a/npm/create-cypress-tests/src/component-testing/templates/react/react-scripts.ts b/npm/create-cypress-tests/src/component-testing/templates/react/react-scripts.ts deleted file mode 100644 index 860106b74b24..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/react/react-scripts.ts +++ /dev/null @@ -1,62 +0,0 @@ -import chalk from 'chalk' -import { createFindPackageJsonIterator } from '../../../findPackageJson' -import { Template } from '../Template' -import { validateSemverVersion } from '../../../utils' -import { MIN_SUPPORTED_VERSION } from '../../versions' -import * as babel from '@babel/core' - -export const ReactScriptsTemplate: Template = { - recommendedComponentFolder: 'src', - message: 'It looks like you are using create-react-app.', - dependencies: ['@cypress/webpack-dev-server'], - getExampleUrl: ({ componentFolder }) => { - return componentFolder === 'src' - ? 'https://github.com/cypress-io/cypress/tree/develop/npm/react/examples/react-scripts' - : 'https://github.com/cypress-io/cypress/tree/develop/npm/react/examples/react-scripts-folder' - }, - getPluginsCodeAst: () => { - return { - requiresReturnConfig: true, - RequireAst: babel.template.ast('const injectDevServer = require(\'@cypress/react/plugins/react-scripts\')'), - IfComponentTestingPluginsAst: babel.template.ast([ - 'injectDevServer(on, config)', - ].join('\n'), { preserveComments: true }), - } - }, - test: () => { - // TODO also determine ejected create react app - const packageJsonIterator = createFindPackageJsonIterator(process.cwd()) - - return packageJsonIterator.map(({ dependencies, devDependencies }) => { - if (dependencies || devDependencies) { - const allDeps = { ...devDependencies, ...dependencies } || {} - - if (!allDeps['react-scripts']) { - return { success: false } - } - - if ( - !validateSemverVersion( - allDeps['react-scripts'], - MIN_SUPPORTED_VERSION['react-scripts'], - ) - ) { - console.warn( - `It looks like you are using ${chalk.green( - 'create-react-app', - )}, but we support only projects with version ${chalk.bold( - MIN_SUPPORTED_VERSION['react-scripts'], - )} of react-scripts.`, - ) - - // yey found the template - return { success: false } - } - - return { success: true } - } - - return { success: false } - }) - }, -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/react/reactWebpackFile.test.ts b/npm/create-cypress-tests/src/component-testing/templates/react/reactWebpackFile.test.ts deleted file mode 100644 index 4212fcc04f51..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/react/reactWebpackFile.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { expect } from 'chai' -import mockFs from 'mock-fs' -import { snapshotPluginsAstCode } from '../../../test-utils' -import { WebpackTemplate } from './reactWebpackFile' - -describe('webpack-file install template', () => { - afterEach(mockFs.restore) - - it('resolves webpack.config.js', () => { - mockFs({ - '/webpack.config.js': 'module.exports = { }', - }) - - const { success, payload } = WebpackTemplate.test(process.cwd()) - - expect(success).to.equal(true) - expect(payload?.webpackConfigPath).to.equal('/webpack.config.js') - }) - - it('finds the closest package.json and tries to fetch webpack config path from scrips', () => { - mockFs({ - '/configs/webpack.js': 'module.exports = { }', - '/package.json': JSON.stringify({ - scripts: { - build: 'webpack --config configs/webpack.js', - }, - }), - }) - - const { success, payload } = WebpackTemplate.test(process.cwd()) - - expect(success).to.equal(true) - expect(payload?.webpackConfigPath).to.equal('/configs/webpack.js') - }) - - it('looks for package.json in the upper folder', () => { - mockFs({ - '/i/am/in/some/deep/folder/withFile': 'test', - '/somewhere/configs/webpack.js': 'module.exports = { }', - '/package.json': JSON.stringify({ - scripts: { - build: 'webpack --config somewhere/configs/webpack.js', - }, - }), - }) - - const { success, payload } = WebpackTemplate.test( - 'i/am/in/some/deep/folder', - ) - - expect(success).to.equal(true) - expect(payload?.webpackConfigPath).to.equal('/somewhere/configs/webpack.js') - }) - - it('returns success:false if cannot find webpack config', () => { - mockFs({ - '/a.js': '1', - '/b.js': '2', - }) - - const { success, payload } = WebpackTemplate.test('/') - - expect(success).to.equal(false) - expect(payload).to.equal(undefined) - }) - - it('correctly generates plugins config when webpack config path is missing', () => { - snapshotPluginsAstCode(WebpackTemplate) - }) - - it('correctly generates plugins config when webpack config path is provided', () => { - snapshotPluginsAstCode(WebpackTemplate, { webpackConfigPath: '/config/webpack.config.js' }) - }) -}) diff --git a/npm/create-cypress-tests/src/component-testing/templates/react/reactWebpackFile.ts b/npm/create-cypress-tests/src/component-testing/templates/react/reactWebpackFile.ts deleted file mode 100644 index 6bc89f7fe0fe..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/react/reactWebpackFile.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as babel from '@babel/core' -import path from 'path' -import { Template } from '../Template' -import { findWebpackConfig } from '../templateUtils' - -export const WebpackTemplate: Template<{ webpackConfigPath: string }> = { - message: - 'It looks like you have custom `webpack.config.js`. We can use it to bundle the components for testing.', - getExampleUrl: () => { - return 'https://github.com/cypress-io/cypress/tree/develop/npm/react/examples/webpack-file' - }, - recommendedComponentFolder: 'cypress/component', - dependencies: ['@cypress/webpack-dev-server'], - getPluginsCodeAst: (payload, { cypressProjectRoot }) => { - const includeWarnComment = !payload - const webpackConfigPath = payload - ? path.relative(cypressProjectRoot, payload.webpackConfigPath) - : './webpack.config.js' - - return { - requiresReturnConfig: true, - RequireAst: babel.template.ast('const injectDevServer = require("@cypress/react/plugins/load-webpack")'), - IfComponentTestingPluginsAst: babel.template.ast([ - 'injectDevServer(on, config, {', - includeWarnComment - ? ' // TODO replace with valid webpack config path' - : '', - ` webpackFilename: '${webpackConfigPath}'`, - '})', - ].join('\n'), { preserveComments: true }), - } - }, - test: (root) => { - const webpackConfigPath = findWebpackConfig(root) - - return webpackConfigPath ? { - success: true, - payload: { webpackConfigPath }, - } : { - success: false, - } - }, -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/react/webpack-options-module-exports.template.js b/npm/create-cypress-tests/src/component-testing/templates/react/webpack-options-module-exports.template.js deleted file mode 100644 index cffd3bce12ff..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/react/webpack-options-module-exports.template.js +++ /dev/null @@ -1,24 +0,0 @@ -/** @type import("webpack").Configuration */ -const webpackConfig = { - resolve: { - extensions: ['.js', '.ts', '.jsx', '.tsx'], - }, - mode: 'development', - devtool: false, - output: { - publicPath: '/', - chunkFilename: '[name].bundle.js', - }, - // TODO: update with valid configuration for your components - module: { - rules: [ - { - test: /\.(js|jsx|mjs|ts|tsx)$/, - loader: 'babel-loader', - options: { cacheDirectory: path.resolve(__dirname, '.babel-cache') }, - }, - ] - }, -} - -on('dev-server:start', (options) => startDevServer({ options, webpackConfig })) diff --git a/npm/create-cypress-tests/src/component-testing/templates/react/webpack-options.ts b/npm/create-cypress-tests/src/component-testing/templates/react/webpack-options.ts deleted file mode 100644 index 8765be52910c..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/react/webpack-options.ts +++ /dev/null @@ -1,35 +0,0 @@ -import fs from 'fs' -import path from 'path' -import * as babel from '@babel/core' -import chalk from 'chalk' -import { Template } from '../Template' - -export const WebpackOptions: Template = { - // this should never show ideally - message: `Unable to detect where webpack options are.`, - getExampleUrl: () => { - return 'https://github.com/cypress-io/cypress/tree/develop/npm/react/examples/webpack-options' - }, - test: () => ({ success: false }), - recommendedComponentFolder: 'src', - dependencies: ['webpack', '@cypress/webpack-dev-server'], - getPluginsCodeAst: () => { - return { - RequireAst: babel.template.ast([ - 'const path = require("path")', - 'const { startDevServer } = require("@cypress/webpack-dev-Server")', - ].join('\n')), - IfComponentTestingPluginsAst: babel.template.ast( - fs.readFileSync(path.resolve(__dirname, 'webpack-options-module-exports.template.js'), { encoding: 'utf-8' }), - { preserveComments: true }, - ), - } - }, - printHelper: () => { - console.log( - `${chalk.inverse('Important:')} this configuration is using ${chalk.blue( - 'new webpack configuration', - )} to bundle components. If you are using some framework (e.g. next) or bundling tool (e.g. rollup/vite) consider using them to bundle component specs for cypress. \n`, - ) - }, -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/react/webpackOptions.test.ts b/npm/create-cypress-tests/src/component-testing/templates/react/webpackOptions.test.ts deleted file mode 100644 index c7b7e2e6f9a3..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/react/webpackOptions.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { WebpackOptions } from './webpack-options' -import { snapshotPluginsAstCode } from '../../../test-utils' - -describe('webpack-options template', () => { - it('correctly generates plugins config', () => snapshotPluginsAstCode(WebpackOptions)) -}) diff --git a/npm/create-cypress-tests/src/component-testing/templates/templateUtils.ts b/npm/create-cypress-tests/src/component-testing/templates/templateUtils.ts deleted file mode 100644 index 8b1f8e978f3e..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/templateUtils.ts +++ /dev/null @@ -1,53 +0,0 @@ -import findUp from 'find-up' -import path from 'path' -import { createFindPackageJsonIterator } from '../../findPackageJson' - -export function extractWebpackConfigPathFromScript (script: string) { - if (script.includes('webpack ') || script.includes('webpack-dev-server ')) { - const webpackCliArgs = script.split(' ').map((part) => part.trim()) - const configArgIndex = webpackCliArgs.findIndex((arg) => arg === '--config') - - return configArgIndex === -1 ? null : webpackCliArgs[configArgIndex + 1] - } - - return null -} - -export function findWebpackConfig (root: string) { - const webpackConfigPath = findUp.sync('webpack.config.js', { cwd: root }) - - if (webpackConfigPath) { - return webpackConfigPath - } - - const packageJsonIterator = createFindPackageJsonIterator(root) - - const { success, payload } = packageJsonIterator.map(({ scripts }, packageJsonPath) => { - if (!scripts) { - return { success: false } - } - - for (const script of Object.values(scripts)) { - const webpackConfigRelativePath = extractWebpackConfigPathFromScript( - script, - ) - - if (webpackConfigRelativePath) { - const directoryRoot = path.resolve(packageJsonPath, '..') - const webpackConfigPath = path.resolve( - directoryRoot, - webpackConfigRelativePath, - ) - - return { - success: true, - payload: { webpackConfigPath }, - } - } - } - - return { success: false } - }) - - return success ? payload?.webpackConfigPath : null -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/vue/index.ts b/npm/create-cypress-tests/src/component-testing/templates/vue/index.ts deleted file mode 100644 index 512f4ff6ee8d..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/vue/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Template } from '../Template' -import { VueCliTemplate } from './vueCli' -import { VueWebpackTemplate } from './vueWebpackFile' - -export const vueTemplates: Record> = { - webpack: VueWebpackTemplate, - 'vue-cli': VueCliTemplate, -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/vue/vueCli.test.ts b/npm/create-cypress-tests/src/component-testing/templates/vue/vueCli.test.ts deleted file mode 100644 index 29bb76b76d2f..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/vue/vueCli.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { expect } from 'chai' -import mockFs from 'mock-fs' -import { snapshotPluginsAstCode } from '../../../test-utils' -import { VueCliTemplate } from './vueCli' - -describe('vue webpack-file install template', () => { - beforeEach(mockFs.restore) - - it('resolves webpack.config.js', () => { - mockFs({ - '/package.json': JSON.stringify({ - 'devDependencies': { - '@vue/cli-plugin-babel': '~4.5.0', - '@vue/cli-plugin-eslint': '~4.5.0', - '@vue/cli-plugin-router': '~4.5.0', - '@vue/cli-service': '~4.5.0', - }, - }), - }) - - const { success } = VueCliTemplate.test('/') - - expect(success).to.equal(true) - }) - - it('returns success:false if vue-cli-service is not installed', () => { - mockFs({ - '/package.json': JSON.stringify({ - 'devDependencies': { - 'webpack': '*', - 'vue': '2.x', - }, - }), - }) - - const { success } = VueCliTemplate.test('/') - - expect(success).to.equal(false) - }) - - it('correctly generates plugins for vue-cli-service', () => { - snapshotPluginsAstCode(VueCliTemplate) - }) -}) diff --git a/npm/create-cypress-tests/src/component-testing/templates/vue/vueCli.ts b/npm/create-cypress-tests/src/component-testing/templates/vue/vueCli.ts deleted file mode 100644 index f920c85fa89a..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/vue/vueCli.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as babel from '@babel/core' -import { scanFSForAvailableDependency } from '../../../findPackageJson' -import { Template } from '../Template' - -export const VueCliTemplate: Template = { - message: - 'It looks like you are using vue-cli-service to run and build an application.', - getExampleUrl: () => 'https://github.com/cypress-io/cypress/tree/develop/npm/vue/examples/cli', - recommendedComponentFolder: 'src', - dependencies: ['@cypress/webpack-dev-server'], - getPluginsCodeAst: () => { - return { - requiresReturnConfig: true, - RequireAst: babel.template.ast([ - 'const { startDevServer } = require("@cypress/webpack-dev-server")', - `const webpackConfig = require("@vue/cli-service/webpack.config.js")`, - ].join('\n')), - IfComponentTestingPluginsAst: babel.template.ast([ - `on('dev-server:start', (options) => startDevServer({ options, webpackConfig }))`, - ].join('\n'), { preserveComments: true }), - } - }, - test: (root) => { - const hasVueCliService = scanFSForAvailableDependency(root, { '@vue/cli-service': '>=4' }) - - return { - success: hasVueCliService, - } - }, -} diff --git a/npm/create-cypress-tests/src/component-testing/templates/vue/vueWebpackFile.test.ts b/npm/create-cypress-tests/src/component-testing/templates/vue/vueWebpackFile.test.ts deleted file mode 100644 index e0b058958ef2..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/vue/vueWebpackFile.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { expect } from 'chai' -import mockFs from 'mock-fs' -import { snapshotPluginsAstCode } from '../../../test-utils' -import { VueWebpackTemplate } from './vueWebpackFile' - -describe('vue webpack-file install template', () => { - beforeEach(mockFs.restore) - - it('resolves webpack.config.js', () => { - mockFs({ - '/webpack.config.js': 'module.exports = { }', - }) - - const { success, payload } = VueWebpackTemplate.test(process.cwd()) - - expect(success).to.equal(true) - expect(payload?.webpackConfigPath).to.equal('/webpack.config.js') - }) - - it('finds the closest package.json and tries to fetch webpack config path from scrips', () => { - mockFs({ - '/configs/webpack.js': 'module.exports = { }', - '/package.json': JSON.stringify({ - scripts: { - build: 'webpack --config configs/webpack.js', - }, - }), - }) - - const { success, payload } = VueWebpackTemplate.test(process.cwd()) - - expect(success).to.equal(true) - expect(payload?.webpackConfigPath).to.equal('/configs/webpack.js') - }) - - it('looks for package.json in the upper folder', () => { - mockFs({ - '/some/deep/folder/withFile': 'test', - '/somewhere/configs/webpack.js': 'module.exports = { }', - '/package.json': JSON.stringify({ - scripts: { - build: 'webpack --config somewhere/configs/webpack.js', - }, - }), - }) - - const { success, payload } = VueWebpackTemplate.test( - '/some/deep/folder', - ) - - expect(success).to.equal(true) - expect(payload?.webpackConfigPath).to.equal('/somewhere/configs/webpack.js') - }) - - it('returns success:false if cannot find webpack config', () => { - mockFs({ - '/a.js': '1', - '/b.js': '2', - }) - - const { success, payload } = VueWebpackTemplate.test('/') - - expect(success).to.equal(false) - expect(payload).to.equal(undefined) - }) - - it('correctly generates plugins config when webpack config path is missing', () => { - snapshotPluginsAstCode(VueWebpackTemplate) - }) - - it('correctly generates plugins config when webpack config path is provided', () => { - snapshotPluginsAstCode(VueWebpackTemplate, { webpackConfigPath: '/build/webpack.config.js' }) - }) -}) diff --git a/npm/create-cypress-tests/src/component-testing/templates/vue/vueWebpackFile.ts b/npm/create-cypress-tests/src/component-testing/templates/vue/vueWebpackFile.ts deleted file mode 100644 index 106145b4067c..000000000000 --- a/npm/create-cypress-tests/src/component-testing/templates/vue/vueWebpackFile.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as babel from '@babel/core' -import path from 'path' -import { Template } from '../Template' -import { findWebpackConfig } from '../templateUtils' - -export const VueWebpackTemplate: Template<{ webpackConfigPath: string }> = { - message: - 'It looks like you have custom `webpack.config.js`. We can use it to bundle the components for testing.', - getExampleUrl: () => 'https://github.com/cypress-io/cypress/tree/develop/npm/vue/examples/cli', - recommendedComponentFolder: 'cypress/component', - dependencies: ['@cypress/webpack-dev-server'], - getPluginsCodeAst: (payload, { cypressProjectRoot }) => { - const includeWarnComment = !payload - const webpackConfigPath = payload - ? path.relative(cypressProjectRoot, payload.webpackConfigPath) - : './webpack.config.js' - - return { - requiresReturnConfig: true, - RequireAst: babel.template.ast([ - 'const { startDevServer } = require("@cypress/webpack-dev-server")', - - `const webpackConfig = require("${webpackConfigPath}")`, - includeWarnComment - ? '// TODO replace with valid webpack config path' - : '', - ].join('\n'), { preserveComments: true }), - IfComponentTestingPluginsAst: babel.template.ast([ - `on('dev-server:start', (options) => startDevServer({ options, webpackConfig }))`, - ].join('\n')), - } - }, - test: (root) => { - const webpackConfigPath = findWebpackConfig(root) - - return webpackConfigPath ? { - success: true, - payload: { webpackConfigPath }, - } : { - success: false, - } - }, -} diff --git a/npm/create-cypress-tests/src/component-testing/versions.ts b/npm/create-cypress-tests/src/component-testing/versions.ts deleted file mode 100644 index 734122ff5fe2..000000000000 --- a/npm/create-cypress-tests/src/component-testing/versions.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const MIN_SUPPORTED_VERSION = { - 'react-scripts': '^=3.x || ^=4.x', - next: '^=9.x || ^=10.x', -} diff --git a/npm/create-cypress-tests/src/findPackageJson.ts b/npm/create-cypress-tests/src/findPackageJson.ts deleted file mode 100644 index 81858d319c95..000000000000 --- a/npm/create-cypress-tests/src/findPackageJson.ts +++ /dev/null @@ -1,116 +0,0 @@ -import path from 'path' -import fs from 'fs' -import findUp from 'find-up' -import { validateSemverVersion } from './utils' - -type PackageJsonLike = { - name?: string - scripts?: Record - dependencies?: Record - devDependencies?: Record - [key: string]: unknown -} - -type FindPackageJsonResult = - | { - packageData: PackageJsonLike - filename: string - done: false - } - | { - packageData: undefined - filename: undefined - done: true - } - -/** - * Return the parsed package.json that we find in a parent folder. - * - * @returns {Object} Value, filename and indication if the iteration is done. - */ -export function createFindPackageJsonIterator (rootPath = process.cwd()) { - function scanForPackageJson (cwd: string): FindPackageJsonResult { - const packageJsonPath = findUp.sync('package.json', { cwd }) - - if (!packageJsonPath) { - return { - packageData: undefined, - filename: undefined, - done: true, - } - } - - const packageData = JSON.parse( - fs.readFileSync(packageJsonPath, { - encoding: 'utf-8', - }), - ) - - return { - packageData, - filename: packageJsonPath, - done: false, - } - } - - return { - map: ( - cb: ( - data: PackageJsonLike, - packageJsonPath: string, - ) => { success: boolean, payload?: TPayload }, - ) => { - let stepPathToScan = rootPath - - // eslint-disable-next-line - while (true) { - const result = scanForPackageJson(stepPathToScan) - - if (result.done) { - // didn't find the package.json - return { success: false } - } - - if (result.packageData) { - const cbResult = cb(result.packageData, result.filename) - - if (cbResult.success) { - return { success: true, payload: cbResult.payload } - } - } - - const nextStepPathToScan = path.resolve(stepPathToScan, '..') - - if (nextStepPathToScan === stepPathToScan) { - // we are at the root. Give up - return { success: false } - } - - stepPathToScan = nextStepPathToScan - } - }, - } -} - -export function scanFSForAvailableDependency (cwd: string, lookingForDeps: Record) { - const { success } = createFindPackageJsonIterator(cwd) - .map(({ dependencies, devDependencies }, path) => { - if (!dependencies && !devDependencies) { - return { success: false } - } - - return { - success: Object.entries({ ...dependencies, ...devDependencies }) - .some(([dependency, version]) => { - return ( - Boolean(lookingForDeps[dependency]) - && validateSemverVersion(version, lookingForDeps[dependency] as string, dependency) - ) - }), - } - }) - - return success -} - -export type PackageJsonIterator = ReturnType diff --git a/npm/create-cypress-tests/src/index.ts b/npm/create-cypress-tests/src/index.ts deleted file mode 100644 index 9e6e68e3f4ec..000000000000 --- a/npm/create-cypress-tests/src/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env node -import { program } from 'commander' -import { main } from './main' -import { version } from '../package.json' - -program -.option('--ignore-examples', 'Ignore generating example tests and fixtures by creating one ready-to-fill spec file') -.option('--use-npm', 'Use npm even if yarn is available') -.option('--ignore-ts', 'Ignore typescript if available') -.option('--component-tests', 'Run component testing installation without asking') - -program.version(version, '-v --version') -program.parse(process.argv) - -main({ - useNpm: program.useNpm, - ignoreTs: program.ignoreTs, - ignoreExamples: Boolean(program.ignoreExamples), - setupComponentTesting: program.componentTests, -}).catch(console.error) diff --git a/npm/create-cypress-tests/src/initialTemplate.ts b/npm/create-cypress-tests/src/initialTemplate.ts deleted file mode 100644 index 332df33e3899..000000000000 --- a/npm/create-cypress-tests/src/initialTemplate.ts +++ /dev/null @@ -1,18 +0,0 @@ -import path from 'path' -import fs from 'fs-extra' - -const INITIAL_TEMPLATE_PATH = path.resolve(__dirname, '..', 'initial-template') - -export async function getInitialSupportFilesPaths () { - return ( - await fs.readdir(path.join(INITIAL_TEMPLATE_PATH, 'support')) - ).map((filename) => path.join(INITIAL_TEMPLATE_PATH, 'support', filename)) -} - -export function getInitialPluginsFilePath () { - return path.join(INITIAL_TEMPLATE_PATH, 'plugins', 'index.js') -} - -export function getInitialTsConfigPath () { - return path.join(INITIAL_TEMPLATE_PATH, 'tsconfig.json') -} diff --git a/npm/create-cypress-tests/src/installCypress.ts b/npm/create-cypress-tests/src/installCypress.ts deleted file mode 100644 index 9abd21345b52..000000000000 --- a/npm/create-cypress-tests/src/installCypress.ts +++ /dev/null @@ -1,78 +0,0 @@ -import fs from 'fs-extra' -import findUp from 'find-up' -import path from 'path' -import { installDependency } from './utils' -import chalk from 'chalk' -import ora from 'ora' -import * as initialTemplate from './initialTemplate' - -type InstallCypressOpts = { - useYarn: boolean - useTypescript: boolean - ignoreExamples: boolean -} - -async function copyFiles ({ ignoreExamples, useTypescript }: InstallCypressOpts) { - let fileSpinner = ora('Creating config files').start() - - await fs.outputFile(path.resolve(process.cwd(), useTypescript ? 'cypress.config.ts' : 'cypress.config.js'), useTypescript ? `export default {}` : `module.exports = {}\n`) - await fs.copy( - initialTemplate.getInitialPluginsFilePath(), - path.resolve('cypress', 'plugins/index.js'), - ) - - const supportFiles: string[] = await initialTemplate.getInitialSupportFilesPaths() - - await Promise.all( - supportFiles.map((supportFilePath) => { - const newSupportFilePath = path.resolve('cypress', 'support', path.basename(supportFilePath)) - - return fs.copy(supportFilePath, newSupportFilePath) - }), - ) - - if (useTypescript) { - await fs.copy(initialTemplate.getInitialTsConfigPath(), path.resolve('cypress', 'tsconfig.json')) - } - - // TODO think about better approach - if (ignoreExamples) { - const dummySpec = [ - 'describe("Spec", () => {', - '', - '})', - '', - ].join('\n') - - const specFileName = useTypescript ? 'spec.cy.ts' : 'spec.cy.js' - const specFileToCreate = path.resolve('cypress', 'e2e', specFileName) - - await fs.outputFile(specFileToCreate, dummySpec) - console.log(`In order to ignore examples a spec file ${chalk.green(path.relative(process.cwd(), specFileToCreate))}.`) - } - - fileSpinner.succeed() -} - -export async function findInstalledOrInstallCypress (options: InstallCypressOpts) { - const configFile = options.useTypescript ? 'cypress.config.ts' : 'cypress.config.js' - let cypressConfigPath = await findUp(configFile) - - if (!cypressConfigPath) { - await installDependency('cypress', options) - await copyFiles(options) - - cypressConfigPath = await findUp(configFile) - } - - if (!cypressConfigPath) { - throw new Error('Unexpected error during cypress installation.') - } - - const config = await import(cypressConfigPath) - - return { - cypressConfigPath, - config: config.default, - } -} diff --git a/npm/create-cypress-tests/src/main.test.ts b/npm/create-cypress-tests/src/main.test.ts deleted file mode 100644 index 3b3a81eefa1b..000000000000 --- a/npm/create-cypress-tests/src/main.test.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { expect, use } from 'chai' -import path from 'path' -import sinon, { SinonStub, SinonSpy, SinonSpyCallApi } from 'sinon' -import mockFs from 'mock-fs' -import fsExtra from 'fs-extra' -import { main } from './main' -import sinonChai from 'sinon-chai' -import childProcess from 'child_process' - -use(sinonChai) - -function mockFsWithInitialTemplate (...args: Parameters) { - const [fsConfig, options] = args - - mockFs({ - ...fsConfig, - // @ts-expect-error Load required template files - [path.resolve(__dirname, '..', 'initial-template')]: mockFs.load(path.resolve(__dirname, '..', 'initial-template')), - }, options) -} - -function someOfSpyCallsIncludes (spy: any, logPart: string) { - return spy.getCalls().some( - (spy: SinonSpyCallApi) => { - return spy.args.some((callArg) => typeof callArg === 'string' && callArg.includes(logPart)) - }, - ) -} - -describe('create-cypress-tests', () => { - let promptSpy: SinonStub | null = null - let logSpy: SinonSpy | null = null - let errorSpy: SinonSpy | null = null - let execStub: SinonStub | null = null - let fsCopyStub: SinonStub | null = null - let processExitStub: SinonStub | null = null - - beforeEach(() => { - logSpy = sinon.spy(global.console, 'log') - errorSpy = sinon.spy(global.console, 'error') - // @ts-ignore - execStub = sinon.stub(childProcess, 'exec').callsFake((command, callback) => callback()) - // @ts-ignore - fsCopyStub = sinon.stub(fsExtra, 'copy').returns(Promise.resolve()) - processExitStub = sinon.stub(process, 'exit').callsFake(() => { - throw new Error('process.exit should not be called') - }) - }) - - afterEach(() => { - mockFs.restore() - logSpy?.restore() - promptSpy?.restore() - execStub?.restore() - fsCopyStub?.restore() - processExitStub?.restore() - execStub?.restore() - errorSpy?.restore() - }) - - it('Install cypress if no config found', async () => { - mockFsWithInitialTemplate({ - '/package.json': JSON.stringify({ }), - }) - - await main({ useNpm: false, ignoreTs: false, ignoreExamples: false, setupComponentTesting: false }) - - expect(execStub).calledWith('yarn add cypress --dev') - }) - - it('Uses npm if yarn is not available', async () => { - execStub - ?.onFirstCall().callsFake((command, callback) => callback('yarn is not available')) - ?.onSecondCall().callsFake((command, callback) => callback()) - ?.onThirdCall().callsFake((command, callback) => callback()) - - mockFsWithInitialTemplate({ - '/package.json': JSON.stringify({ }), - }) - - await main({ useNpm: false, ignoreTs: false, ignoreExamples: false, setupComponentTesting: false }) - expect(execStub).calledWith('npm install -D cypress') - }) - - it('Uses npm if --use-npm was provided', async () => { - mockFsWithInitialTemplate({ - '/package.json': JSON.stringify({ }), - }) - - await main({ useNpm: true, ignoreTs: false, ignoreExamples: false, setupComponentTesting: false }) - - expect(execStub).calledWith('npm install -D cypress') - }) - - it('Prints correct commands helper for npm', async () => { - mockFsWithInitialTemplate({ - '/package.json': JSON.stringify({ }), - }) - - await main({ useNpm: true, ignoreTs: false, ignoreExamples: false, setupComponentTesting: false }) - expect(someOfSpyCallsIncludes(logSpy, 'npx cypress open')).to.be.true - }) - - it('Prints correct commands helper for yarn', async () => { - mockFsWithInitialTemplate({ - '/package.json': JSON.stringify({ }), - }) - - await main({ useNpm: false, ignoreTs: false, ignoreExamples: false, setupComponentTesting: false }) - expect(someOfSpyCallsIncludes(logSpy, 'yarn cypress open')).to.be.true - }) - - it('Fails if git repository have untracked or uncommitted files', async () => { - mockFsWithInitialTemplate({ - '/package.json': JSON.stringify({ }), - }) - - execStub?.callsFake((_, callback) => callback(null, { stdout: 'test' })) - processExitStub?.callsFake(() => {}) - - await main({ useNpm: true, ignoreTs: false, ignoreExamples: false, setupComponentTesting: false }) - - expect( - someOfSpyCallsIncludes(errorSpy, 'This repository has untracked files or uncommitted changes.'), - ).to.equal(true) - - expect(processExitStub).to.be.called - }) - - context('e2e fs tests', () => { - const e2eTestOutputPath = path.resolve(__dirname, 'test-output') - - beforeEach(async () => { - fsCopyStub?.restore() - mockFs.restore() - sinon.stub(process, 'cwd').returns(e2eTestOutputPath) - - await fsExtra.remove(e2eTestOutputPath) - await fsExtra.mkdir(e2eTestOutputPath) - }) - - it('Copies plugins and support files', async () => { - await fsExtra.outputFile( - path.join(e2eTestOutputPath, 'package.json'), - JSON.stringify({ name: 'test' }, null, 2), - ) - - await main({ useNpm: true, ignoreTs: false, ignoreExamples: false, setupComponentTesting: false }) - - expect(await fsExtra.pathExists(path.resolve(e2eTestOutputPath, 'cypress', 'plugins', 'index.js'))).to.equal(true) - expect(await fsExtra.pathExists(path.resolve(e2eTestOutputPath, 'cypress', 'support', 'e2e.js'))).to.equal(true) - expect(await fsExtra.pathExists(path.resolve(e2eTestOutputPath, 'cypress', 'support', 'commands.js'))).to.equal(true) - expect(await fsExtra.pathExists(path.resolve(e2eTestOutputPath, 'cypress.config.ts'))).to.equal(true) - }) - - it('Copies tsconfig if typescript is installed', async () => { - await fsExtra.outputFile( - path.join(e2eTestOutputPath, 'package.json'), - JSON.stringify({ - name: 'test-typescript', - dependencies: { typescript: '^4.0.0' }, - }, null, 2), - ) - - await main({ useNpm: false, ignoreTs: false, ignoreExamples: false, setupComponentTesting: false }) - await fsExtra.pathExists(path.resolve(e2eTestOutputPath, 'cypress', 'tsconfig.json')) - console.log(path.resolve(e2eTestOutputPath, 'cypress', 'tsconfig.json')) - }) - }) -}) diff --git a/npm/create-cypress-tests/src/main.ts b/npm/create-cypress-tests/src/main.ts deleted file mode 100644 index fc28840559e6..000000000000 --- a/npm/create-cypress-tests/src/main.ts +++ /dev/null @@ -1,106 +0,0 @@ -import fs from 'fs' -import findUp from 'find-up' -import chalk from 'chalk' -import util from 'util' -import inquirer from 'inquirer' -import { initComponentTesting } from './component-testing/init-component-testing' -import { exec } from 'child_process' -import { scanFSForAvailableDependency } from './findPackageJson' -import { findInstalledOrInstallCypress } from './installCypress' - -type MainArgv = { - useNpm: boolean - ignoreTs: boolean - ignoreExamples: boolean - setupComponentTesting: boolean -} - -async function getGitStatus () { - const execAsync = util.promisify(exec) - - try { - let { stdout } = await execAsync(`git status --porcelain`) - - console.log(stdout) - - return stdout.trim() - } catch (e) { - return '' - } -} - -async function shouldUseYarn () { - const execAsync = util.promisify(exec) - - return execAsync('yarn --version') - .then(() => true) - .catch(() => false) -} - -function shouldUseTypescript () { - return scanFSForAvailableDependency(process.cwd(), { typescript: '*' }) -} - -async function askForComponentTesting () { - const { shouldSetupComponentTesting } = await inquirer.prompt({ - type: 'confirm', - name: 'shouldSetupComponentTesting', - message: `Do you want to setup ${chalk.cyan('component testing')}? ${chalk.grey('You can do this later by rerunning this command')}.`, - }) - - return shouldSetupComponentTesting -} - -function printCypressCommandsHelper (options: { shouldSetupComponentTesting: boolean, useYarn: boolean }) { - const printCommand = (command: string, description: string) => { - const displayedRunner = options.useYarn ? 'yarn' : 'npx' - - console.log() - console.log(chalk.cyan(` ${displayedRunner} ${command}`)) - console.log(` ${description}`) - } - - printCommand('cypress open', 'Opens cypress local development app.') - printCommand('cypress run', 'Runs tests in headless mode.') - - if (options.shouldSetupComponentTesting) { - printCommand('cypress open --component', 'Opens Cypress component testing interactive mode.') - printCommand('cypress run-ct', 'Runs all Cypress component testing suites.') - } -} - -export async function main ({ useNpm, ignoreTs, setupComponentTesting, ignoreExamples }: MainArgv) { - const rootPackageJsonPath = await findUp('package.json') - const useYarn = useNpm === true ? false : await shouldUseYarn() - const useTypescript = ignoreTs ? false : shouldUseTypescript() - - if (!rootPackageJsonPath) { - console.log(`${chalk.bold.red(`It looks like you are running cypress installation wizard outside of npm module.`)}\nIf you would like to setup a new project for cypress tests please run the ${chalk.inverse(useNpm ? ' npm init ' : ' yarn init ')} first.`) - process.exit(1) - } - - const { name = 'unknown', version = '0.0.0' } = JSON.parse(fs.readFileSync(rootPackageJsonPath).toString()) - - console.log(`Running ${chalk.green('cypress 🌲')} installation wizard for ${chalk.cyan(`${name}@${version}`)}`) - - const gitStatus = await getGitStatus() - - if (gitStatus) { - console.error(`\n${chalk.bold.red('This repository has untracked files or uncommitted changes.')}\nThis command will ${chalk.cyan('make changes in the codebase')}, so please remove untracked files, stash or commit any changes, and try again.`) - process.exit(1) - } - - const { config, cypressConfigPath } = await findInstalledOrInstallCypress({ useYarn, useTypescript, ignoreExamples }) - const shouldSetupComponentTesting = setupComponentTesting ?? await askForComponentTesting() - - if (shouldSetupComponentTesting) { - await initComponentTesting({ config, cypressConfigPath, useYarn }) - } - - console.log(`\n👍 Success! Cypress is installed and ready to run tests.`) - printCypressCommandsHelper({ useYarn, shouldSetupComponentTesting }) - - console.log(`\nHappy testing with ${chalk.green('cypress.io')} 🌲\n`) -} - -export { scanFSForAvailableDependency } diff --git a/npm/create-cypress-tests/src/test-utils.ts b/npm/create-cypress-tests/src/test-utils.ts deleted file mode 100644 index 6021690678b9..000000000000 --- a/npm/create-cypress-tests/src/test-utils.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as babel from '@babel/core' -import snapshot from 'snap-shot-it' -import mockFs from 'mock-fs' -import { SinonSpyCallApi } from 'sinon' -import { createTransformPluginsFileBabelPlugin } from './component-testing/babel/babelTransform' -import { Template } from './component-testing/templates/Template' - -export function someOfSpyCallsIncludes (spy: any, logPart: string) { - return spy.getCalls().some( - (spy: SinonSpyCallApi) => { - return spy.args.some((callArg) => typeof callArg === 'string' && callArg.includes(logPart)) - }, - ) -} - -export function snapshotPluginsAstCode (template: Template, payload?: T) { - mockFs.restore() - const code = [ - 'const something = require("something")', - 'module.exports = (on) => {', - '};', - ].join('\n') - - const babelPlugin = createTransformPluginsFileBabelPlugin(template.getPluginsCodeAst(payload ?? null, { cypressProjectRoot: '/' })) - const output = babel.transformSync(code, { - plugins: [babelPlugin], - }) - - if (!output || !output.code) { - throw new Error('Babel transform output is empty.') - } - - snapshot(output.code) -} diff --git a/npm/create-cypress-tests/src/utils.ts b/npm/create-cypress-tests/src/utils.ts deleted file mode 100644 index e79055e60283..000000000000 --- a/npm/create-cypress-tests/src/utils.ts +++ /dev/null @@ -1,76 +0,0 @@ -import semver from 'semver' -import chalk from 'chalk' -import ora from 'ora' -import util from 'util' -import { exec } from 'child_process' - -/** - * Compare available version range with the provided version from package.json - * @param packageName Package name used to display a helper message to user. - */ -export function validateSemverVersion ( - version: string, - allowedVersionRange: string, - packageName?: string, -) { - let isValid: boolean - - try { - const minAvailableVersion = semver.minVersion(version)?.raw - - isValid = Boolean( - minAvailableVersion && - semver.satisfies(minAvailableVersion, allowedVersionRange), - ) - } catch (e) { - // handle not semver versions like "latest", "git:" or "file:" - isValid = false - } - - if (!isValid && packageName) { - const packageNameSymbol = chalk.green(packageName) - - console.warn( - `It seems like you are using ${packageNameSymbol} with version ${chalk.bold( - version, - )}, however we support only ${packageNameSymbol} projects with version ${chalk.bold( - allowedVersionRange, - )}. \n`, - ) - } - - return isValid -} - -export async function installDependency (name: string, options: { useYarn: boolean}) { - const commandToRun = options.useYarn ? `yarn add ${name} --dev` : `npm install -D ${name}` - let cliSpinner = ora(`Installing ${name} ${chalk.gray(`(${commandToRun})`)}`).start() - - try { - // do this inside function for test stubbing - const execAsync = util.promisify(exec) - - await execAsync(commandToRun) - } catch (e) { - cliSpinner.fail(`Can not install ${name} using ${chalk.inverse(commandToRun)})}`) - console.log(e) - - process.exit(1) - } - - cliSpinner.succeed() -} - -export async function prettifyCode (finalCode?: string | null) { - try { - const maybePrettier = require('prettier') - - if (maybePrettier && maybePrettier.format) { - finalCode = maybePrettier.format(finalCode, { parser: 'babel' }) - } - } catch (e) { - return null - } finally { - return finalCode - } -} diff --git a/npm/create-cypress-tests/tsconfig.json b/npm/create-cypress-tests/tsconfig.json deleted file mode 100644 index 5cf13aaaaa09..000000000000 --- a/npm/create-cypress-tests/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "compilerOptions": { - "outDir": "./dist", - "rootDir": ".", - "esModuleInterop": true, - "allowJs": true, - "allowSyntheticDefaultImports": true, - "declaration": true, - "moduleResolution": "node", - "strict": true, - "strictNullChecks": true, - "resolveJsonModule": true, - "module": "CommonJS", - "target": "ES2018", - "types": [ - "node", - ], - "lib": [ - "ES2018" - ], - "noImplicitAny": true - }, - "exclude": [ - "./src/**/*.test.ts", - "node_modules" - ], - "include": [ - "./src/**/*.ts", - ] -} diff --git a/npm/create-cypress-tests/tsconfig.test.json b/npm/create-cypress-tests/tsconfig.test.json deleted file mode 100644 index df01205d9a40..000000000000 --- a/npm/create-cypress-tests/tsconfig.test.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "noEmit": true, - "types": [ - "mocha" - ] - }, - "include": [ - "./src/**/*.test.ts" - ] -} \ No newline at end of file diff --git a/package.json b/package.json index b886660e1576..8576cb33ed98 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "binary-package": "cross-env NODE_OPTIONS=--max_old_space_size=8192 node ./scripts/binary.js package", "check-binary-on-cdn": "node ./scripts/binary.js checkIfBinaryExistsOnCdn", "build": "lerna run build --stream && lerna run build-cli --stream", - "build-prod": "lerna run build-prod --stream --ignore create-cypress-tests && node ./cli/scripts/post-build.js && lerna run build-prod --stream --scope create-cypress-tests --scope", + "build-prod": "lerna run build-prod --stream && node ./cli/scripts/post-build.js && lerna run build-prod --stream --scope", "build-v8-snapshot-dev": "node --max-old-space-size=8192 tooling/v8-snapshot/scripts/setup-v8-snapshot-in-cypress.js --env=dev", "build-v8-snapshot-prod": "node --max-old-space-size=8192 tooling/v8-snapshot/scripts/setup-v8-snapshot-in-cypress.js", "check-node-version": "node scripts/check-node-version.js", diff --git a/packages/example/README.md b/packages/example/README.md index 8d57d43339f8..377f5f731eb4 100644 --- a/packages/example/README.md +++ b/packages/example/README.md @@ -2,7 +2,7 @@ This package is responsible for copying the `cypress/e2e` and `app` files from [`cypress-example-kitchensink`](https://github.com/cypress-io/cypress-example-kitchensink) into the cypress repository. -The `cypress/e2e` tests, pulled into this package from the [kitchen sink app](https://github.com/cypress-io/cypress-example-kitchensink), are used for scaffolding user's e2e tests in `packages/data-context` and in `npm/create-cypress-tests`. +The `cypress/e2e` tests, pulled into this package from the [kitchen sink app](https://github.com/cypress-io/cypress-example-kitchensink), are used for scaffolding user's e2e tests in `packages/data-context`. The `app` content, pulled into this package from the [kitchen sink app](https://github.com/cypress-io/cypress-example-kitchensink), is published to `cypress-io/cypress` repository's Github page, [https://example.cypress.io](https://example.cypress.io). diff --git a/scripts/binary/util/packages.ts b/scripts/binary/util/packages.ts index a45c7827e02f..9653d8faf0c4 100644 --- a/scripts/binary/util/packages.ts +++ b/scripts/binary/util/packages.ts @@ -140,7 +140,7 @@ export const replaceLocalNpmVersions = async function (basePath: string) { let shouldWriteFile = false for (const [depName, version] of Object.entries(dependencies)) { - const matchedPkg = Boolean(depName.startsWith('@cypress/') || depName === 'create-cypress-tests') + const matchedPkg = Boolean(depName.startsWith('@cypress/')) if (!matchedPkg || version !== '0.0.0-development') { continue