@@ -32,6 +32,8 @@ import getCompiler from "./helpers/getCompiler";
3232
3333import getCompilerHooks from "./helpers/getCompilerHooks" ;
3434
35+ import { readSseEvents , readSseHandshake , waitUntilValid } from "./helpers/sse" ;
36+
3537// Suppress unnecessary stats output
3638jest . spyOn ( globalThis . console , "log" ) . mockImplementation ( ) ;
3739
@@ -6952,30 +6954,6 @@ describe.each([
69526954 let server ;
69536955 let req ;
69546956
6955- /**
6956- * @param {import("supertest").Test } pending pending supertest request
6957- * @returns {Promise<import("supertest").Response> } resolved response
6958- */
6959- async function readSseHandshake ( pending ) {
6960- return new Promise ( ( resolve , reject ) => {
6961- pending
6962- . buffer ( false )
6963- . parse ( ( res , cb ) => {
6964- res . on ( "data" , ( ) => { } ) ;
6965- res . on ( "end" , ( ) => cb ( null , "" ) ) ;
6966- // SSE never closes on its own; force-close after we have headers.
6967- setTimeout ( ( ) => res . destroy ( ) , 50 ) ;
6968- } )
6969- . end ( ( err , res ) => {
6970- if ( err && err . code !== "ECONNRESET" ) {
6971- reject ( err ) ;
6972- return ;
6973- }
6974- resolve ( res ) ;
6975- } ) ;
6976- } ) ;
6977- }
6978-
69796957 afterEach ( async ( ) => {
69806958 await close ( server , instance ) ;
69816959 } ) ;
@@ -7061,5 +7039,101 @@ describe.each([
70617039 expect ( res . status ) . toBe ( 200 ) ;
70627040 expect ( res . headers [ "content-type" ] ) . toMatch ( / t e x t \/ e v e n t - s t r e a m / ) ;
70637041 } ) ;
7042+
7043+ it ( "sends a permissive CORS header" , async ( ) => {
7044+ const compiler = getCompiler ( webpackConfig ) ;
7045+ [ server , req , instance ] = await frameworkFactory (
7046+ name ,
7047+ framework ,
7048+ compiler ,
7049+ { hot : true } ,
7050+ ) ;
7051+
7052+ const res = await readSseHandshake ( req . get ( "/__webpack_hmr" ) ) ;
7053+
7054+ expect ( res . headers [ "access-control-allow-origin" ] ) . toBe ( "*" ) ;
7055+ } ) ;
7056+
7057+ describe ( "SSE payload" , ( ) => {
7058+ it ( "sends a sync event with the build hash to a client connecting after a build" , async ( ) => {
7059+ const compiler = getCompiler ( webpackConfig ) ;
7060+ [ server , req , instance ] = await frameworkFactory (
7061+ name ,
7062+ framework ,
7063+ compiler ,
7064+ { hot : true } ,
7065+ ) ;
7066+
7067+ await waitUntilValid ( instance ) ;
7068+
7069+ const events = await readSseEvents ( server , "/__webpack_hmr" ) ;
7070+ const sync = events . find ( ( event ) => event . action === "sync" ) ;
7071+
7072+ expect ( sync ) . toBeDefined ( ) ;
7073+ expect ( typeof sync . hash ) . toBe ( "string" ) ;
7074+ expect ( sync . errors ) . toEqual ( [ ] ) ;
7075+ } ) ;
7076+
7077+ it ( "sends a sync event per compilation for a MultiCompiler" , async ( ) => {
7078+ const compiler = getCompiler ( webpackMultiConfig ) ;
7079+ [ server , req , instance ] = await frameworkFactory (
7080+ name ,
7081+ framework ,
7082+ compiler ,
7083+ { hot : true } ,
7084+ ) ;
7085+
7086+ await waitUntilValid ( instance ) ;
7087+
7088+ const events = await readSseEvents ( server , "/__webpack_hmr" ) ;
7089+ const syncs = events . filter ( ( event ) => event . action === "sync" ) ;
7090+
7091+ expect ( syncs ) . toHaveLength ( webpackMultiConfig . length ) ;
7092+ for ( const sync of syncs ) {
7093+ expect ( typeof sync . hash ) . toBe ( "string" ) ;
7094+ }
7095+ } ) ;
7096+
7097+ it ( "streams building and built events on recompilation" , async ( ) => {
7098+ const compiler = getCompiler ( { ...webpackConfig , watch : true } ) ;
7099+ [ server , req , instance ] = await frameworkFactory (
7100+ name ,
7101+ framework ,
7102+ compiler ,
7103+ { hot : true } ,
7104+ ) ;
7105+
7106+ await waitUntilValid ( instance ) ;
7107+
7108+ const pending = readSseEvents ( server , "/__webpack_hmr" , 2000 ) ;
7109+ // Trigger a rebuild once the client is listening.
7110+ setTimeout ( ( ) => instance . invalidate ( ) , 150 ) ;
7111+ const events = await pending ;
7112+ const actions = events . map ( ( event ) => event . action ) ;
7113+
7114+ expect ( actions ) . toContain ( "building" ) ;
7115+ expect ( actions ) . toContain ( "built" ) ;
7116+ } ) ;
7117+
7118+ it ( "broadcasts to every connected client" , async ( ) => {
7119+ const compiler = getCompiler ( webpackConfig ) ;
7120+ [ server , req , instance ] = await frameworkFactory (
7121+ name ,
7122+ framework ,
7123+ compiler ,
7124+ { hot : true } ,
7125+ ) ;
7126+
7127+ await waitUntilValid ( instance ) ;
7128+
7129+ const [ first , second ] = await Promise . all ( [
7130+ readSseEvents ( server , "/__webpack_hmr" ) ,
7131+ readSseEvents ( server , "/__webpack_hmr" ) ,
7132+ ] ) ;
7133+
7134+ expect ( first . some ( ( event ) => event . action === "sync" ) ) . toBe ( true ) ;
7135+ expect ( second . some ( ( event ) => event . action === "sync" ) ) . toBe ( true ) ;
7136+ } ) ;
7137+ } ) ;
70647138 } ) ;
70657139} ) ;
0 commit comments