# Gitlab CI/CD: JAVA/Playwright

<figure><img src="/files/jraGcsgxvTzwCGTFBmpn" alt=""><figcaption></figcaption></figure>

## Basic GitLab CI/CD & aqua workflow

This guide demonstrates integrating automated testing into your GitLab CI/CD pipeline, specifically using Maven project with *JAVA/Playwright.* However, the steps can be adapted for any test automation tool or framework to submit results to aqua.

#### Prerequisites

* a GitLab project with CI/CD enabled
* an aqua account to submit test results
* an application or project configured with Maven project (e.g. using Visual Studi Code)

First, generate clean maven project or use existing one.

<figure><img src="/files/e9I6NbA82zniNG5THmWL" alt=""><figcaption></figcaption></figure>

Make sure the following dependencies are added to the **pom.xml** file We will need *Playwright, JUnit* and *JSON* libraries.

<figure><img src="/files/fH5kowVVOPVppKFARMME" alt=""><figcaption></figcaption></figure>

Create a TestBase class to keep all general methods and implementations of the common logic and hooks: *BeforeAll, BeforeEach, AfterAll, AfterEach, etc.*&#x20;

Create a TestCase class which extends the TestBase class and implements actual logic of a particular test. Here and further we assume that this class has an aqua Test Case instance so we will update its execution info in aqua web app. Test Case has one Step (Step 1) in aqua.&#x20;

