1
1
/* eslint-env jest */
2
2
3
- import cheerio from 'cheerio'
4
3
import { join } from 'path'
5
4
import fs from 'fs-extra'
6
5
import webdriver from 'next-webdriver'
@@ -14,10 +13,11 @@ import {
14
13
nextBuild as _nextBuild ,
15
14
nextStart as _nextStart ,
16
15
renderViaHTTP ,
17
- check ,
18
16
} from 'next-test-utils'
19
17
20
18
import css from './css'
19
+ import rsc from './rsc'
20
+ import streaming from './streaming'
21
21
22
22
const nodeArgs = [ '-r' , join ( __dirname , '../../react-18/test/require-hook.js' ) ]
23
23
const appDir = join ( __dirname , '../app' )
@@ -123,20 +123,6 @@ async function nextDev(dir, port) {
123
123
} )
124
124
}
125
125
126
- async function resolveStreamResponse ( response , onData ) {
127
- let result = ''
128
- onData = onData || ( ( ) => { } )
129
- await new Promise ( ( resolve ) => {
130
- response . body . on ( 'data' , ( chunk ) => {
131
- result += chunk . toString ( )
132
- onData ( chunk . toString ( ) , result )
133
- } )
134
-
135
- response . body . on ( 'end' , resolve )
136
- } )
137
- return result
138
- }
139
-
140
126
describe ( 'concurrentFeatures - basic' , ( ) => {
141
127
it ( 'should warn user for experimental risk with server components' , async ( ) => {
142
128
const edgeRuntimeWarning =
@@ -308,197 +294,43 @@ runSuite('document', 'dev', documentSuite)
308
294
runSuite ( 'document' , 'prod' , documentSuite )
309
295
310
296
async function runBasicTests ( context , env ) {
311
- const isDev = env === 'dev'
312
- it ( 'should render html correctly' , async ( ) => {
313
- const homeHTML = await renderViaHTTP ( context . appPort , '/' , null , {
314
- headers : {
315
- 'x-next-test-client' : 'test-util' ,
316
- } ,
317
- } )
318
-
319
- // should have only 1 DOCTYPE
320
- expect ( homeHTML ) . toMatch ( / ^ < ! D O C T Y P E h t m l > < h t m l / )
321
-
322
- // dynamic routes
323
- const dynamicRouteHTML1 = await renderViaHTTP (
324
- context . appPort ,
325
- '/routes/dynamic1'
326
- )
327
- const dynamicRouteHTML2 = await renderViaHTTP (
328
- context . appPort ,
329
- '/routes/dynamic2'
330
- )
331
-
332
- const path404HTML = await renderViaHTTP ( context . appPort , '/404' )
297
+ it ( 'should render 500 error correctly' , async ( ) => {
333
298
const path500HTML = await renderViaHTTP ( context . appPort , '/err' )
334
- const pathNotFoundHTML = await renderViaHTTP (
335
- context . appPort ,
336
- '/this-is-not-found'
337
- )
338
-
339
- const page404Content = 'custom-404-page'
340
-
341
- expect ( homeHTML ) . toContain ( 'component:index.server' )
342
- expect ( homeHTML ) . toContain ( 'env:env_var_test' )
343
- expect ( homeHTML ) . toContain ( 'header:test-util' )
344
- expect ( homeHTML ) . toContain ( 'path:/' )
345
- expect ( homeHTML ) . toContain ( 'foo.client' )
346
-
347
- expect ( dynamicRouteHTML1 ) . toContain ( 'query: dynamic1' )
348
- expect ( dynamicRouteHTML2 ) . toContain ( 'query: dynamic2' )
349
-
350
- const $404 = cheerio . load ( path404HTML )
351
- expect ( $404 ( '#__next' ) . text ( ) ) . toBe ( page404Content )
352
299
353
- // In dev mode: it should show the error popup.
300
+ // In dev mode it should show the error popup.
301
+ const isDev = env === 'dev'
354
302
expect ( path500HTML ) . toContain ( isDev ? 'Error: oops' : 'custom-500-page' )
355
- expect ( pathNotFoundHTML ) . toContain ( page404Content )
356
303
} )
357
304
358
- it ( 'should disable cache for fizz pages' , async ( ) => {
359
- const urls = [ '/' , '/next-api/image' , '/next-api/link' ]
360
- await Promise . all (
361
- urls . map ( async ( url ) => {
362
- const { headers } = await fetchViaHTTP ( context . appPort , url )
363
- expect ( headers . get ( 'cache-control' ) ) . toBe (
364
- 'no-cache, no-store, max-age=0, must-revalidate'
365
- )
366
- } )
367
- )
368
- } )
369
-
370
- it ( 'should support next/link' , async ( ) => {
371
- const linkHTML = await renderViaHTTP ( context . appPort , '/next-api/link' )
372
- const $ = cheerio . load ( linkHTML )
373
- const linkText = $ ( 'div[hidden] > a[href="/"]' ) . text ( )
374
-
375
- expect ( linkText ) . toContain ( 'go home' )
376
-
377
- const browser = await webdriver ( context . appPort , '/next-api/link' )
378
-
379
- // We need to make sure the app is fully hydrated before clicking, otherwise
380
- // it will be a full redirection instead of being taken over by the next
381
- // router. This timeout prevents it being flaky caused by fast refresh's
382
- // rebuilding event.
383
- await new Promise ( ( res ) => setTimeout ( res , 1000 ) )
384
- await browser . eval ( 'window.beforeNav = 1' )
385
-
386
- await browser . waitForElementByCss ( '#next_id' ) . click ( )
387
- await check ( ( ) => browser . elementByCss ( '#query' ) . text ( ) , 'query:1' )
388
-
389
- await browser . waitForElementByCss ( '#next_id' ) . click ( )
390
- await check ( ( ) => browser . elementByCss ( '#query' ) . text ( ) , 'query:2' )
391
-
392
- expect ( await browser . eval ( 'window.beforeNav' ) ) . toBe ( 1 )
393
- } )
394
-
395
- it ( 'should suspense next/image on server side' , async ( ) => {
396
- const imageHTML = await renderViaHTTP ( context . appPort , '/next-api/image' )
397
- const $ = cheerio . load ( imageHTML )
398
- const imageTag = $ ( 'div[hidden] > span > span > img' )
305
+ it ( 'should render 404 error correctly' , async ( ) => {
306
+ const path404HTML = await renderViaHTTP ( context . appPort , '/404' )
307
+ const pathNotFoundHTML = await renderViaHTTP ( context . appPort , '/not-found' )
399
308
400
- expect ( imageTag . attr ( 'src' ) ) . toContain ( 'data:image' )
309
+ expect ( path404HTML ) . toContain ( 'custom-404-page' )
310
+ expect ( pathNotFoundHTML ) . toContain ( 'custom-404-page' )
401
311
} )
402
312
403
- it ( 'should handle multiple named exports correctly' , async ( ) => {
404
- const clientExportsHTML = await renderViaHTTP (
313
+ it ( 'should render dynamic routes correctly' , async ( ) => {
314
+ const dynamicRoute1HTML = await renderViaHTTP (
405
315
context . appPort ,
406
- '/client-exports'
407
- )
408
- const $clientExports = cheerio . load ( clientExportsHTML )
409
- expect ( $clientExports ( 'div[hidden] > div' ) . text ( ) ) . toBe ( 'abcde' )
410
-
411
- const browser = await webdriver ( context . appPort , '/client-exports' )
412
- const text = await browser . waitForElementByCss ( '#__next' ) . text ( )
413
- expect ( text ) . toBe ( 'abcde' )
414
- } )
415
-
416
- it ( 'should support multi-level server component imports' , async ( ) => {
417
- const html = await renderViaHTTP ( context . appPort , '/multi' )
418
- expect ( html ) . toContain ( 'bar.server.js:' )
419
- expect ( html ) . toContain ( 'foo.client' )
420
- } )
421
-
422
- it ( 'should support streaming' , async ( ) => {
423
- await fetchViaHTTP ( context . appPort , '/streaming' , null , { } ) . then (
424
- async ( response ) => {
425
- let gotFallback = false
426
- let gotData = false
427
-
428
- await resolveStreamResponse ( response , ( _ , result ) => {
429
- gotData = result . includes ( 'next_streaming_data' )
430
- if ( ! gotFallback ) {
431
- gotFallback = result . includes ( 'next_streaming_fallback' )
432
- if ( gotFallback ) {
433
- expect ( gotData ) . toBe ( false )
434
- }
435
- }
436
- } )
437
-
438
- expect ( gotFallback ) . toBe ( true )
439
- expect ( gotData ) . toBe ( true )
440
- }
441
- )
442
-
443
- // Should end up with "next_streaming_data".
444
- const browser = await webdriver ( context . appPort , '/streaming' )
445
- const content = await browser . eval ( `window.document.body.innerText` )
446
- expect ( content ) . toMatchInlineSnapshot ( '"next_streaming_data"' )
447
- } )
448
-
449
- it ( 'should support streaming flight request' , async ( ) => {
450
- await fetchViaHTTP ( context . appPort , '/?__flight__=1' ) . then (
451
- async ( response ) => {
452
- const result = await resolveStreamResponse ( response )
453
- expect ( result ) . toContain ( 'component:index.server' )
454
- }
316
+ '/routes/dynamic1'
455
317
)
456
- } )
457
-
458
- it ( 'should support partial hydration with inlined server data' , async ( ) => {
459
- await fetchViaHTTP ( context . appPort , '/partial-hydration' , null , { } ) . then (
460
- async ( response ) => {
461
- let gotFallback = false
462
- let gotData = false
463
- let gotInlinedData = false
464
-
465
- await resolveStreamResponse ( response , ( _ , result ) => {
466
- gotInlinedData = result . includes ( 'self.__next_s=' )
467
- gotData = result . includes ( 'next_streaming_data' )
468
- if ( ! gotFallback ) {
469
- gotFallback = result . includes ( 'next_streaming_fallback' )
470
- if ( gotFallback ) {
471
- expect ( gotData ) . toBe ( false )
472
- expect ( gotInlinedData ) . toBe ( false )
473
- }
474
- }
475
- } )
476
-
477
- expect ( gotFallback ) . toBe ( true )
478
- expect ( gotData ) . toBe ( true )
479
- expect ( gotInlinedData ) . toBe ( true )
480
- }
318
+ const dynamicRoute2HTML = await renderViaHTTP (
319
+ context . appPort ,
320
+ '/routes/dynamic2'
481
321
)
482
322
483
- // Should end up with "next_streaming_data".
484
- const browser = await webdriver ( context . appPort , '/partial-hydration' )
485
- const content = await browser . eval ( `window.document.body.innerText` )
486
- expect ( content ) . toContain ( 'next_streaming_data' )
487
-
488
- // Should support partial hydration: the boundary should still be pending
489
- // while another part is hydrated already.
490
- expect ( await browser . eval ( `window.partial_hydration_suspense_result` ) ) . toBe (
491
- 'next_streaming_fallback'
492
- )
493
- expect ( await browser . eval ( `window.partial_hydration_counter_result` ) ) . toBe (
494
- 'count: 1'
495
- )
323
+ expect ( dynamicRoute1HTML ) . toContain ( 'query: dynamic1' )
324
+ expect ( dynamicRoute2HTML ) . toContain ( 'query: dynamic2' )
496
325
} )
497
326
498
327
it ( 'should support api routes' , async ( ) => {
499
328
const res = await renderViaHTTP ( context . appPort , '/api/ping' )
500
329
expect ( res ) . toContain ( 'pong' )
501
330
} )
331
+
332
+ rsc ( context )
333
+ streaming ( context )
502
334
}
503
335
504
336
function runSuite ( suiteName , env , { runTests, before, after } ) {
0 commit comments