@@ -1185,6 +1185,68 @@ public void snapshotSurvivesReadinessThrow() throws Exception {
11851185 assertEquals ("<html></html>" , result .get ("html" ));
11861186 }
11871187
1188+ @ Test
1189+ public void snapshotMergesCliConfigWithPerCallOptionsPrecedence () throws Exception {
1190+ // .percy.yml config carries a config-only key (enableJavaScript) and a
1191+ // percyCSS value that the per-call option should override.
1192+ RemoteWebDriver mockedDriver = mock (RemoteWebDriver .class );
1193+ Percy mockedPercy = spy (new Percy (mockedDriver ));
1194+
1195+ setField (mockedPercy , "isPercyEnabled" , true );
1196+ setField (mockedPercy , "domJs" ,
1197+ "window.PercyDOM = window.PercyDOM || {}; window.PercyDOM.serialize = function(){ return {}; };" );
1198+ setField (mockedPercy , "cliConfig" , new JSONObject ().put ("snapshot" ,
1199+ new JSONObject ()
1200+ .put ("enableJavaScript" , true )
1201+ .put ("percyCSS" , "FROM_CONFIG" )));
1202+ mockedPercy .sessionType = "web" ;
1203+
1204+ when (mockedDriver .getCurrentUrl ()).thenReturn ("https://example.com" );
1205+ WebDriver .Options mockedOptions = mock (WebDriver .Options .class );
1206+ when (mockedDriver .manage ()).thenReturn (mockedOptions );
1207+ when (mockedOptions .getCookies ()).thenReturn (Collections .emptySet ());
1208+ when (mockedDriver .findElements (By .tagName ("iframe" ))).thenReturn (Collections .emptyList ());
1209+
1210+ // Capture every script passed to the JavascriptExecutor so we can inspect
1211+ // the PercyDOM.serialize(...) payload that getSerializedDOM builds.
1212+ ArgumentCaptor <String > scriptCaptor = ArgumentCaptor .forClass (String .class );
1213+ when (((JavascriptExecutor ) mockedDriver ).executeScript (any (String .class )))
1214+ .thenReturn (new HashMap <String , Object >());
1215+
1216+ // Avoid an actual POST back to the CLI.
1217+ doReturn (new JSONObject ()).when (mockedPercy )
1218+ .request (eq ("/percy/snapshot" ), any (JSONObject .class ), eq ("merge precedence" ));
1219+
1220+ Map <String , Object > options = new HashMap <String , Object >();
1221+ options .put ("percyCSS" , "FROM_CALL" );
1222+
1223+ mockedPercy .snapshot ("merge precedence" , options );
1224+
1225+ verify ((JavascriptExecutor ) mockedDriver , atLeastOnce ()).executeScript (scriptCaptor .capture ());
1226+
1227+ String serializeScript = null ;
1228+ for (String script : scriptCaptor .getAllValues ()) {
1229+ if (script != null && script .startsWith ("return PercyDOM.serialize(" )) {
1230+ serializeScript = script ;
1231+ }
1232+ }
1233+ assertNotNull (serializeScript , "PercyDOM.serialize script should have been executed" );
1234+
1235+ // Extract the JSON argument passed to PercyDOM.serialize(...) and assert
1236+ // the merged options reflect config<->per-call precedence.
1237+ String jsonArg = serializeScript
1238+ .substring (serializeScript .indexOf ('(' ) + 1 , serializeScript .lastIndexOf (')' ))
1239+ .trim ();
1240+ JSONObject serialized = new JSONObject (jsonArg );
1241+
1242+ // Config-only key survives the merge.
1243+ assertTrue (serialized .getBoolean ("enableJavaScript" ),
1244+ "enableJavaScript from .percy.yml config should be present in serialized options" );
1245+ // Per-call option wins over the config value.
1246+ assertEquals ("FROM_CALL" , serialized .getString ("percyCSS" ),
1247+ "per-call percyCSS should override the .percy.yml config value" );
1248+ }
1249+
11881250 private static Object invokePrivate (Object target , String methodName , Class <?>[] paramTypes , Object ... args )
11891251 throws Exception {
11901252 Method method = Percy .class .getDeclaredMethod (methodName , paramTypes );
0 commit comments