@@ -12,100 +12,176 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
13
13
const starterTemplateDir = "../../../starters/nextjs/basic" ;
14
14
15
+ // Define scenarios to test
16
+ interface Scenario {
17
+ name : string ; // Name of the scenario
18
+ setup ?: ( cwd : string ) => Promise < void > ; // Optional setup function before building the app
19
+ tests ?: string [ ] ; // List of test files to run
20
+ }
21
+
22
+ const scenarios : Scenario [ ] = [
23
+ {
24
+ name : "basic" ,
25
+ // No setup needed for basic scenario
26
+ tests : [ "app.spec.ts" ] ,
27
+ } ,
28
+ {
29
+ name : "with-middleware" ,
30
+ setup : async ( cwd : string ) => {
31
+ // Create a middleware.ts file
32
+ const middlewareContent = `
33
+ import type { NextRequest } from 'next/server'
34
+
35
+ export function middleware(request: NextRequest) {
36
+ // This is a simple middleware that doesn't modify the request
37
+ console.log('Middleware executed', request.nextUrl.pathname);
38
+ }
39
+
40
+ export const config = {
41
+ matcher: '/((?!api|_next/static|_next/image|favicon.ico).*)',
42
+ };
43
+ ` ;
44
+
45
+ await fsExtra . writeFile ( join ( cwd , "src" , "middleware.ts" ) , middlewareContent ) ;
46
+ console . log ( `Created middleware.ts file` ) ;
47
+ } ,
48
+ tests : [ "middleware.spec.ts" ] , // Only run middleware-specific tests
49
+ } ,
50
+ ] ;
51
+
15
52
const errors : any [ ] = [ ] ;
16
53
17
54
await rmdir ( join ( __dirname , "runs" ) , { recursive : true } ) . catch ( ( ) => undefined ) ;
18
55
19
- console . log ( "\nBuilding and starting test project..." ) ;
20
-
21
- const runId = Math . random ( ) . toString ( ) . split ( "." ) [ 1 ] ;
22
- const cwd = join ( __dirname , "runs" , runId ) ;
23
- await mkdirp ( cwd ) ;
24
-
25
- console . log ( `[${ runId } ] Copying ${ starterTemplateDir } to working directory` ) ;
26
- await cp ( starterTemplateDir , cwd , { recursive : true } ) ;
27
-
28
- console . log ( `[${ runId } ] > npm ci --silent --no-progress` ) ;
29
- await promiseSpawn ( "npm" , [ "ci" , "--silent" , "--no-progress" ] , {
30
- cwd,
31
- stdio : "inherit" ,
32
- shell : true ,
33
- } ) ;
34
-
35
- const buildScript = relative ( cwd , join ( __dirname , "../dist/bin/build.js" ) ) ;
36
- console . log ( `[${ runId } ] > node ${ buildScript } ` ) ;
37
-
38
- const packageJson = JSON . parse ( readFileSync ( join ( cwd , "package.json" ) , "utf-8" ) ) ;
39
- const frameworkVersion = packageJson . dependencies . next . replace ( "^" , "" ) ;
40
- await promiseSpawn ( "node" , [ buildScript ] , {
41
- cwd,
42
- stdio : "inherit" ,
43
- shell : true ,
44
- env : {
45
- ...process . env ,
46
- FRAMEWORK_VERSION : frameworkVersion ,
47
- } ,
48
- } ) ;
49
-
50
- const bundleYaml = parseYaml ( readFileSync ( join ( cwd , ".apphosting/bundle.yaml" ) ) . toString ( ) ) ;
56
+ // Run each scenario
57
+ for ( const scenario of scenarios ) {
58
+ console . log ( `\n\nRunning scenario: ${ scenario . name } ` ) ;
51
59
52
- const runCommand = bundleYaml . runConfig . runCommand ;
60
+ const runId = `${ scenario . name } -${ Math . random ( ) . toString ( ) . split ( "." ) [ 1 ] } ` ;
61
+ const cwd = join ( __dirname , "runs" , runId ) ;
62
+ await mkdirp ( cwd ) ;
53
63
54
- if ( typeof runCommand !== "string" ) {
55
- throw new Error ( "runCommand must be a string" ) ;
56
- }
64
+ console . log ( `[${ runId } ] Copying ${ starterTemplateDir } to working directory` ) ;
65
+ await cp ( starterTemplateDir , cwd , { recursive : true } ) ;
57
66
58
- const [ runScript , ...runArgs ] = runCommand . split ( " " ) ;
59
- let resolveHostname : ( it : string ) => void ;
60
- let rejectHostname : ( ) => void ;
61
- const hostnamePromise = new Promise < string > ( ( resolve , reject ) => {
62
- resolveHostname = resolve ;
63
- rejectHostname = reject ;
64
- } ) ;
65
- const port = 8080 + Math . floor ( Math . random ( ) * 1000 ) ;
66
- console . log ( `[${ runId } ] > PORT=${ port } ${ runCommand } ` ) ;
67
- const run = spawn ( runScript , runArgs , {
68
- cwd,
69
- shell : true ,
70
- env : {
71
- NODE_ENV : "production" ,
72
- PORT : port . toString ( ) ,
73
- PATH : process . env . PATH ,
74
- } ,
75
- } ) ;
76
- run . stderr . on ( "data" , ( data ) => console . error ( data . toString ( ) ) ) ;
77
- run . stdout . on ( "data" , ( data ) => {
78
- console . log ( data . toString ( ) ) ;
79
- // Check for the "Ready in" message to determine when the server is fully started
80
- if ( data . toString ( ) . includes ( `Ready in` ) ) {
81
- // We use 0.0.0.0 instead of localhost to avoid issues when ipv6 is not available (Node 18)
82
- resolveHostname ( `http://0.0.0.0:${ port } ` ) ;
83
- }
84
- } ) ;
85
- run . on ( "close" , ( code ) => {
86
- if ( code ) {
87
- rejectHostname ( ) ;
67
+ // Run scenario-specific setup if provided
68
+ if ( scenario . setup ) {
69
+ console . log ( `[${ runId } ] Running setup for ${ scenario . name } ` ) ;
70
+ await scenario . setup ( cwd ) ;
88
71
}
89
- } ) ;
90
- const host = await hostnamePromise ;
91
72
92
- console . log ( "\n\n" ) ;
93
-
94
- try {
95
- console . log ( `> HOST=${ host } ts-mocha -p tsconfig.json e2e/*.spec.ts` ) ;
96
- await promiseSpawn ( "ts-mocha" , [ "-p" , "tsconfig.json" , "e2e/*.spec.ts" ] , {
97
- shell : true ,
73
+ console . log ( `[${ runId } ] > npm ci --silent --no-progress` ) ;
74
+ await promiseSpawn ( "npm" , [ "ci" , "--silent" , "--no-progress" ] , {
75
+ cwd,
98
76
stdio : "inherit" ,
99
- env : {
100
- ...process . env ,
101
- HOST : host ,
102
- } ,
103
- } ) . finally ( ( ) => {
104
- run . stdin . end ( ) ;
105
- run . kill ( "SIGKILL" ) ;
77
+ shell : true ,
106
78
} ) ;
107
- } catch ( e ) {
108
- errors . push ( e ) ;
79
+
80
+ const buildScript = relative ( cwd , join ( __dirname , "../dist/bin/build.js" ) ) ;
81
+ const buildLogPath = join ( cwd , "build.log" ) ;
82
+ console . log ( `[${ runId } ] > node ${ buildScript } (output written to ${ buildLogPath } )` ) ;
83
+
84
+ const packageJson = JSON . parse ( readFileSync ( join ( cwd , "package.json" ) , "utf-8" ) ) ;
85
+ const frameworkVersion = packageJson . dependencies . next . replace ( "^" , "" ) ;
86
+
87
+ try {
88
+ await promiseSpawn ( "node" , [ buildScript ] , {
89
+ cwd,
90
+ stdioString : true ,
91
+ stdio : "pipe" ,
92
+ shell : true ,
93
+ env : {
94
+ ...process . env ,
95
+ FRAMEWORK_VERSION : frameworkVersion ,
96
+ } ,
97
+ } ) . then ( ( result ) => {
98
+ // Write stdout and stderr to the log file
99
+ fsExtra . writeFileSync ( buildLogPath , result . stdout + result . stderr ) ;
100
+ } ) ;
101
+
102
+ const bundleYaml = parseYaml ( readFileSync ( join ( cwd , ".apphosting/bundle.yaml" ) ) . toString ( ) ) ;
103
+
104
+ const runCommand = bundleYaml . runConfig . runCommand ;
105
+
106
+ if ( typeof runCommand !== "string" ) {
107
+ throw new Error ( "runCommand must be a string" ) ;
108
+ }
109
+
110
+ const [ runScript , ...runArgs ] = runCommand . split ( " " ) ;
111
+ let resolveHostname : ( it : string ) => void ;
112
+ let rejectHostname : ( ) => void ;
113
+ const hostnamePromise = new Promise < string > ( ( resolve , reject ) => {
114
+ resolveHostname = resolve ;
115
+ rejectHostname = reject ;
116
+ } ) ;
117
+ const port = 8080 + Math . floor ( Math . random ( ) * 1000 ) ;
118
+ const runLogPath = join ( cwd , "run.log" ) ;
119
+ console . log ( `[${ runId } ] > PORT=${ port } ${ runCommand } (output written to ${ runLogPath } )` ) ;
120
+ const runLogStream = fsExtra . createWriteStream ( runLogPath ) ;
121
+
122
+ const run = spawn ( runScript , runArgs , {
123
+ cwd,
124
+ shell : true ,
125
+ env : {
126
+ NODE_ENV : "production" ,
127
+ PORT : port . toString ( ) ,
128
+ PATH : process . env . PATH ,
129
+ } ,
130
+ } ) ;
131
+
132
+ run . stderr . on ( "data" , ( data ) => {
133
+ const output = data . toString ( ) ;
134
+ runLogStream . write ( output ) ;
135
+ } ) ;
136
+
137
+ run . stdout . on ( "data" , ( data ) => {
138
+ const output = data . toString ( ) ;
139
+ runLogStream . write ( output ) ;
140
+ // Check for the "Ready in" message to determine when the server is fully started
141
+ if ( output . includes ( `Ready in` ) ) {
142
+ // We use 0.0.0.0 instead of localhost to avoid issues when ipv6 is not available (Node 18)
143
+ resolveHostname ( `http://0.0.0.0:${ port } ` ) ;
144
+ }
145
+ } ) ;
146
+
147
+ run . on ( "close" , ( code ) => {
148
+ runLogStream . end ( ) ;
149
+ if ( code ) {
150
+ rejectHostname ( ) ;
151
+ }
152
+ } ) ;
153
+ const host = await hostnamePromise ;
154
+
155
+ console . log ( "\n\n" ) ;
156
+
157
+ try {
158
+ // Determine which test files to run
159
+ const testPattern = scenario . tests
160
+ ? scenario . tests . map ( ( test ) => `e2e/${ test } ` ) . join ( " " )
161
+ : "e2e/*.spec.ts" ;
162
+
163
+ console . log (
164
+ `> HOST=${ host } SCENARIO=${ scenario . name } ts-mocha -p tsconfig.json ${ testPattern } ` ,
165
+ ) ;
166
+ await promiseSpawn ( "ts-mocha" , [ "-p" , "tsconfig.json" , ...testPattern . split ( " " ) ] , {
167
+ shell : true ,
168
+ stdio : "inherit" ,
169
+ env : {
170
+ ...process . env ,
171
+ HOST : host ,
172
+ SCENARIO : scenario . name ,
173
+ } ,
174
+ } ) . finally ( ( ) => {
175
+ run . stdin . end ( ) ;
176
+ run . kill ( "SIGKILL" ) ;
177
+ } ) ;
178
+ } catch ( e ) {
179
+ errors . push ( e ) ;
180
+ }
181
+ } catch ( e ) {
182
+ console . error ( `Error in scenario ${ scenario . name } :` , e ) ;
183
+ errors . push ( e ) ;
184
+ }
109
185
}
110
186
111
187
if ( errors . length ) {
0 commit comments