@@ -3988,6 +3988,7 @@ describe("review.visual (#3609 preview.url_template / #3610 routes)", () => {
39883988 } ,
39893989 } ) ;
39903990 expect ( m . review . visual ) . toEqual ( {
3991+ productionUrl : null ,
39913992 preview : { urlTemplate : "https://pr-{number}.preview.example.com" } ,
39923993 routes : { paths : [ "/pricing" , "/docs" ] , maxRoutes : 3 } ,
39933994 themes : [ ] ,
@@ -4090,7 +4091,68 @@ describe("review.visual (#3609 preview.url_template / #3610 routes)", () => {
40904091 it ( "resolveReviewVisualConfig: null manifest yields empty defaults; a set manifest passes through" , ( ) => {
40914092 expect ( resolveReviewVisualConfig ( null ) ) . toEqual ( { ...EMPTY_VISUAL_CONFIG } ) ;
40924093 const manifest = parseFocusManifest ( { review : { visual : { routes : { paths : [ "/app" ] } } } } ) ;
4093- expect ( resolveReviewVisualConfig ( manifest ) ) . toEqual ( { preview : { urlTemplate : null } , routes : { paths : [ "/app" ] , maxRoutes : null } , themes : [ ] , gif : false , enabled : null , themeStorageKey : null , actionsFallback : false } ) ;
4094+ expect ( resolveReviewVisualConfig ( manifest ) ) . toEqual ( { productionUrl : null , preview : { urlTemplate : null } , routes : { paths : [ "/app" ] , maxRoutes : null } , themes : [ ] , gif : false , enabled : null , themeStorageKey : null , actionsFallback : false } ) ;
4095+ } ) ;
4096+ } ) ;
4097+
4098+ describe ( "review.visual.production_url (#3611 follow-up — per-repo override of the global PUBLIC_SITE_ORIGIN env var)" , ( ) => {
4099+ it ( "parses a valid production_url, marks present, and round-trips" , ( ) => {
4100+ const m = parseFocusManifest ( { review : { visual : { production_url : "https://metagraph.sh" } } } ) ;
4101+ expect ( m . review . visual . productionUrl ) . toBe ( "https://metagraph.sh" ) ;
4102+ expect ( m . review . present ) . toBe ( true ) ;
4103+ expect ( reviewConfigToJson ( m . review ) ) . toEqual ( { visual : { production_url : "https://metagraph.sh" } } ) ;
4104+ } ) ;
4105+
4106+ it ( "absent production_url stays null and does not mark review present on its own" , ( ) => {
4107+ expect ( parseFocusManifest ( { } ) . review . visual . productionUrl ) . toBeNull ( ) ;
4108+ expect ( parseFocusManifest ( { review : { visual : { } } } ) . review . present ) . toBe ( false ) ;
4109+ } ) ;
4110+
4111+ it ( "rejects a non-HTTPS production_url with a warning" , ( ) => {
4112+ const bad = parseFocusManifest ( { review : { visual : { production_url : "http://metagraph.sh" } } } ) ;
4113+ expect ( bad . review . visual . productionUrl ) . toBeNull ( ) ;
4114+ expect ( bad . warnings . some ( ( w ) => / r e v i e w \. v i s u a l \. p r o d u c t i o n _ u r l .* v a l i d H T T P S U R L / . test ( w ) ) ) . toBe ( true ) ;
4115+ } ) ;
4116+
4117+ it ( "rejects a production_url resolving to a private/internal host with a warning" , ( ) => {
4118+ const bad = parseFocusManifest ( { review : { visual : { production_url : "https://prod.internal" } } } ) ;
4119+ expect ( bad . review . visual . productionUrl ) . toBeNull ( ) ;
4120+ expect ( bad . warnings . some ( ( w ) => / r e v i e w \. v i s u a l \. p r o d u c t i o n _ u r l .* v a l i d H T T P S U R L / . test ( w ) ) ) . toBe ( true ) ;
4121+ } ) ;
4122+
4123+ it ( "rejects a malformed production_url with a warning" , ( ) => {
4124+ const bad = parseFocusManifest ( { review : { visual : { production_url : "not-a-url-at-all" } } } ) ;
4125+ expect ( bad . review . visual . productionUrl ) . toBeNull ( ) ;
4126+ expect ( bad . warnings . some ( ( w ) => / r e v i e w \. v i s u a l \. p r o d u c t i o n _ u r l .* v a l i d H T T P S U R L / . test ( w ) ) ) . toBe ( true ) ;
4127+ } ) ;
4128+
4129+ it ( "composes with preview.url_template — both configured independently and both round-trip" , ( ) => {
4130+ const m = parseFocusManifest ( {
4131+ review : { visual : { production_url : "https://metagraph.sh" , preview : { url_template : "https://pr-{number}.example.com" } } } ,
4132+ } ) ;
4133+ expect ( m . review . visual . productionUrl ) . toBe ( "https://metagraph.sh" ) ;
4134+ expect ( m . review . visual . preview . urlTemplate ) . toBe ( "https://pr-{number}.example.com" ) ;
4135+ expect ( reviewConfigToJson ( m . review ) ) . toEqual ( {
4136+ visual : { production_url : "https://metagraph.sh" , preview : { url_template : "https://pr-{number}.example.com" } } ,
4137+ } ) ;
4138+ } ) ;
4139+
4140+ it ( "resolveReviewVisualConfig passes a configured production_url through" , ( ) => {
4141+ const manifest = parseFocusManifest ( { review : { visual : { production_url : "https://metagraph.sh" } } } ) ;
4142+ expect ( resolveReviewVisualConfig ( manifest ) . productionUrl ) . toBe ( "https://metagraph.sh" ) ;
4143+ } ) ;
4144+
4145+ it ( "overlay: a per-repo production_url wins over a global-default value" , ( ) => {
4146+ const globalDefault = parseReviewConfigMapping ( { visual : { production_url : "https://gittensory.aethereal.dev" } } , [ ] ) ;
4147+ const perRepo = parseReviewConfigMapping ( { visual : { production_url : "https://metagraph.sh" } } , [ ] ) ;
4148+ expect ( overlayReviewConfig ( globalDefault , perRepo ) . visual . productionUrl ) . toBe ( "https://metagraph.sh" ) ;
4149+ } ) ;
4150+
4151+ it ( "overlay: an unset per-repo production_url falls back to the global-default value" , ( ) => {
4152+ const globalDefault = parseReviewConfigMapping ( { visual : { production_url : "https://gittensory.aethereal.dev" } } , [ ] ) ;
4153+ const perRepo = parseReviewConfigMapping ( { visual : { routes : { paths : [ "/app" ] } } } , [ ] ) ;
4154+ expect ( overlayReviewConfig ( globalDefault , perRepo ) . visual . productionUrl ) . toBe ( "https://gittensory.aethereal.dev" ) ;
4155+ expect ( overlayReviewConfig ( globalDefault , perRepo ) . visual . routes . paths ) . toEqual ( [ "/app" ] ) ;
40944156 } ) ;
40954157} ) ;
40964158
@@ -4175,7 +4237,7 @@ describe("review.visual.gif (#3612 scroll-through GIF capture)", () => {
41754237
41764238 it ( "composes with themes — both configured independently and both round-trip" , ( ) => {
41774239 const m = parseFocusManifest ( { review : { visual : { gif : true , themes : [ "dark" ] } } } ) ;
4178- expect ( m . review . visual ) . toEqual ( { preview : { urlTemplate : null } , routes : { paths : [ ] , maxRoutes : null } , themes : [ "dark" ] , gif : true , enabled : null , themeStorageKey : null , actionsFallback : false } ) ;
4240+ expect ( m . review . visual ) . toEqual ( { productionUrl : null , preview : { urlTemplate : null } , routes : { paths : [ ] , maxRoutes : null } , themes : [ "dark" ] , gif : true , enabled : null , themeStorageKey : null , actionsFallback : false } ) ;
41794241 expect ( reviewConfigToJson ( m . review ) ) . toEqual ( { visual : { themes : [ "dark" ] , gif : true } } ) ;
41804242 } ) ;
41814243
@@ -4278,7 +4340,7 @@ describe("review.visual.theme_storage_key (#4109 localStorage theme-forcing fall
42784340
42794341 it ( "composes with themes — both configured independently and both round-trip" , ( ) => {
42804342 const m = parseFocusManifest ( { review : { visual : { themes : [ "dark" ] , theme_storage_key : "theme" } } } ) ;
4281- expect ( m . review . visual ) . toEqual ( { preview : { urlTemplate : null } , routes : { paths : [ ] , maxRoutes : null } , themes : [ "dark" ] , gif : false , enabled : null , themeStorageKey : "theme" , actionsFallback : false } ) ;
4343+ expect ( m . review . visual ) . toEqual ( { productionUrl : null , preview : { urlTemplate : null } , routes : { paths : [ ] , maxRoutes : null } , themes : [ "dark" ] , gif : false , enabled : null , themeStorageKey : "theme" , actionsFallback : false } ) ;
42824344 expect ( reviewConfigToJson ( m . review ) ) . toEqual ( { visual : { themes : [ "dark" ] , theme_storage_key : "theme" } } ) ;
42834345 } ) ;
42844346
@@ -4333,7 +4395,7 @@ describe("review.visual.actions_fallback (#4112 GitHub-Actions build-and-serve f
43334395
43344396 it ( "composes with gif — both configured independently and both round-trip" , ( ) => {
43354397 const m = parseFocusManifest ( { review : { visual : { actions_fallback : true , gif : true } } } ) ;
4336- expect ( m . review . visual ) . toEqual ( { preview : { urlTemplate : null } , routes : { paths : [ ] , maxRoutes : null } , themes : [ ] , gif : true , enabled : null , themeStorageKey : null , actionsFallback : true } ) ;
4398+ expect ( m . review . visual ) . toEqual ( { productionUrl : null , preview : { urlTemplate : null } , routes : { paths : [ ] , maxRoutes : null } , themes : [ ] , gif : true , enabled : null , themeStorageKey : null , actionsFallback : true } ) ;
43374399 expect ( reviewConfigToJson ( m . review ) ) . toEqual ( { visual : { gif : true , actions_fallback : true } } ) ;
43384400 } ) ;
43394401
0 commit comments