@@ -5,26 +5,35 @@ import { seed } from "@cloudflare/workers-utils/test-helpers";
55import { afterEach , beforeEach , describe , it , vi } from "vitest" ;
66import * as details from "../../../autoconfig/details" ;
77import * as configCache from "../../../config-cache" ;
8+ import * as isInteractiveModule from "../../../is-interactive" ;
89import { clearOutputFilePath } from "../../../output" ;
10+ import { getPackageManager , NpmPackageManager } from "../../../package-manager" ;
911import { PAGES_CONFIG_CACHE_FILENAME } from "../../../pages/constants" ;
1012import { mockConsoleMethods } from "../../helpers/mock-console" ;
1113import { mockConfirm } from "../../helpers/mock-dialogs" ;
1214import { useMockIsTTY } from "../../helpers/mock-istty" ;
1315import { runInTempDir } from "../../helpers/run-in-tmp" ;
1416import type { Config } from "@cloudflare/workers-utils" ;
17+ import type { Mock , MockInstance } from "vitest" ;
1518
1619describe ( "autoconfig details - getDetailsForAutoConfig()" , ( ) => {
1720 runInTempDir ( ) ;
1821 const { setIsTTY } = useMockIsTTY ( ) ;
1922 mockConsoleMethods ( ) ;
23+ let isNonInteractiveOrCISpy : MockInstance ;
2024
2125 beforeEach ( ( ) => {
2226 setIsTTY ( true ) ;
27+ ( getPackageManager as Mock ) . mockResolvedValue ( NpmPackageManager ) ;
28+ isNonInteractiveOrCISpy = vi
29+ . spyOn ( isInteractiveModule , "isNonInteractiveOrCI" )
30+ . mockReturnValue ( false ) ;
2331 } ) ;
2432
2533 afterEach ( ( ) => {
2634 vi . unstubAllGlobals ( ) ;
2735 clearOutputFilePath ( ) ;
36+ isNonInteractiveOrCISpy . mockRestore ( ) ;
2837 } ) ;
2938
3039 it ( "should set configured: true if a configPath exists" , async ( {
@@ -74,9 +83,10 @@ describe("autoconfig details - getDetailsForAutoConfig()", () => {
7483 }
7584 ) ;
7685
77- it ( "should bail when multiple frameworks are detected" , async ( {
86+ it ( "should select the known framework when multiple frameworks are detected but only one is known " , async ( {
7887 expect,
7988 } ) => {
89+ // Gatsby is not in allKnownFrameworks, so only Astro should be considered
8090 await writeFile (
8191 "package.json" ,
8292 JSON . stringify ( {
@@ -87,10 +97,28 @@ describe("autoconfig details - getDetailsForAutoConfig()", () => {
8797 } )
8898 ) ;
8999
100+ const result = await details . getDetailsForAutoConfig ( ) ;
101+
102+ // Should select Astro since it's the only known framework
103+ expect ( result . framework ?. id ) . toBe ( "astro" ) ;
104+ expect ( result . framework ?. name ) . toBe ( "Astro" ) ;
105+ } ) ;
106+
107+ it ( "should bail when run in the root of a workspace" , async ( { expect } ) => {
108+ await seed ( {
109+ "pnpm-workspace.yaml" : "packages:\n - 'packages/*'\n" ,
110+ "package.json" : JSON . stringify ( {
111+ name : "my-workspace" ,
112+ workspaces : [ "packages/*" ] ,
113+ } ) ,
114+ "packages/my-app/package.json" : JSON . stringify ( { name : "my-app" } ) ,
115+ "packages/my-app/index.html" : "<h1>Hello World</h1>" ,
116+ } ) ;
117+
90118 await expect (
91119 details . getDetailsForAutoConfig ( )
92120 ) . rejects . toThrowErrorMatchingInlineSnapshot (
93- `[Error: Wrangler was unable to automatically configure your project to work with Cloudflare, since multiple frameworks were found: Astro, Gatsby ]`
121+ `[Error: The Wrangler application detection logic has been run in the root of a workspace, this is not supported. Change your working directory to one of the applications in the workspace and try again. ]`
94122 ) ;
95123 } ) ;
96124
@@ -341,4 +369,132 @@ describe("autoconfig details - getDetailsForAutoConfig()", () => {
341369 expect ( result . framework ?. id ) . toBe ( "astro" ) ;
342370 } ) ;
343371 } ) ;
372+
373+ describe ( "multiple frameworks detected" , ( ) => {
374+ describe ( "local environment (non-CI)" , ( ) => {
375+ beforeEach ( ( ) => {
376+ isNonInteractiveOrCISpy . mockReturnValue ( false ) ;
377+ } ) ;
378+
379+ it ( "should return a single framework when multiple frameworks are detected" , async ( {
380+ expect,
381+ } ) => {
382+ await writeFile (
383+ "package.json" ,
384+ JSON . stringify ( {
385+ dependencies : {
386+ astro : "5" ,
387+ "@angular/core" : "18" ,
388+ } ,
389+ } )
390+ ) ;
391+
392+ const result = await details . getDetailsForAutoConfig ( ) ;
393+
394+ // Should return a framework (either astro or angular)
395+ expect ( result . framework ) . toBeDefined ( ) ;
396+ expect ( [ "astro" , "angular" ] ) . toContain ( result . framework ?. id ) ;
397+ } ) ;
398+ } ) ;
399+
400+ describe ( "CI environment" , ( ) => {
401+ beforeEach ( ( ) => {
402+ isNonInteractiveOrCISpy . mockReturnValue ( true ) ;
403+ } ) ;
404+
405+ it ( "should throw MultipleFrameworksCIError when multiple known frameworks are detected in CI" , async ( {
406+ expect,
407+ } ) => {
408+ await writeFile (
409+ "package.json" ,
410+ JSON . stringify ( {
411+ dependencies : {
412+ astro : "5" ,
413+ nuxt : "3" ,
414+ } ,
415+ } )
416+ ) ;
417+
418+ await expect ( details . getDetailsForAutoConfig ( ) ) . rejects . toThrowError (
419+ / W r a n g l e r w a s u n a b l e t o a u t o m a t i c a l l y c o n f i g u r e y o u r p r o j e c t t o w o r k w i t h C l o u d f l a r e , s i n c e m u l t i p l e f r a m e w o r k s w e r e f o u n d /
420+ ) ;
421+ } ) ;
422+
423+ it ( "should NOT throw when Vite and another known framework are detected in CI (Vite is filtered out)" , async ( {
424+ expect,
425+ } ) => {
426+ await writeFile (
427+ "package.json" ,
428+ JSON . stringify ( {
429+ dependencies : {
430+ astro : "5" ,
431+ vite : "5" ,
432+ } ,
433+ } )
434+ ) ;
435+
436+ const result = await details . getDetailsForAutoConfig ( ) ;
437+ expect ( result . framework ?. id ) . toBe ( "astro" ) ;
438+ } ) ;
439+
440+ it ( "should throw MultipleFrameworksCIError when multiple unknown frameworks are detected in CI" , async ( {
441+ expect,
442+ } ) => {
443+ await writeFile (
444+ "package.json" ,
445+ JSON . stringify ( {
446+ dependencies : {
447+ gatsby : "5" ,
448+ gridsome : "1" ,
449+ } ,
450+ } )
451+ ) ;
452+
453+ await expect (
454+ details . getDetailsForAutoConfig ( )
455+ ) . rejects . toThrowErrorMatchingInlineSnapshot (
456+ `
457+ [Error: Wrangler was unable to automatically configure your project to work with Cloudflare, since multiple frameworks were found: Gatsby, Gridsome.
458+
459+ To fix this issue either:
460+ - check your project's configuration to make sure that the target framework
461+ is the only configured one and try again
462+ - run \`wrangler setup\` locally to get an interactive user experience where
463+ you can specify what framework you want to target
464+ ]
465+ `
466+ ) ;
467+ } ) ;
468+ } ) ;
469+
470+ it ( "should return non-Vite framework when Vite and another known framework are detected" , async ( {
471+ expect,
472+ } ) => {
473+ await writeFile (
474+ "package.json" ,
475+ JSON . stringify ( {
476+ dependencies : {
477+ next : "14" ,
478+ vite : "5" ,
479+ } ,
480+ } )
481+ ) ;
482+
483+ const result = await details . getDetailsForAutoConfig ( ) ;
484+ expect ( result . framework ?. id ) . toBe ( "next" ) ;
485+ } ) ;
486+
487+ it ( "should fallback to static framework when no frameworks detected" , async ( {
488+ expect,
489+ } ) => {
490+ await seed ( {
491+ "index.html" : "<h1>Hello World</h1>" ,
492+ "package.json" : JSON . stringify ( { } ) ,
493+ } ) ;
494+
495+ const result = await details . getDetailsForAutoConfig ( ) ;
496+
497+ expect ( result . framework ?. id ) . toBe ( "static" ) ;
498+ } ) ;
499+ } ) ;
344500} ) ;
0 commit comments