All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.liferay.jenkins.results.parser.test.clazz.group.PlaywrightBatchTestClassGroup Maven / Gradle / Ivy

There is a newer version: 1.0.1492
Show newest version
/**
 * SPDX-FileCopyrightText: (c) 2023 Liferay, Inc. https://liferay.com
 * SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
 */

package com.liferay.jenkins.results.parser.test.clazz.group;

import com.liferay.jenkins.results.parser.AntUtil;
import com.liferay.jenkins.results.parser.JenkinsResultsParserUtil;
import com.liferay.jenkins.results.parser.NotificationUtil;
import com.liferay.jenkins.results.parser.PortalGitWorkingDirectory;
import com.liferay.jenkins.results.parser.PortalTestClassJob;
import com.liferay.jenkins.results.parser.job.property.JobProperty;
import com.liferay.jenkins.results.parser.test.batch.PlaywrightTestBatch;
import com.liferay.jenkins.results.parser.test.batch.PlaywrightTestSelector;
import com.liferay.jenkins.results.parser.test.clazz.TestClass;
import com.liferay.jenkins.results.parser.test.clazz.TestClassFactory;

import java.io.File;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.lang3.StringEscapeUtils;

import org.json.JSONArray;
import org.json.JSONObject;

/**
 * @author Kenji Heigel
 */
public class PlaywrightBatchTestClassGroup extends BatchTestClassGroup {

	public void addDefaultProjectJobProperty(String batchName) {
		if (isRootCauseAnalysis()) {
			String portalBatchTestSelector = System.getenv(
				"PORTAL_BATCH_TEST_SELECTOR");

			if (JenkinsResultsParserUtil.isNullOrEmpty(
					portalBatchTestSelector)) {

				portalBatchTestSelector = getBuildStartProperty(
					"PORTAL_BATCH_TEST_SELECTOR");
			}

			_addProjectNames(portalBatchTestSelector);

			return;
		}

		JobProperty jobProperty = getJobProperty(
			PLAYWRIGHT_TEST_PROJECT_PROPERTY_NAME, testSuiteName, batchName);

		String jobPropertyValue = jobProperty.getValue();

		if (JenkinsResultsParserUtil.isNullOrEmpty(jobPropertyValue)) {
			jobPropertyValue = System.getenv("PLAYWRIGHT_PROJECT_NAME");
		}

		if (JenkinsResultsParserUtil.isNullOrEmpty(jobPropertyValue)) {
			jobPropertyValue = _getProjectNames();
		}

		_addProjectNames(jobPropertyValue);

		recordJobProperty(jobProperty);
	}

	protected PlaywrightBatchTestClassGroup(
		JSONObject jsonObject, PortalTestClassJob portalTestClassJob) {

		super(jsonObject, portalTestClassJob);
	}

	protected PlaywrightBatchTestClassGroup(
		String batchName, PlaywrightTestBatch playwrightTestBatch,
		PortalTestClassJob portalTestClassJob) {

		super(batchName, portalTestClassJob);

		PlaywrightTestSelector playwrightTestSelector =
			playwrightTestBatch.getTestSelector();

		Set playwrightJobProperties =
			playwrightTestSelector.getPlaywrightJobProperties();

		playwrightJobProperties.removeAll(Collections.singleton(null));

		for (JobProperty jobProperty : playwrightJobProperties) {
			Collections.addAll(
				_projectNames,
				jobProperty.getValue(
				).split(
					","
				));
		}

		recordJobProperties(new ArrayList<>(playwrightJobProperties));

		setTestClasses();
	}

	protected PlaywrightBatchTestClassGroup(
		String batchName, PortalTestClassJob portalTestClassJob) {

		super(batchName, portalTestClassJob);

		if (ignore()) {
			return;
		}

		if (testRelevantChanges) {
			List relevantPlaywrightJobProperties =
				getRelevantPlaywrightJobProperties();

			if (!relevantPlaywrightJobProperties.isEmpty()) {
				recordJobProperties(relevantPlaywrightJobProperties);
			}
		}
		else {
			addDefaultProjectJobProperty(batchName);
		}

		setTestClasses();
	}

