diff --git a/e2e/react-extensions/src/cypress-component-tests.test.ts b/e2e/react-extensions/src/cypress-component-tests.test.ts
index 1f1ffe073683d..134889ffd9b8e 100644
--- a/e2e/react-extensions/src/cypress-component-tests.test.ts
+++ b/e2e/react-extensions/src/cypress-component-tests.test.ts
@@ -4,6 +4,7 @@ import {
ensureCypressInstallation,
newProject,
runCLI,
+ runCypressTests,
uniq,
updateFile,
updateJson,
@@ -146,18 +147,22 @@ export default Input;
runCLI(
`generate @nx/react:cypress-component-configuration --project=${appName} --generate-tests`
);
- expect(runCLI(`component-test ${appName} --no-watch`)).toContain(
- 'All specs passed!'
- );
+ if (runCypressTests()) {
+ expect(runCLI(`component-test ${appName} --no-watch`)).toContain(
+ 'All specs passed!'
+ );
+ }
}, 300_000);
it('should successfully component test lib being used in app', () => {
runCLI(
`generate @nx/react:cypress-component-configuration --project=${usedInAppLibName} --generate-tests`
);
- expect(runCLI(`component-test ${usedInAppLibName} --no-watch`)).toContain(
- 'All specs passed!'
- );
+ if (runCypressTests()) {
+ expect(runCLI(`component-test ${usedInAppLibName} --no-watch`)).toContain(
+ 'All specs passed!'
+ );
+ }
}, 300_000);
it('should test buildable lib not being used in app', () => {
@@ -184,9 +189,12 @@ describe(Input.name, () => {
runCLI(
`generate @nx/react:cypress-component-configuration --project=${buildableLibName} --generate-tests --build-target=${appName}:build`
);
- expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
- 'All specs passed!'
- );
+
+ if (runCypressTests()) {
+ expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
+ 'All specs passed!'
+ );
+ }
// add tailwind
runCLI(`generate @nx/react:setup-tailwind --project=${buildableLibName}`);
@@ -213,9 +221,11 @@ ${content}`;
}
);
- expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
- 'All specs passed!'
- );
+ if (runCypressTests()) {
+ expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain(
+ 'All specs passed!'
+ );
+ }
}, 300_000);
it('should work with async webpack config', () => {
@@ -250,8 +260,41 @@ ${content}`;
return config;
});
- const results = runCLI(`component-test ${appName}`);
- expect(results).toContain('I am from the custom async Webpack config');
- expect(results).toContain('All specs passed!');
+ if (runCypressTests()) {
+ const results = runCLI(`component-test ${appName}`);
+ expect(results).toContain('I am from the custom async Webpack config');
+ expect(results).toContain('All specs passed!');
+ }
+ });
+
+ // flaky bc of upstream issue https://github.com/cypress-io/cypress/issues/25913
+ it.skip('should CT vite projects importing other projects', () => {
+ const viteLibName = uniq('vite-lib');
+ runCLI(
+ `generate @nrwl/react:lib ${viteLibName} --bundler=vite --no-interactive`
+ );
+
+ updateFile(`libs/${viteLibName}/src/lib/${viteLibName}.tsx`, () => {
+ return `import { Btn } from '@${projectName}/${usedInAppLibName}';
+
+export function MyComponent() {
+ return (
+ <>
+
hello
+ > + ); +} +export default MyComponent;`; + }); + + runCLI( + `generate @nrwl/react:cypress-component-configuration --project=${viteLibName} --generate-tests --bundler=vite --build-target=${appName}:build` + ); + if (runCypressTests()) { + expect(runCLI(`component-test ${viteLibName}`)).toContain( + 'All specs passed!' + ); + } }); }); diff --git a/packages/react/plugins/component-testing/index.ts b/packages/react/plugins/component-testing/index.ts index 300f1a17eb33e..0c55692280dab 100644 --- a/packages/react/plugins/component-testing/index.ts +++ b/packages/react/plugins/component-testing/index.ts @@ -5,6 +5,7 @@ import { import type { CypressExecutorOptions } from '@nx/cypress/src/executors/cypress/cypress.impl'; import { ExecutorContext, + joinPathFragments, logger, parseTargetString, ProjectGraph, @@ -19,7 +20,8 @@ import { getProjectConfigByPath, } from '@nx/cypress/src/utils/ct-helpers'; -import type { Configuration } from 'webpack'; +import { existsSync, lstatSync } from 'fs'; +import { dirname, join } from 'path'; type ViteDevServer = { framework: 'react'; bundler: 'vite'; @@ -66,6 +68,36 @@ export function nxComponentTestingPreset( specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}', devServer: { ...({ framework: 'react', bundler: 'vite' } as const), + viteConfig: async () => { + const normalizedPath = ['.ts', '.js'].some((ext) => + pathToConfig.endsWith(ext) + ) + ? pathToConfig + : dirname(pathToConfig); + const viteConfigPath = findViteConfig(normalizedPath); + + const { mergeConfig, loadConfigFromFile, searchForWorkspaceRoot } = + (await import('vite')) as typeof import('vite'); + + const resolved = await loadConfigFromFile( + { + mode: 'watch', + command: 'serve', + }, + viteConfigPath + ); + return mergeConfig(resolved.config, { + server: { + fs: { + allow: [ + searchForWorkspaceRoot(normalizedPath), + workspaceRoot, + joinPathFragments(workspaceRoot, 'node_modules/vite'), + ], + }, + }, + }); + }, }, }; } @@ -232,3 +264,12 @@ function buildTargetWebpack( }; } } +function findViteConfig(projectRootFullPath: string): string { + const allowsExt = ['js', 'mjs', 'ts', 'cjs', 'mts', 'cts']; + + for (const ext of allowsExt) { + if (existsSync(join(projectRootFullPath, `vite.config.${ext}`))) { + return join(projectRootFullPath, `vite.config.${ext}`); + } + } +} diff --git a/packages/react/src/generators/cypress-component-configuration/lib/add-files.ts b/packages/react/src/generators/cypress-component-configuration/lib/add-files.ts index 0b90fd8b61d09..aae5fb9125c79 100644 --- a/packages/react/src/generators/cypress-component-configuration/lib/add-files.ts +++ b/packages/react/src/generators/cypress-component-configuration/lib/add-files.ts @@ -56,6 +56,13 @@ export async function addFiles( addDependenciesToPackageJson(tree, {}, { '@nx/webpack': nxVersion }); } + if ( + options.bundler === 'vite' || + (!options.bundler && actualBundler === 'vite') + ) { + addDependenciesToPackageJson(tree, {}, { '@nx/vite': nxVersion }); + } + if (options.generateTests) { const filePaths = []; visitNotIgnoredFiles(tree, projectConfig.sourceRoot, (filePath) => { diff --git a/scripts/depcheck/missing.ts b/scripts/depcheck/missing.ts index 5382ecc6c8090..e742883d3c018 100644 --- a/scripts/depcheck/missing.ts +++ b/scripts/depcheck/missing.ts @@ -114,6 +114,8 @@ const IGNORE_MATCHES_IN_PACKAGE = { 'url-loader', 'webpack', 'webpack-merge', + // used via the CT react plugin installed via vite plugin + 'vite', ], 'react-native': ['@nx/storybook'], rollup: ['@swc/core'],