Search This Blog

Breaking

Saturday, 6 September 2025

September 06, 2025

QA Automation Playground: Hands-On Practice with Playwright & Selenium


QA Automation Playground 🚀


Recently, I started learning Playwright. To get comfortable with its methods and handle real-world automation challenges, I built this interactive web form where you can experiment with different fields, buttons, and complex scenarios.

You can also use this web form to practice your QA automation skills, explore common automation techniques, and gain hands-on experience with Selenium and Playwright.

Open Live Demo in New Tab


Tuesday, 5 December 2023

December 05, 2023

REST Assured API Not Responding While Postman Works – Root Cause & Fix

Recently, I was debugging an issue for a teammate where an API was not responding when executed through Java code. Interestingly, the same API worked perfectly:

  • From my machine
  • From his Postman
  • From other systems

But it failed only when triggered via Java using REST Assured.


The Problem

Here’s the request our code was making:


RestAssured.baseURI = "URL";

RestAssured.useRelaxedHTTPSValidation();

Response response = RestAssured.given()

        .auth()

        .preemptive()

        .basic(username, password)

        .header("Content-Type", "application/json")

        .body(requestBody)

        .post(endPoint)

        .then()

        .extract()

        .response();

                                          .


When executing this code:

  • No response was returned
  • The request remained stuck in execution
  • Debugging didn’t reveal much

The Root Cause

After about an hour of debugging and going through documentation, we discovered something subtle:

👉 By default, REST Assured automatically appends a charset to the Content-Type header.

This behavior can sometimes cause issues if the server is not expecting it.


The Fix

We resolved the issue by disabling the automatic charset addition using EncoderConfig:

given()
.config(RestAssured.config()
.encoderConfig(encoderConfig()
.appendDefaultContentCharsetToContentTypeIfUndefined(false)));

Additional Configuration (Optional)

You can also explicitly define charset behavior:

RestAssured.config = RestAssured.config()
.encoderConfig(encoderConfig()
.defaultContentCharset("US-ASCII"));

Why This Works

  • If no charset is specified:
    • Default content charset → ISO-8859-1
    • Query parameter charset → UTF-8
  • Some servers reject or mishandle unexpected charset values
  • Disabling or controlling this behavior resolves compatibility issues

Interesting Observation

After applying the fix, the API started responding correctly.

However, when we later removed the configuration, it still worked.

We didn’t verify server logs, but it’s likely that:

  • The responses were being served from cache
  • Or the server state had changed after initial successful requests

Key Takeaways

  • REST Assured silently adds charset → can break APIs
  • Always compare working tools like Postman vs code behavior
  • When debugging API issues:
    • Check headers carefully
    • Look for hidden defaults

Conclusion

Sometimes, the issue isn’t with your API or logic—but with default configurations you didn’t even know existed.

A small tweak in REST Assured configuration can save hours of debugging.

Thursday, 23 November 2023

November 23, 2023

Git Clone Fails in Jenkins with “Filename Too Long” Error – Fix


Recently, we started facing an issue in our automation jobs where builds were failing in Jenkins while cloning the Git repository.

The error we observed was:

error: unable to create file
src/......../path/to/that/file/abc.java is too long




Root Cause

After investigating the issue, we found that the root cause was a limitation in Windows.

By default, Windows has a maximum file path length limit of 260 characters.
If the repository contains deeply nested folders or long file names, Git fails to create those files during the clone operation.


Solution

To resolve this issue, we need to enable long path support in Git.

Steps to Fix

  1. Open Git Bash on the affected machine with administrator privileges
  2. Run the following command:

Once this command is executed successfully, the issue gets resolved and Git is able to clone repositories with long file paths.


Key Takeaways

  • Windows has a default file path limit of 260 characters
  • Git cloning may fail for deeply nested repositories
  • Enabling core.longpaths resolves the issue

Final Note

This is a common issue in CI/CD environments like Jenkins when working with large repositories.
It’s a simple fix, but not very obvious if you’re not aware of the Windows limitation.

Friday, 14 July 2023

July 14, 2023

Jenkins AccessDeniedException While Accessing Folder (Works Manually) – Fix

Recently, I encountered an issue where a Jenkins job failed with an AccessDeniedException while trying to access a folder on Windows.

What made it confusing was:

  • The same folder was accessible manually
  • The same code worked when run directly on the machine





Problem

When the Jenkins job tried to access a file inside a folder, it failed with:

AccessDeniedException

However:

  • Manual access → ✅ Working
  • Direct execution on machine → ✅ Working
  • Jenkins-triggered execution → ❌ Failing

Debugging Approach