TestCase class implements simple actions: open [www.wikipedia.org](https://www.wikipedia.org/) web site using playwright actions, do a few clicks there and check actual Web URL:&#x20;

<figure><img src="/files/IqVPDFqUWIJMb9iOJIrm" alt=""><figcaption></figcaption></figure>

Using @AfterEach hook in TestBase class connect to aqua and get bearer token from REST GET request sent by playwright  <br>

<figure><img src="/files/LLYjZNs7KqADdaxl0dWi" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/ng1LC4gpOe0Acq83UXYP" alt=""><figcaption></figcaption></figure>

The above token will be needed for POST request to create an aqua Test Case execution. We will use the following endpoint and JSON schema:&#x20;

<https://app.aqua-cloud.io/aquaWebNG/Help#tag/TestExecution/operation/TestExecution_Create>&#x20;

Example implementation in TestBase class for the above request looks as follows:

```
static void createTCExecution(TestInfo tcInfo, String status){ 

   Map<String, String> headers = new HashMap<>(); 

   headers.put("authorization", "Bearer " + bearerToken); 

   headers.put("content-type", "application/json"); 

   headers.put("accept", "application/json"); 

   headers.put("Connection", "keep-alive"); 

   

 

   APIRequestContext tcExecutionRequest = playwright.request().newContext(new APIRequest.NewContextOptions() 

     .setExtraHTTPHeaders(headers)); 

     

     JSONArray finalArr = new JSONArray(); 

     JSONObject json = new JSONObject(); 

 

     int tcId = Integer.parseInt(tcInfo.getTags().iterator().next()); 

 

 

     json.put("Guid", JSONObject.NULL); 

     json.put("TestCaseId", tcId); 

     json.put("TestCaseName", JSONObject.NULL); 

     json.put("Finalize", false); 

     json.put("ValueSetName", JSONObject.NULL); 

     json.put("TestScenarioInfo", JSONObject.NULL); 

     

     JSONArray stepsArr = new JSONArray(); 

     JSONObject jsonSteps = new JSONObject(); 

     

     jsonSteps.put("Index", 1); 

     jsonSteps.put("Name", "Step 1"); 

     jsonSteps.put("StepType", "Step"); 

     jsonSteps.put("Status", status); 

     jsonSteps.put("Description", JSONObject.NULL); 

     jsonSteps.put("ExpectedResults", JSONObject.NULL); 

     jsonSteps.put("ActualResults", JSONObject.NULL); 

     jsonSteps.put("ActualResultsLastUpdatedBy", JSONObject.NULL); 

     jsonSteps.put("ActualResultsLastUpdated","0001-01-01T00:00:00"); 

 

     stepsArr.put(jsonSteps); 

 

     json.put("Steps", stepsArr); 

     json.put("TestedVersion", JSONObject.NULL); 

     json.put("Status", JSONObject.NULL); 

     

      JSONObject execDuration = new JSONObject(); 

      execDuration.put("FieldValueType","TimeSpan"); 

      execDuration.put("Text","20 second"); 

      execDuration.put("Value",20); 

      execDuration.put("Unit","Second"); 

      

     json.put("ExecutionDuration", execDuration); 

     json.put("AttachedLabels", new JSONArray()); 

     json.put("CustomFields", new JSONArray()); 

     json.put("Attachments", JSONObject.NULL); 

     json.put("TesterId", JSONObject.NULL); 

     json.put("ExecutionDate", JSONObject.NULL); 

     json.put("AttachedFiles", new JSONArray()); 

 

     finalArr.put(json); 

 

     APIResponse tcExcutionResponse = tcExecutionRequest.post(appUrl + "api/TestExecution", RequestOptions.create().setData(finalArr.toString())); 

 

     assertTrue(tcExcutionResponse.ok()); 

 } 

```

In the above request we are passing “logResult” variable. It represents the actual JUnit test result (“Pass”, “Failed”, “NotRun”, etc.) To obtain this status from JUnit run we implement [Test Watcher Interface](https://junit.org/junit5/docs/5.5.1/api/org/junit/jupiter/api/extension/TestWatcher.html) in the TestBase Class: &#x20;

<figure><img src="/files/w8vZznEJs4UEmFsYc9ZK" alt=""><figcaption></figcaption></figure>

In this class we should implement Interface Methods as below:

```
@Override 

 public void testSuccessful(ExtensionContext context) { 

   System.out.println("Call Success extension"); 

   logResult = "Pass"; 

 } 

 

 @Override 

 public void testAborted(ExtensionContext context, Throwable cause) { 

   System.out.println("Call Abort extension"); 

   logResult = "NotRun"; 

 } 

 

 @Override 

 public void testFailed(ExtensionContext context, Throwable cause) { 

   System.out.println("Call Fail extension"); 

   logResult = "Failed"; 

 } 
```

We assume that Test Class name is based on actual aqua Test Case Id: e.g in the case above we updated aqua Test Case with Id=270.

<figure><img src="/files/L6uQruiJyfgCqpvwyuvW" alt="" width="315"><figcaption></figcaption></figure>

This Id variable can be passed from Test Class by using TestInfo from <mark style="color:green;">org.junit.jupiter.api.TestInfo</mark>; &#x20;

After successful JUnit test execution we should see corresponding single Test Case execution in aqua

<figure><img src="/files/vLmfCKMOKZzETWhc4CAl" alt=""><figcaption></figcaption></figure>

To be able to run the above Maven Project using GitLab CI/CD we need to create corresponding workflow file .gitlab-ci.yml. Simple Example of .gitlab-ci.yml script is shown below:

```
image: maven:latest 

 

stages:          

 - test 

 

maven-build-job:    

 stage: test     

 script: 

   - pwd  

   - sleep 10 

   - cd aqua-demo/ 

   - mvn compile 

   - mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" 

   - mvn clean test 
```

Once a GitLab test runner is configured and connected to your GitLab projects we can Run GitLab Pipeline using the above workflow:

<figure><img src="/files/fz5GJgSAjt0IsGg2P6PB" alt=""><figcaption></figcaption></figure>

**Full TestBase class listing:**

```
package com.aqua; 

 

import com.google.gson.Gson; 

import com.google.gson.JsonObject; 

import com.microsoft.playwright.APIRequest; 

import com.microsoft.playwright.APIRequestContext; 

import com.microsoft.playwright.APIResponse; 

import com.microsoft.playwright.Browser; 

import com.microsoft.playwright.BrowserContext; 

import com.microsoft.playwright.BrowserType; 

import com.microsoft.playwright.Page; 

import com.microsoft.playwright.Playwright; 

import com.microsoft.playwright.options.FormData; 

import com.microsoft.playwright.options.RequestOptions; 

 

import org.junit.jupiter.api.AfterAll; 

import org.junit.jupiter.api.AfterEach; 

import org.junit.jupiter.api.BeforeAll; 

import org.junit.jupiter.api.BeforeEach; 

import org.junit.jupiter.api.TestInfo; 

import org.junit.jupiter.api.extension.ExtensionContext; 

import org.junit.jupiter.api.extension.TestWatcher; 

import org.json.*; 

 

import static org.junit.jupiter.api.Assertions.assertTrue; 

 

import java.util.HashMap; 

import java.util.Map; 

 

public class TestBase implements TestWatcher 

{ 

 private static Playwright playwright; 

 private static Browser browser; 

 private static String appUrl = "https://aqua-auto-aqamasterpla.aqua-testing.com/aquawebng/"; 

 private static String bearerToken; 

 private static APIRequestContext request; 

 private static BrowserContext context; 

 private static TestInfo testInfo; 

 private static String logResult; 

 Page page; 

 

 static void getBearerToken() { 

   Map<String, String> headers = new HashMap<>(); 

   headers.put("content-type", "application/x-www-form-urlencoded"); 

 

   request = playwright.request().newContext(new APIRequest.NewContextOptions() 

     .setExtraHTTPHeaders(headers)); 

   APIResponse response = request.post(appUrl + "api/token", RequestOptions.create().setForm( 

     FormData.create() 

       .set("grant_type", "password") 

       .set("username","start") 

       .set("password","default"))); 

 

   assertTrue(response.ok()); 

 

   JsonObject j = new Gson().fromJson(response.text(), JsonObject.class); 

   bearerToken = j.get("access_token").getAsString(); 

 } 

 

 static void createTCExecution(TestInfo tcInfo, String status){ 

   Map<String, String> headers = new HashMap<>(); 

   headers.put("authorization", "Bearer " + bearerToken); 

   headers.put("content-type", "application/json"); 

   headers.put("accept", "application/json"); 

   headers.put("Connection", "keep-alive"); 

   

 

   APIRequestContext tcExecutionRequest = playwright.request().newContext(new APIRequest.NewContextOptions() 

     .setExtraHTTPHeaders(headers)); 

     

     JSONArray finalArr = new JSONArray(); 

     JSONObject json = new JSONObject(); 

 

     int tcId = Integer.parseInt(tcInfo.getTags().iterator().next()); 

 

 

     json.put("Guid", JSONObject.NULL); 

     json.put("TestCaseId", tcId); 

     json.put("TestCaseName", JSONObject.NULL); 

     json.put("Finalize", false); 

     json.put("ValueSetName", JSONObject.NULL); 

     json.put("TestScenarioInfo", JSONObject.NULL); 

     

     JSONArray stepsArr = new JSONArray(); 

     JSONObject jsonSteps = new JSONObject(); 

     

     jsonSteps.put("Index", 1); 

     jsonSteps.put("Name", "Step 1"); 

     jsonSteps.put("StepType", "Step"); 

     jsonSteps.put("Status", status); 

     jsonSteps.put("Description", JSONObject.NULL); 

     jsonSteps.put("ExpectedResults", JSONObject.NULL); 

     jsonSteps.put("ActualResults", JSONObject.NULL); 

     jsonSteps.put("ActualResultsLastUpdatedBy", JSONObject.NULL); 

     jsonSteps.put("ActualResultsLastUpdated","0001-01-01T00:00:00"); 

 

     stepsArr.put(jsonSteps); 

 

     json.put("Steps", stepsArr); 

     json.put("TestedVersion", JSONObject.NULL); 

     json.put("Status", JSONObject.NULL); 

     

      JSONObject execDuration = new JSONObject(); 

      execDuration.put("FieldValueType","TimeSpan"); 

      execDuration.put("Text","20 second"); 

      execDuration.put("Value",20); 

      execDuration.put("Unit","Second"); 

      

     json.put("ExecutionDuration", execDuration); 

     json.put("AttachedLabels", new JSONArray()); 

     json.put("CustomFields", new JSONArray()); 

     json.put("Attachments", JSONObject.NULL); 

     json.put("TesterId", JSONObject.NULL); 

     json.put("ExecutionDate", JSONObject.NULL); 

     json.put("AttachedFiles", new JSONArray()); 

 

     finalArr.put(json); 

 

     APIResponse tcExcutionResponse = tcExecutionRequest.post(appUrl + "api/TestExecution", RequestOptions.create().setData(finalArr.toString())); 

 

     assertTrue(tcExcutionResponse.ok()); 

 } 

 

 @BeforeAll 

 static void berforeAll() { 

   playwright = Playwright.create(); 

   browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setSlowMo(50)); 

 } 

 

 @AfterAll 

 static void closeBrowser() { 

   createTCExecution(testInfo, logResult); 

   context.close(); 

   playwright.close(); 

 } 

 

 @BeforeEach 

 void createContextAndPage(TestInfo testInfo) { 

   TestBase.testInfo = testInfo; 

   context = browser.newContext(); 

   page = context.newPage(); 

 } 

 @Override 

 public void testSuccessful(ExtensionContext context) { 

   System.out.println("Call Success extension"); 

   logResult = "Pass"; 

 } 

 

 @Override 

 public void testAborted(ExtensionContext context, Throwable cause) { 

   System.out.println("Call Abort extension"); 

   logResult = "NotRun"; 

 } 

 

 @Override 

 public void testFailed(ExtensionContext context, Throwable cause) { 

   System.out.println("Call Fail extension"); 

   logResult = "Failed"; 

 } 

 

 @AfterEach 

 void closeContext() { 

   getBearerToken(); 

   System.out.println("Aqua TC Id being executed: " + testInfo.getTags().iterator().next()); 

 } 

 

} 

 
```

**Full Test Class listing:**

```
package com.aqua; 

 

import static org.junit.jupiter.api.Assertions.assertEquals; 

 

import org.junit.jupiter.api.Tag; 

import org.junit.jupiter.api.Test; 

import org.junit.jupiter.api.TestInfo; 

import org.junit.jupiter.api.extension.ExtendWith; 

 

@ExtendWith(TestBase.class) 

public class TestCase270 extends TestBase{ 

 

   @Test 

   @Tag("270") 

   void shouldSearchWiki(TestInfo testInfo) { 

     page.navigate("https://www.wikipedia.org/"); 

     page.locator("input[name=\"search\"]").click(); 

     page.locator("input[name=\"search\"]").fill("playwright"); 

     page.locator("input[name=\"search\"]").press("Enter"); 

     assertEquals("https://en.wikipedia.org/wiki/Playwright", page.url()); 

     System.out.println("Test Case executed"); 

   } 

 

} 
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aqua-cloud.io/documentation/automation/automation-ci-cd-tools-and-aqua-rest-api/gitlab-ci-cd-java-playwright.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
