@@ -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
@@ -6975,30 +6977,6 @@ describe.each([
69756977 let server ;
69766978 let req ;
69776979
6978- /**
6979- * @param {import("supertest").Test } pending pending supertest request
6980- * @returns {Promise<import("supertest").Response> } resolved response
6981- */
6982- async function readSseHandshake ( pending ) {
6983- return new Promise ( ( resolve , reject ) => {
6984- pending
6985- . buffer ( false )
6986- . parse ( ( res , cb ) => {
6987- res . on ( "data" , ( ) => { } ) ;
6988- res . on ( "end" , ( ) => cb ( null , "" ) ) ;
6989- // SSE never closes on its own; force-close after we have headers.
6990- setTimeout ( ( ) => res . destroy ( ) , 50 ) ;
6991- } )
6992- . end ( ( err , res ) => {
6993- if ( err && err . code !== "ECONNRESET" ) {
6994- reject ( err ) ;
6995- return ;
6996- }
6997- resolve ( res ) ;
6998- } ) ;
6999- } ) ;
7000- }
7001-
70026980 afterEach ( async ( ) => {
70036981 await close ( server , instance ) ;
70046982 } ) ;
@@ -7084,5 +7062,101 @@ describe.each([
70847062 expect ( res . status ) . toBe ( 200 ) ;
70857063 expect ( res . headers [ "content-type" ] ) . toMatch ( / t e x t \/ e v e n t - s t r e a m / ) ;
70867064 } ) ;
7065+
7066+ it ( "sends a permissive CORS header" , async ( ) => {
7067+ const compiler = getCompiler ( webpackConfig ) ;
7068+ [ server , req , instance ] = await frameworkFactory (
7069+ name ,
7070+ framework ,
7071+ compiler ,
7072+ { hot : true } ,
7073+ ) ;
7074+
7075+ const res = await readSseHandshake ( req . get ( "/__webpack_hmr" ) ) ;
7076+
7077+ expect ( res . headers [ "access-control-allow-origin" ] ) . toBe ( "*" ) ;
7078+ } ) ;
7079+
7080+ describe ( "SSE payload" , ( ) => {
7081+ it ( "sends a sync event with the build hash to a client connecting after a build" , async ( ) => {
7082+ const compiler = getCompiler ( webpackConfig ) ;
7083+ [ server , req , instance ] = await frameworkFactory (
7084+ name ,
7085+ framework ,
7086+ compiler ,
7087+ { hot : true } ,
7088+ ) ;
7089+
7090+ await waitUntilValid ( instance ) ;
7091+
7092+ const events = await readSseEvents ( server , "/__webpack_hmr" ) ;
7093+ const sync = events . find ( ( event ) => event . action === "sync" ) ;
7094+
7095+ expect ( sync ) . toBeDefined ( ) ;
7096+ expect ( typeof sync . hash ) . toBe ( "string" ) ;
7097+ expect ( sync . errors ) . toEqual ( [ ] ) ;
7098+ } ) ;
7099+
7100+ it ( "sends a sync event per compilation for a MultiCompiler" , async ( ) => {
7101+ const compiler = getCompiler ( webpackMultiConfig ) ;
7102+ [ server , req , instance ] = await frameworkFactory (
7103+ name ,
7104+ framework ,
7105+ compiler ,
7106+ { hot : true } ,
7107+ ) ;
7108+
7109+ await waitUntilValid ( instance ) ;
7110+
7111+ const events = await readSseEvents ( server , "/__webpack_hmr" ) ;
7112+ const syncs = events . filter ( ( event ) => event . action === "sync" ) ;
7113+
7114+ expect ( syncs ) . toHaveLength ( webpackMultiConfig . length ) ;
7115+ for ( const sync of syncs ) {
7116+ expect ( typeof sync . hash ) . toBe ( "string" ) ;
7117+ }
7118+ } ) ;
7119+
7120+ it ( "streams building and built events on recompilation" , async ( ) => {
7121+ const compiler = getCompiler ( { ...webpackConfig , watch : true } ) ;
7122+ [ server , req , instance ] = await frameworkFactory (
7123+ name ,
7124+ framework ,
7125+ compiler ,
7126+ { hot : true } ,
7127+ ) ;
7128+
7129+ await waitUntilValid ( instance ) ;
7130+
7131+ const pending = readSseEvents ( server , "/__webpack_hmr" , 2000 ) ;
7132+ // Trigger a rebuild once the client is listening.
7133+ setTimeout ( ( ) => instance . invalidate ( ) , 150 ) ;
7134+ const events = await pending ;
7135+ const actions = events . map ( ( event ) => event . action ) ;
7136+
7137+ expect ( actions ) . toContain ( "building" ) ;
7138+ expect ( actions ) . toContain ( "built" ) ;
7139+ } ) ;
7140+
7141+ it ( "broadcasts to every connected client" , async ( ) => {
7142+ const compiler = getCompiler ( webpackConfig ) ;
7143+ [ server , req , instance ] = await frameworkFactory (
7144+ name ,
7145+ framework ,
7146+ compiler ,
7147+ { hot : true } ,
7148+ ) ;
7149+
7150+ await waitUntilValid ( instance ) ;
7151+
7152+ const [ first , second ] = await Promise . all ( [
7153+ readSseEvents ( server , "/__webpack_hmr" ) ,
7154+ readSseEvents ( server , "/__webpack_hmr" ) ,
7155+ ] ) ;
7156+
7157+ expect ( first . some ( ( event ) => event . action === "sync" ) ) . toBe ( true ) ;
7158+ expect ( second . some ( ( event ) => event . action === "sync" ) ) . toBe ( true ) ;
7159+ } ) ;
7160+ } ) ;
70877161 } ) ;
70887162} ) ;
0 commit comments