We analyzed the issue step by step from multiple angles.


1. File Path Construction

We first checked how the file path was being created.

Why?
If Jenkins runs on Linux and your path is Windows-style, it can fail.

Best Practice:
Use Java’s file separator instead of hardcoding paths.

Example:

File.separator

Outcome:
The path was correct.


2. Folder Accessibility

Next, we verified whether the folder was accessible from the machine where Jenkins was running.

Why?
The folder was a shared location, so access permissions matter.

Outcome:
The folder was accessible.


3. Run Code Locally on Same Machine

We executed the same code directly on the Jenkins machine.

Why?
To rule out Java or dependency issues.

Outcome:
Code worked perfectly.


4. Narrowing Down the Issue

At this point:

  • Code works locally ✅
  • Works on Jenkins machine directly ✅
  • Fails only when triggered via Jenkins ❌

👉 This confirmed the issue is Jenkins execution context related, not code-related.


5. Check Jenkins User Permissions

We verified which user Jenkins was using to execute the job.

Why?
Jenkins jobs may run under a different system/service user.

Outcome:
The user appeared to be the same as the logged-in user.


6. The Unexpected Fix

As part of general troubleshooting, we restarted the machine.

Why?
We noticed that when giving folder access to users, a reboot was sometimes required.

Outcome:
After restart, the issue was resolved and access started working.


Final Observation

Even after fixing the issue, one question remained:

  • Why did the code work when run manually on the same machine?
  • But fail only when triggered via Jenkins?

Most likely reasons:

  • Cached permissions
  • Jenkins service session not refreshed
  • OS-level permission propagation delay

Key Takeaways

  • AccessDeniedException is often environment-related, not code-related
  • Jenkins execution context can differ from manual execution
  • Always check:
    • File paths
    • Permissions
    • Execution user
  • Sometimes, a simple system restart resolves hidden permission issues

Closing Thought

This issue looked simple at first but turned out to be tricky due to inconsistent behavior.

If you’ve faced something similar or know the exact root cause behind this behavior, feel free to share—would be interesting to understand it deeper.

Tuesday, 16 May 2023

May 16, 2023

ConcurrentModificationException in Java – Cause and Fix with Examples

Have you ever tried removing elements from an ArrayList (or any collection) while iterating over it?

If yes, you might have encountered a ConcurrentModificationException at runtime.

This usually happens when we try to modify a collection while iterating over it using a loop.








What Does the Exception Mean?

As per Java documentation:

This exception is thrown when a collection is modified while it is being iterated, in a way that is not allowed.


Problem Example

Let’s understand this with a simple example:

List<String> values = new ArrayList<>();
values.add("A");
values.add("b-");
values.add("c");
values.add("D-");

for (String value : values) {
if (value.endsWith("-")) {
values.remove(value);
}
}

What’s Going Wrong?

In the above code:

  • We are iterating over the list
  • At the same time, we are removing elements from it

When the first matching element is removed:

  • The list size changes
  • The iterator (used internally by the for-each loop) becomes inconsistent

👉 This leads to ConcurrentModificationException


Correct Approach (Java 8+)

To safely remove elements while iterating, use removeIf():

values.removeIf(value -> value.endsWith("-"));
System.out.println("Values are: " + values);

Why removeIf() Works

  • It internally uses an iterator
  • Handles removal safely without breaking iteration
  • Cleaner and more readable

Alternative (Pre-Java 8)

If you’re not using Java 8, use an explicit iterator:

Iterator<String> iterator = values.iterator();

while (iterator.hasNext()) {
String value = iterator.next();
if (value.endsWith("-")) {
iterator.remove();
}
}

Key Takeaways

  • Don’t modify a collection directly inside a for-each loop
  • Use removeIf() in Java 8+
  • Or use Iterator.remove() for safe removal
  • ConcurrentModificationException is about unsafe modification during iteration, not multithreading

Friday, 28 April 2023

April 28, 2023

How to Update JSON in Java using JSONPath (Add Fields, Arrays & Objects)

Recently, I came across a situation where I needed to update an existing JSON by:

  • Modifying a value
  • Adding a new field
  • Adding a JSON object (map)
  • Adding a JSON array











Original JSON:

{
"firstName": "John",
"age": 26,
"address": {
"streetAddress": "naist street",
"city": "Nara",
"postalCode": "630-0192"
}
}

Expected JSON:


{ "firstName": "John", "lastName": "Mathpal", "address": { "streetAddress": "naist street", "city":
"Ranikhet", "postalCode": "630-0192", "landmark": "More Market" }, "age": 26.0, "vehicle": { "name": "honda", "model": "GS", "price": "15.8 L" }, "phoneDetails": [{ "type": "iphone", "number": "123" }, { "type": "android", "number": "456" }] }