	protected File getPlaywrightBaseDir() {
		PortalTestClassJob portalTestClassJob = (PortalTestClassJob)getJob();

		PortalGitWorkingDirectory portalGitWorkingDirectory =
			portalTestClassJob.getPortalGitWorkingDirectory();

		return new File(
			portalGitWorkingDirectory.getWorkingDirectory(),
			"modules/test/playwright");
	}

	protected List getRelevantPlaywrightJobProperties() {
		Set playwrightJobProperties = new HashSet<>();

		for (File modifiedFile :
				portalGitWorkingDirectory.getModifiedFilesList(false)) {

			List playwrightTestProjectJobProperties =
				getJobProperties(
					modifiedFile, PLAYWRIGHT_TEST_PROJECT_PROPERTY_NAME,
					JobProperty.Type.MODULE_TEST_DIR, null);

			for (JobProperty playwrightTestProjectJobProperty :
					playwrightTestProjectJobProperties) {

				if (playwrightTestProjectJobProperty.getValue() != null) {
					String projectNames =
						playwrightTestProjectJobProperty.getValue();

					_addProjectNames(projectNames);

					playwrightJobProperties.add(
						playwrightTestProjectJobProperty);
				}
			}
		}

		playwrightJobProperties.removeAll(Collections.singleton(null));

		return new ArrayList<>(playwrightJobProperties);
	}

	protected List getSpecJSONObjects() {
		return _specJSONObjects;
	}

	protected void setTestClasses() {
		long start = System.currentTimeMillis();

		_loadPlaywrightJSONObjects();

		for (String projectName : _projectNames) {
			List testClasses = _getTestClasses(projectName);

			if (testClasses.isEmpty()) {
				continue;
			}

			SegmentTestClassGroup segmentTestClassGroup =
				TestClassGroupFactory.newSegmentTestClassGroup(this);

			if (segmentTestClassGroup instanceof
					PlaywrightSegmentTestClassGroup) {

				PlaywrightSegmentTestClassGroup
					playwrightSegmentTestClassGroup =
						(PlaywrightSegmentTestClassGroup)segmentTestClassGroup;

				playwrightSegmentTestClassGroup.setProjectName(projectName);

				AxisTestClassGroup axisTestClassGroup =
					TestClassGroupFactory.newAxisTestClassGroup(this);

				playwrightSegmentTestClassGroup.addAxisTestClassGroup(
					axisTestClassGroup);

				for (TestClass testClass : testClasses) {
					axisTestClassGroup.addTestClass(testClass);

					addTestClass(testClass);
				}

				addAxisTestClassGroup(axisTestClassGroup);

				addSegmentTestClassGroup(playwrightSegmentTestClassGroup);
			}
		}

		List testClasses = getTestClasses();

		long duration = System.currentTimeMillis() - start;

		System.out.println(
			JenkinsResultsParserUtil.combine(
				"[", getBatchName(), "] ", "Found ",
				String.valueOf(testClasses.size()),
				" Playwright test classes in ",
				JenkinsResultsParserUtil.toDurationString(duration)));
	}

	protected static final String PLAYWRIGHT_TEST_PROJECT_PROPERTY_NAME =
		"playwright.test.project";

	private void _addProjectNames(String projectNames) {
		projectNames = projectNames.trim();

		Collections.addAll(_projectNames, projectNames.split("\\s*,\\s*"));
	}