My first requirement was to update the: city from "Nara" to "Ranikhet".

For this, I used JSONPath DocumentContext and its set() method.

First, I read the JSON file:

String requestBody = readRequestBody(path);
private static String readRequestBody(String requestBodyPath) throws IOException {
File requestBody = new File(requestBodyPath);
return JsonPath.parse(requestBody).jsonString();
}
String valueToBeAddedUpdated = "Ranikhet";
String nodeWhereValueNeedToUpdate = "$.address.city";
requestBody = setJsonDataValue(nodeWhereValueNeedToUpdate, valueToBeAddedUpdated, requestBody);

so to set the value:

private static String setJsonDataValue(String nodeWhereValueNeedToUpdate, Object valueToBeAddedUpdated, String requestBody) {
return JsonPath.parse(requestBody).set(nodeWhereValueNeedToUpdate, valueToBeAddedUpdated).jsonString();
}

It sets the value in the json for city value to :

{

"firstName": "John",

"age": 26,

"address": {

"streetAddress": "naist street",

"city": "Ranikhet",

"postalCode": "630-0192"

}

}


After settign the value, I added a node "lastName": "Mathpal",

requestBody = addJsonNode("$","lastName", "Mathpal",requestBody);
private static String addJsonNode(String jsonpath, String nodeName, String nodeValue, String requestBody) {
DocumentContext documentContext = JsonPath.parse(requestBody);
documentContext.put(JsonPath.compile(jsonpath), nodeName, nodeValue);
String updatedRequestBody = documentContext.jsonString();
return updatedRequestBody;
}

After setting the value, I added a node inside the address: "landmark": "More Market"

requestBody = addJsonNode("$.address","landmark", "More Market",requestBody);

Once this is added, I tried to add a JSONMap vehicle to the existing json.

So to set it, I first prepared a map with vehicle and its details.

String nodeName = "vehicle";
Map<String, Object> finalMap = prepareJsonMapForVehicle(nodeName);
private static Map<String, Object> prepareJsonMapForVehicle(String nodeName) {
Map<String, Object> finalMap = new LinkedHashMap<>();
Map<String, Object> vehicleMap = new LinkedHashMap<>();
vehicleMap.put("name", "honda");
vehicleMap.put("model", "GS");
vehicleMap.put("price", "15.8 L");
finalMap.put(nodeName, vehicleMap);
return finalMap;
}

Once that is done, I used GSON to add that map to the existign requestBody.

requestBody = addJsonMap(requestBody, finalMap);
private static String addJsonMap(String requestBody, Map<String, Object> finalMap) {
HashMap<String, Object> map = new Gson().fromJson(requestBody, HashMap.class);
map.putAll(finalMap);
return new Gson().toJson(map);
}

GSON have 2 methods fromJSON and toJSON which helps to convert java map to json and vice versa.


Once that is done, now added the json array "phoneDetails" field.

So to add it, i first created the jsonArray field as an jsonArray.

String nodeName1 = "phoneDetails";
JsonArray phoneDetails = new JsonArray();

And then added that JSONArray to existing json with the help of JSONPath put method.

 String nodeName1 = "phoneDetails";

JsonArray phoneDetails = new JsonArray();
requestBody =addJsonNodeAsArray("$", nodeName1,phoneDetails,requestBody);

in the addJsonNodeAsArray method, I used the JSONPath put method to set the value.

private static String addJsonNodeAsArray(String jsonpath, String nodeName, JsonArray nodeValue, String requestBody) {
DocumentContext documentContext = JsonPath.parse(requestBody);
documentContext.put(JsonPath.compile(jsonpath), nodeName, nodeValue);
String updatedRequestBody = documentContext.jsonString();
return updatedRequestBody;
}


Once the json Array is added, we cans et value for it.

requestBody = addJsonArrayValues("$.phoneDetails", requestBody,  "iphone", "123") ;
System.out.println("-------------------------------------------------------------------------------------------");

The only change in this method is, that it takes a map while adding. 

private static String addJsonArrayValues(String jsonPath, String requestBody, String arrayNodeName, String arrayNodeValue) {
DocumentContext documentContext= JsonPath.parse(requestBody);
documentContext.add(JsonPath.compile(jsonPath),prepareJsonMapForPhone(arrayNodeName, arrayNodeValue));
String latestIne = documentContext.jsonString();
return latestIne;
}

Once that is done, we can add another json value inside into it too.

requestBody = addJsonArrayValues("$.phoneDetails", requestBody,  "android", "456") ;
System.out.println("After adding another value to the jsonArray inside a json:" +requestBody);


This is how I updated a JSON value and added new nodes, including a JSON array and a JSON object.

April 28, 2023

How to Set Formatted Description in JIRA using API (Rich Text Guide)

During my Jirafication work, I encountered a scenario where a Sonar issue description was not rendered properly while creating a JIRA story. Instead of formatted content, it appeared as plain text.

To troubleshoot this, I first replicated the steps manually and then inspected the request via the browser’s network console. Here are the key findings:










1. To make text bold: just put the text between *.

For Example: 

Input Text: This isssue is related to Thread.

Expected Output: *This isssue is related to Thread*

This will make this line bold when you will hit with your API.

API: {{JIRA_URL}}/ rest/api/latest/issue

Method: POST

{

"fields": {

"description": "*This isssue is related to Thread*"

}

}

2. To make text in color: put the color code like this:

"{color:#0747a6}This isssue is related to Thread.{color}"

For Example: 

Input Text: This isssue is related to Thread.

Expected Output: This isssue is related to Thread


3. To put Java code: "{code:java} your java code {code}"

As of now, I have just used these formattings. I will update others, if will use them.



Wednesday, 19 April 2023

April 19, 2023

Sonar Findings - JIRA FIcation


Recentally, I am working on to JIRAfy all our projects Sonar findings. To get start to implement it, I first created a flow diagram. To segregate Sonar issues, I used sonar rule filter. Rule is a filter on the basis of which all soanr issues are categorized. 

For Example: Thread.sleep(1000) should not be a part of test code. 

Every rule is associated with a rule id. So with the help of rule id, I filtered issues. Once i got all the issues, the next step I took, to create JIRA issues for them. 

To jiraficatiom, I first created a epic mentioning the rule and it's description. Inside the epic, I created a story if it does not exist in JIRA. To verify, that a story alredy exist in jira, I used label as a filter, whose value will be rule. Once the story will be created, we will link subtask to it.





This is right now in initial stage, will share details how it will proceede.





 

Thursday, 30 March 2023

March 30, 2023

[Jenkins] How to pass choice parameter in Jenkins file

 Recentally I was working in converting our pipeline jenkin jobs to Jenkinsfiles so that it can be better managed and tracked. In the jenkin file, I was using declarative pipeline.

In one of the jenkin file, I have to pass the choice parameters from one job to another. 

To implement this, I first created the choice parameter in jenkin file as:


properties([

                 paramteres([

                  choice(choices:['https://www.google.com', 'https://s-www.google.com', 'https://d-www.google.com'], description: 'Select your env?', name:'url')

                ])

])


Then I stored the url value inside a variable: 

def env = params.url


After getting the env, we can pass it in the job as :








pipeline {

           agenet none{

                             stages('') {

                                            parallel {

                                                          stage('Run your first job') {

                                                           steps {

                                                                    script {

                  build(job: 'Job path' , paramters:[string(name:'url', value:String.valueof(params.url))]) 

                                                                   }

                                                               } 

                                                         }

                                                        stage('Run your second job') {

                                                         ......................

                                                       }

                                               }

                                      } // close stages

post {

   always {

    }

}

This allows to pass the chocie aprameter from parent job to downstream jobs.


                    

                               


   

Tuesday, 28 February 2023

February 28, 2023

Jsoup Not Fetching Full Page Data? Fix the maxBodySize Limit Issue

Recently, I encountered an issue in our Jsoup-based automation project where the full page data was not being retrieved.

Document doc = Jsoup.connect(performanceTabURL).get(); 












When I printed the doc object, I noticed that the response did not contain the complete page content.

What made this issue more interesting was that it was environment-specific. On one machine/network (client environment), the response was truncated, while on another network, the same code was able to fetch the complete data without any issues.

Although I couldn’t pinpoint the exact root cause for this inconsistent behavior, I did find a reliable workaround.

Root Cause

By default, Jsoup limits the response body size to 1 MB. If the page content exceeds this limit, the response gets truncated—leading to incomplete data.

Solution

To resolve this, I updated the request configuration to remove the body size limit using the maxBodySize method:

So I tweaked the above code and added maxBodySize which takes size as an integer value.

Document doc = Jsoup.connect(performanceTabURL).maxBodySize(0).get();

Setting the size to 0 removes any restriction on the response body size.

Outcome

With this change, I was able to consistently fetch the complete page data across environments without worrying about size limitations.

If you're working with large HTML responses using Jsoup, this is a small but important tweak that can save you from subtle and hard-to-debug issues.