	private String _callNPMCommand(File baseDir, String npmCommand) {
		StringBuilder sb = new StringBuilder();

		if (JenkinsResultsParserUtil.isCINode()) {
			sb.append("export CI=true\n");
		}

		sb.append("export PATH=");

		String npmHome = _getPortalProperty("npm.home");

		if (!JenkinsResultsParserUtil.isNullOrEmpty(npmHome)) {
			sb.append(npmHome);
			sb.append(":");
		}

		String nodeHome = _getPortalProperty("node.home");

		if (!JenkinsResultsParserUtil.isNullOrEmpty(nodeHome)) {
			sb.append(nodeHome);
			sb.append("/bin:");
		}

		sb.append("${PATH}\n");

		sb.append(npmCommand);

		File npmScriptFile = new File(baseDir, "npm_script.sh");

		try {
			JenkinsResultsParserUtil.write(npmScriptFile, sb.toString());

			Process process = JenkinsResultsParserUtil.executeBashCommands(
				true, baseDir, 1000 * 60 * 10, sb.toString());

			return JenkinsResultsParserUtil.readInputStream(
				process.getInputStream());
		}
		catch (IOException | TimeoutException exception) {
			throw new RuntimeException(exception);
		}
		finally {
			JenkinsResultsParserUtil.delete(npmScriptFile);
		}
	}

	private String _getPortalProperty(String propertyName) {
		File workingDirectory = JenkinsResultsParserUtil.getCanonicalFile(
			portalGitWorkingDirectory.getWorkingDirectory());

		Properties portalProperties = JenkinsResultsParserUtil.getProperties(
			new File(workingDirectory, "build.properties"),
			new File(workingDirectory, "app.server.properties"),
			new File(workingDirectory, "release.properties"),
			new File(workingDirectory, "test.properties"));

		portalProperties.setProperty(
			"project.dir", workingDirectory.toString());

		return JenkinsResultsParserUtil.getProperty(
			portalProperties, propertyName);
	}

	private String _getProjectNames() {
		_loadPlaywrightJSONObjects();

		JSONObject configJSONObject = _playwrightJSONObject.getJSONObject(
			"config");

		StringBuilder sb = new StringBuilder();

		JSONArray projectsJSONArray = configJSONObject.optJSONArray("projects");

		for (int i = 0; i < projectsJSONArray.length(); i++) {
			JSONObject projectJSONObject = projectsJSONArray.getJSONObject(i);

			sb.append(projectJSONObject.optString("name"));

			sb.append(",");
		}

		sb.setLength(sb.length() - 1);

		return sb.toString();
	}

	private List _getSpecJSONObjects(JSONObject jsonObject) {
		List specJSONObjects = new ArrayList<>();

		JSONArray suitesJSONArray = jsonObject.getJSONArray("suites");

		for (int i = 0; i < suitesJSONArray.length(); i++) {
			JSONObject suiteJSONObject = suitesJSONArray.getJSONObject(i);

			if (suiteJSONObject.has("suites")) {
				specJSONObjects.addAll(_getSpecJSONObjects(suiteJSONObject));
			}

			JSONArray specsJSONArray = suiteJSONObject.optJSONArray("specs");

			if (specsJSONArray == null) {
				continue;
			}

			for (int j = 0; j < specsJSONArray.length(); j++) {
				specJSONObjects.add(specsJSONArray.getJSONObject(j));
			}
		}

		return specJSONObjects;
	}

	private List _getTestClasses(String projectName) {
		List testClasses = new ArrayList<>();

		JSONObject configJSONObject = _playwrightJSONObject.getJSONObject(
			"config");

		File rootDir = new File(configJSONObject.getString("rootDir"));

		Map> specFileTitlesMap = new HashMap<>();

		for (JSONObject specJSONObject : getSpecJSONObjects()) {
			JSONArray testsJSONArray = specJSONObject.optJSONArray("tests");

			if ((testsJSONArray == null) || testsJSONArray.isEmpty()) {
				continue;
			}

			JSONObject testJSONObject = testsJSONArray.getJSONObject(0);

			if (!Objects.equals(
					projectName, testJSONObject.optString("projectName"))) {

				continue;
			}

			File specFile = new File(rootDir, specJSONObject.getString("file"));

			List specTitles = specFileTitlesMap.get(specFile);

			if (specTitles == null) {
				specTitles = new ArrayList<>();
			}

			specTitles.add(specJSONObject.getString("title"));

			specFileTitlesMap.put(specFile, specTitles);
		}

		for (Map.Entry> entry :
				specFileTitlesMap.entrySet()) {

			TestClass testClass = TestClassFactory.newTestClass(
				this, entry.getKey());

			for (String specTitle : entry.getValue()) {
				testClass.addTestClassMethod(
					TestClassFactory.newTestClassMethod(
						false, specTitle, testClass));
			}

			testClasses.add(testClass);
		}

		return testClasses;
	}

	private void _loadPlaywrightJSONObjects() {
		synchronized (_playwrightJSONObjectsLoaded) {
			if (_playwrightJSONObjectsLoaded.get()) {
				return;
			}

			File playwrightBaseDir = getPlaywrightBaseDir();

			try {
				AntUtil.callTarget(
					portalGitWorkingDirectory.getWorkingDirectory(),
					"build.xml", "setup-yarn");
			}
			catch (Exception exception) {
				exception.printStackTrace();
			}

			try {
				_callNPMCommand(playwrightBaseDir, "npm install");

				String result = _callNPMCommand(
					playwrightBaseDir,
					"npm run playwright test -- --list --reporter=json");

				int index = result.indexOf("\n{");

				result = result.substring(index);

				result = result.replace(
					"Finished executing Bash commands.", "");

				_playwrightJSONObject = new JSONObject(result.trim());
			}
			catch (Exception exception) {
				StringBuilder sb = new StringBuilder();

				sb.append("Unable to parse Playwright JSON object ");
				sb.append("<@U04GTH03Q>, <@U01EV0V1Y6N>\n");

				sb.append(System.getenv("TOP_LEVEL_BUILD_URL"));

				NotificationUtil.sendSlackNotification(
					sb.toString(), "#ci-notifications", ":playwright:",
					"Playwright Batch Creation Failure", "Liferay Playwright");

				exception.printStackTrace();
			}

			JSONArray errorsJSONArray = _playwrightJSONObject.optJSONArray(
				"errors");

			if ((errorsJSONArray != null) && (errorsJSONArray.length() > 0)) {
				StringBuilder sb = new StringBuilder();

				sb.append("Errors found in Playwright tests.\n");

				for (int i = 0; i < errorsJSONArray.length(); i++) {
					JSONObject errorJSONObject = errorsJSONArray.getJSONObject(
						i);

					System.out.println(errorJSONObject);

					String errorMessage = errorJSONObject.optString("message");

					if ((errorMessage != null) && !errorMessage.isEmpty()) {
						sb.append(errorMessage);
					}

					JSONObject errorLocationJSONObject =
						errorJSONObject.optJSONObject("location");

					if (errorLocationJSONObject != null) {
						sb.append(" [file://");
						sb.append(errorLocationJSONObject.opt("file"));
						sb.append(":");
						sb.append(errorLocationJSONObject.opt("line"));
						sb.append(":");
						sb.append(errorLocationJSONObject.opt("column"));
						sb.append("]");
					}

					String errorStack = errorJSONObject.optString("stack");

					if ((errorStack != null) && !errorStack.isEmpty()) {
						sb.append("\n");

						sb.append(errorStack);
					}

					String errorSnippet = errorJSONObject.optString("snippet");

					if ((errorSnippet != null) && !errorSnippet.isEmpty()) {
						sb.append("\n");

						sb.append(StringEscapeUtils.unescapeJava(errorSnippet));
					}

					sb.append("\n");
				}

				System.out.println(sb.toString());

				throw new RuntimeException(sb.toString());
			}

			_specJSONObjects.addAll(_getSpecJSONObjects(_playwrightJSONObject));

			_playwrightJSONObjectsLoaded.set(true);
		}
	}

	private static JSONObject _playwrightJSONObject;
	private static final AtomicBoolean _playwrightJSONObjectsLoaded =
		new AtomicBoolean();
	private static final List _specJSONObjects =
		Collections.synchronizedList(new ArrayList());

	private final Set _projectNames = new HashSet<>();

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy