From 815170d5ad0acde3c2988f192f784c19441dda24 Mon Sep 17 00:00:00 2001 From: Andrei Kamarouski Date: Tue, 20 Jun 2023 11:53:23 +0300 Subject: [PATCH 1/7] change version of carina-utils to 1.0.5.REFACTORING-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 42e0309..7428efc 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ UTF-8 11 - 1.0.4 + 1.0.5.REFACTORING-SNAPSHOT 1.7 1.7.30 3.5 From 10f0046621751678f9aec273e2cf4b103309650e Mon Sep 17 00:00:00 2001 From: Andrei Kamarouski Date: Wed, 21 Jun 2023 13:56:33 +0300 Subject: [PATCH 2/7] moved classes from carina-utils --- .../DataProviderParameterGenerator.java | 183 ++++++++++++ .../core/impl/BaseDataProvider.java | 19 +- .../core/impl/XlsDataProvider.java | 3 +- .../utils/parser/xls/AbstractTable.java | 92 ++++++ .../carina/utils/parser/xls/XLSCache.java | 57 ++++ .../utils/parser/xls/XLSChildTable.java | 34 +++ .../carina/utils/parser/xls/XLSParser.java | 273 ++++++++++++++++++ .../carina/utils/parser/xls/XLSTable.java | 119 ++++++++ 8 files changed, 769 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/zebrunner/carina/dataprovider/DataProviderParameterGenerator.java create mode 100644 src/main/java/com/zebrunner/carina/utils/parser/xls/AbstractTable.java create mode 100644 src/main/java/com/zebrunner/carina/utils/parser/xls/XLSCache.java create mode 100644 src/main/java/com/zebrunner/carina/utils/parser/xls/XLSChildTable.java create mode 100644 src/main/java/com/zebrunner/carina/utils/parser/xls/XLSParser.java create mode 100644 src/main/java/com/zebrunner/carina/utils/parser/xls/XLSTable.java diff --git a/src/main/java/com/zebrunner/carina/dataprovider/DataProviderParameterGenerator.java b/src/main/java/com/zebrunner/carina/dataprovider/DataProviderParameterGenerator.java new file mode 100644 index 0000000..d113339 --- /dev/null +++ b/src/main/java/com/zebrunner/carina/dataprovider/DataProviderParameterGenerator.java @@ -0,0 +1,183 @@ +package com.zebrunner.carina.dataprovider; + +/******************************************************************************* + * Copyright 2020-2022 Zebrunner Inc (https://www.zebrunner.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ + +import com.zebrunner.carina.utils.R; +import com.zebrunner.carina.utils.StringGenerator; +import com.zebrunner.carina.utils.commons.SpecialKeywords; +import com.zebrunner.carina.utils.config.Configuration; +import com.zebrunner.carina.utils.config.StandardConfigurationOption; +import com.zebrunner.carina.utils.exception.InvalidArgsException; +import com.zebrunner.carina.utils.parser.xls.XLSParser; +import com.zebrunner.carina.utils.resources.L10N; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.ITestNGMethod; + +import java.lang.invoke.MethodHandles; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class DataProviderParameterGenerator { + + private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final Pattern GENERATE_UUID_PATTERN = Pattern.compile(SpecialKeywords.GENERATE_UUID); + private static final Pattern GENERATE_PATTERN = Pattern.compile(SpecialKeywords.GENERATE); + private static final Pattern GENERATEAN_PATTERN = Pattern.compile(SpecialKeywords.GENERATEAN); + private static final Pattern GENERATEN_PATTERN = Pattern.compile(SpecialKeywords.GENERATEN); + private static final Pattern TESTDATA_PATTERN = Pattern.compile(SpecialKeywords.TESTDATA); + private static final Pattern ENV_PATTERN = Pattern.compile(SpecialKeywords.ENV); + private static final Pattern L10N_PATTERN = Pattern.compile(SpecialKeywords.L10N_PATTERN); + private static final Pattern EXCEL_PATTERN = Pattern.compile(SpecialKeywords.EXCEL); + private static String uuid; + + private DataProviderParameterGenerator() { + // do nothing + } + + public static Object process(String param) { + try { + if (param == null || param.equalsIgnoreCase("nil")) { + return null; + } + + Matcher matcher = GENERATE_UUID_PATTERN.matcher(param); + if (matcher.find()) { + return StringUtils.replace(param, matcher.group(), uuid); + } + matcher = GENERATE_PATTERN.matcher(param); + if (matcher.find()) { + int start = param.indexOf(':') + 1; + int end = param.indexOf('}'); + int size = Integer.parseInt(param.substring(start, end)); + return StringUtils.replace(param, matcher.group(), StringGenerator.generateWord(size)); + } + + matcher = GENERATEAN_PATTERN.matcher(param); + if (matcher.find()) { + int start = param.indexOf(':') + 1; + int end = param.indexOf('}'); + int size = Integer.parseInt(param.substring(start, end)); + return StringUtils.replace(param, matcher.group(), StringGenerator.generateWordAN(size)); + } + + matcher = GENERATEN_PATTERN.matcher(param); + if (matcher.find()) { + int start = param.indexOf(':') + 1; + int end = param.indexOf('}'); + int size = Integer.parseInt(param.substring(start, end)); + return StringUtils.replace(param, matcher.group(), StringGenerator.generateNumeric(size)); + } + + matcher = ENV_PATTERN.matcher(param); + if (matcher.find()) { + int start = param.indexOf(':') + 1; + int end = param.indexOf('}'); + String key = param.substring(start, end); + return StringUtils.replace(param, matcher.group(), Configuration.get(key, StandardConfigurationOption.ENVIRONMENT).orElse("")); + } + + matcher = TESTDATA_PATTERN.matcher(param); + if (matcher.find()) { + int start = param.indexOf(':') + 1; + int end = param.indexOf('}'); + String key = param.substring(start, end); + return StringUtils.replace(param, matcher.group(), R.TESTDATA.get(key)); + } + + matcher = EXCEL_PATTERN.matcher(param); + if (matcher.find()) { + int start = param.indexOf(':') + 1; + int end = param.indexOf('}'); + String key = param.substring(start, end); + return StringUtils.replace(param, matcher.group(), getValueFromXLS(key)); + } + + matcher = L10N_PATTERN.matcher(param); + String initStrL10N = param; + while (matcher.find()) { + int start = param.indexOf(SpecialKeywords.L10N + ":") + 5; + int end = param.indexOf('}'); + String key = param.substring(start, end); + param = StringUtils.replace(param, matcher.group(), L10N.getText(key)); + } + // in case if L10N pattern was applied + if (!initStrL10N.equalsIgnoreCase(param)) { + return param; + } + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } + return param; + } + + public static void processMap(Map paramsMap) { + paramsMap.entrySet() + .stream() + .filter(Objects::nonNull).forEach(entry -> { + String value = entry.getValue(); + if (value == null) + return; + Object param = process(value); + if (param == null) + return; + String newValue = param.toString(); + if (!value.equals(newValue)) { + entry.setValue(newValue); + } + }); + } + + private static String getValueFromXLS(String xlsSheetKey) { + if (StringUtils.isEmpty(xlsSheetKey)) { + throw new InvalidArgsException("Invalid excel key, should be 'xls_file#sheet#key'."); + } + + String xls = xlsSheetKey.split("#")[0]; + String sheet = xlsSheetKey.split("#")[1]; + String key = xlsSheetKey.split("#")[2]; + + return XLSParser.parseValue(xls, sheet, key); + } + + public static String getUUID() { + return uuid; + } + + public static void setUUID(String uUID) { + uuid = uUID; + } + + /** + * Generate hash by class name, method name and arg values. + * + * @param args Object[] test method arguments + * @param method ITestNGMethod + * @return String hash + */ + public static String hash(Object[] args, ITestNGMethod method) { + String toHash = ""; + toHash += Arrays.hashCode(args); + toHash += method.getMethodName(); + toHash += (method.getRealClass()); + return String.valueOf(toHash.hashCode()); + } +} diff --git a/src/main/java/com/zebrunner/carina/dataprovider/core/impl/BaseDataProvider.java b/src/main/java/com/zebrunner/carina/dataprovider/core/impl/BaseDataProvider.java index f411300..d378fbe 100644 --- a/src/main/java/com/zebrunner/carina/dataprovider/core/impl/BaseDataProvider.java +++ b/src/main/java/com/zebrunner/carina/dataprovider/core/impl/BaseDataProvider.java @@ -15,6 +15,12 @@ *******************************************************************************/ package com.zebrunner.carina.dataprovider.core.impl; +import com.zebrunner.carina.dataprovider.DataProviderParameterGenerator; +import com.zebrunner.carina.dataprovider.parser.DSBean; +import com.zebrunner.carina.utils.parser.xls.AbstractTable; +import org.testng.ITestContext; +import org.testng.ITestNGMethod; + import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collections; @@ -24,13 +30,6 @@ import java.util.Map; import java.util.Set; -import org.testng.ITestContext; -import org.testng.ITestNGMethod; - -import com.zebrunner.carina.dataprovider.parser.DSBean; -import com.zebrunner.carina.utils.ParameterGenerator; -import com.zebrunner.carina.utils.parser.xls.AbstractTable; - /** * Created by Patotsky on 19.12.2014. */ @@ -162,7 +161,7 @@ private void configureTestNamingVars(Object[][] dataProvider, DSBean dsBean, Abs for (int rowIndex = 0; rowIndex < dataProvider.length; rowIndex++) { Map row = table.getDataRows().get(rowIndex); - String rowHash = ParameterGenerator.hash(dataProvider[rowIndex], testNGMethod); + String rowHash = DataProviderParameterGenerator.hash(dataProvider[rowIndex], testNGMethod); addValueToMap(tuidMap, rowHash, getValueFromRow(row, dsBean.getUidArgs())); addValueToMap(testColumnNamesMap, rowHash, getValueFromRow(row, List.of(dsBean.getTestMethodColumn()))); } @@ -174,7 +173,7 @@ private void configureTestNamingVarsForGroupedProvider(Object[][] dataProvider, ITestNGMethod testNGMethod) { for (int rowIndex = 0; rowIndex < dataProvider.length; rowIndex++) { - String rowHash = ParameterGenerator.hash(dataProvider[rowIndex], testNGMethod); + String rowHash = DataProviderParameterGenerator.hash(dataProvider[rowIndex], testNGMethod); //get all unique tuid values from certain group String testUid = getValueFromGroupList(rowIndex, groupedList, dsBean.getUidArgs()); @@ -222,7 +221,7 @@ private String getValueFromRow(Map row, List columnNames } protected static Object getStaticParam(String name, DSBean dsBean) { - Object param = ParameterGenerator.process(dsBean.getTestParams().get(name)); + Object param = DataProviderParameterGenerator.process(dsBean.getTestParams().get(name)); if (param == null) { throw new RuntimeException("Cant find parameter " + name + " in suite"); } diff --git a/src/main/java/com/zebrunner/carina/dataprovider/core/impl/XlsDataProvider.java b/src/main/java/com/zebrunner/carina/dataprovider/core/impl/XlsDataProvider.java index 3f6b3ff..e827fa9 100644 --- a/src/main/java/com/zebrunner/carina/dataprovider/core/impl/XlsDataProvider.java +++ b/src/main/java/com/zebrunner/carina/dataprovider/core/impl/XlsDataProvider.java @@ -46,7 +46,8 @@ public Object[][] getDataProvider(Annotation annotation, ITestContext context, I DSBean dsBean = new DSBean(parameters, context.getCurrentXmlTest().getAllParameters()); XLSTable xlsTable = XLSParser.parseSpreadSheet(dsBean.getDsFile(), dsBean.getXlsSheet(), dsBean.getExecuteColumn(), dsBean.getExecuteValue()); - xlsTable.processTable(); + //todo investigate how it will work without this method + // xlsTable.processTable(); String groupColumn = dsBean.getGroupColumn(); if (groupColumn.isEmpty()) { diff --git a/src/main/java/com/zebrunner/carina/utils/parser/xls/AbstractTable.java b/src/main/java/com/zebrunner/carina/utils/parser/xls/AbstractTable.java new file mode 100644 index 0000000..bb31ef1 --- /dev/null +++ b/src/main/java/com/zebrunner/carina/utils/parser/xls/AbstractTable.java @@ -0,0 +1,92 @@ +package com.zebrunner.carina.utils.parser.xls; + +import com.zebrunner.carina.dataprovider.DataProviderParameterGenerator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public abstract class AbstractTable { + + protected List headers; + protected List> dataRows; + protected String executeColumn; + protected String executeValue; + + protected AbstractTable() { + headers = Collections.synchronizedList(new LinkedList()); + dataRows = Collections.synchronizedList(new LinkedList>()); + } + + protected AbstractTable(String executeColumn, String executeValue) { + this(); + this.executeColumn = executeColumn; + this.executeValue = executeValue; + } + + public List getHeaders() { + return headers; + } + + public List> getDataRows() { + return dataRows; + } + + public String getExecuteColumn() { + return executeColumn; + } + + public void setExecuteColumn(String executeColumn) { + this.executeColumn = executeColumn; + } + + public String getExecuteValue() { + return executeValue; + } + + public void setExecuteValue(String executeValue) { + this.executeValue = executeValue; + } + + public void setHeaders(Collection row) { + headers.clear(); + headers.addAll(row); + } + + public abstract void addDataRow(List row); + + public void processTable() { + for (Map row : dataRows) { + DataProviderParameterGenerator.processMap(row); + } + } + + public List>> getGroupedDataProviderMap(String fieldName) { + // add unique group values + Set groupValues = new LinkedHashSet<>(); + for (Map item : dataRows) { + String value = item.get(fieldName); + groupValues.add(value); + } + + // group maps into lists, that has the same unique group value + List>> groupedList = new ArrayList<>(); + for (String groupBy : groupValues) { + List> groupOfRows = new ArrayList<>(); + for (Map item : dataRows) { + String value = item.get(fieldName); + if (value.equals(groupBy)) { + groupOfRows.add(item); + } + } + groupedList.add(groupOfRows); + } + + return groupedList; + } +} diff --git a/src/main/java/com/zebrunner/carina/utils/parser/xls/XLSCache.java b/src/main/java/com/zebrunner/carina/utils/parser/xls/XLSCache.java new file mode 100644 index 0000000..c82e87a --- /dev/null +++ b/src/main/java/com/zebrunner/carina/utils/parser/xls/XLSCache.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright 2020-2022 Zebrunner Inc (https://www.zebrunner.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package com.zebrunner.carina.utils.parser.xls; + +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +public class XLSCache { + private static final Map xlsCache = new HashMap<>(); + + private XLSCache() { + // do nothing + } + + public static synchronized Workbook getWorkbook(String xlsPath) { + if (!xlsCache.containsKey(xlsPath)) { + Workbook wb; + try { + try (InputStream is = ClassLoader.getSystemResourceAsStream(xlsPath)) { + wb = WorkbookFactory.create(is); + } + } catch (IOException e) { + throw new UncheckedIOException("Can't read xls: " + xlsPath, e); + } + xlsCache.put(xlsPath, wb); + } + return xlsCache.get(xlsPath); + } + + public static synchronized String getWorkbookPath(Workbook book) { + for (Entry entry : xlsCache.entrySet()) { + if (entry.getValue() == book) + return entry.getKey(); + } + return null; + } +} diff --git a/src/main/java/com/zebrunner/carina/utils/parser/xls/XLSChildTable.java b/src/main/java/com/zebrunner/carina/utils/parser/xls/XLSChildTable.java new file mode 100644 index 0000000..cabf11a --- /dev/null +++ b/src/main/java/com/zebrunner/carina/utils/parser/xls/XLSChildTable.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright 2020-2022 Zebrunner Inc (https://www.zebrunner.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package com.zebrunner.carina.utils.parser.xls; + +import org.apache.poi.ss.usermodel.Row; + +import java.util.HashMap; +import java.util.Map; + +public class XLSChildTable extends XLSTable { + + public void addDataRow(Row row) { + Map dataMap = new HashMap<>(); + for (int i = 0; i < super.getHeaders().size(); i++) { + synchronized (dataMap) { + dataMap.put(super.getHeaders().get(i), XLSParser.getCellValue(row.getCell(i))); + } + } + super.getDataRows().add(dataMap); + } +} diff --git a/src/main/java/com/zebrunner/carina/utils/parser/xls/XLSParser.java b/src/main/java/com/zebrunner/carina/utils/parser/xls/XLSParser.java new file mode 100644 index 0000000..c5c8cfc --- /dev/null +++ b/src/main/java/com/zebrunner/carina/utils/parser/xls/XLSParser.java @@ -0,0 +1,273 @@ +/******************************************************************************* + * Copyright 2020-2022 Zebrunner Inc (https://www.zebrunner.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package com.zebrunner.carina.utils.parser.xls; + +import com.zebrunner.carina.utils.exception.DataLoadingException; +import com.zebrunner.carina.utils.exception.InvalidArgsException; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.FormulaEvaluator; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.model.ExternalLinksTable; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFTable; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class XLSParser { + + private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final DataFormatter df; + private static FormulaEvaluator evaluator; + + static { + df = new DataFormatter(); + } + + private XLSParser() { + // do nothing + } + + public static String parseValue(String locatorKey, String xlsPath, Locale locale) { + String value; + + Workbook wb = XLSCache.getWorkbook(xlsPath); + Sheet sheet = wb.getSheetAt(0); + + List locales = getLocales(sheet); + if (!locales.contains(locale.getCountry())) { + throw new RuntimeException(String.format("Can't find locale '%s' in xls '%s'!", locale.getCountry(), xlsPath)); + } + int cellN = locales.indexOf(locale.getCountry()) + 1; + + List locatorKeys = getLocatorKeys(sheet); + if (!locatorKeys.contains(locatorKey)) { + throw new RuntimeException(String.format("Can't find locatorKey '%s' in xls '%s'!", locatorKey, xlsPath)); + } + int rowN = locatorKeys.indexOf(locatorKey) + 1; + + try { + value = getCellValue(sheet.getRow(rowN).getCell(cellN)); + } catch (Exception e) { + throw new RuntimeException( + String.format("Can't find value for locatorKey '%s' with locale '%s' in xls '%s'!", locatorKey, locale.getCountry(), xlsPath)); + } + + return value; + } + + private static List getLocales(Sheet sheet) { + List locales = new ArrayList<>(); + int lastCell = sheet.getRow(0).getLastCellNum(); + for (int i = 1; i < lastCell; i++) { + locales.add(getCellValue(sheet.getRow(0).getCell(i))); + } + return locales; + } + + private static List getLocatorKeys(Sheet sheet) { + List locatorKeys = new ArrayList<>(); + int lastRow = sheet.getLastRowNum(); + for (int i = 1; i <= lastRow; i++) { + locatorKeys.add(getCellValue(sheet.getRow(i).getCell(0))); + } + return locatorKeys; + } + + public static String parseValue(String xls, String sheetName, String key) { + String value = null; + + Workbook wb = XLSCache.getWorkbook(xls); + + Sheet sheet = wb.getSheet(sheetName); + if (sheet == null) { + throw new InvalidArgsException(String.format("No sheet: '%s' in excel file: '%s'!", sheetName, xls)); + } + + boolean isKeyFound = false; + for (int i = 1; i <= sheet.getLastRowNum(); i++) { + if (key.equals(getCellValue(sheet.getRow(i).getCell(0)))) { + value = getCellValue(sheet.getRow(i).getCell(1)); + isKeyFound = true; + break; + } + } + + if (!isKeyFound) { + throw new InvalidArgsException(String.format("No key: '%s' on sheet '%s' in excel file: '%s'!", key, sheetName, xls)); + } + + return value; + } + + public static XLSTable parseSpreadSheet(String xls, String sheetName) { + return parseSpreadSheet(xls, sheetName, null, null); + } + + public static XLSTable parseSpreadSheet(String xls, String sheetName, String executeColumn, String executeValue) { + XLSTable dataTable = prepareDataTable(executeColumn, executeValue); + + Workbook wb = XLSCache.getWorkbook(xls); + evaluator = wb.getCreationHelper().createFormulaEvaluator(); + + Sheet sheet = wb.getSheet(sheetName); + if (sheet == null) { + throw new InvalidArgsException(String.format("No sheet: '%s' in excel file: '%s'!", sheetName, xls)); + } + + try { + for (int i = 0; i <= sheet.getLastRowNum(); i++) { + if (i == 0) { + dataTable.setHeaders(sheet.getRow(i)); + } else { + dataTable.addDataRow(sheet.getRow(i), wb, sheet); + } + } + } catch (Exception e) { + LOGGER.error("Error while parsing spreadsheet!", e); + } + return dataTable; + } + + public static String getCellValue(Cell cell) { + if (cell == null) + return ""; + + switch (cell.getCellType()) { + case STRING: + return df.formatCellValue(cell).trim(); + case NUMERIC: + return df.formatCellValue(cell).trim(); + case BOOLEAN: + return df.formatCellValue(cell).trim(); + case FORMULA: + return (cell.getCellFormula().contains("[") && cell.getCellFormula().contains("]")) ? null : df.formatCellValue(cell, evaluator).trim(); + case BLANK: + return ""; + default: + return null; + } + } + + public static XLSChildTable parseCellLinks(Cell cell, Workbook wb, Sheet sheet) { + if (cell == null) + return null; + + if (cell.getCellType() == CellType.FORMULA) { + if (cell.getCellFormula().contains("#This Row")) { + if (cell.getCellFormula().contains("!")) { + // Parse link to the cell with table name in the external doc([2]!Table1[[#This Row],[Header6]]) + List paths = Arrays.asList(cell.getCellFormula().split("!")); + int externalLinkNumber = Integer.parseInt(paths.get(0).replaceAll("\\D+", "")) - 1; + String tableName = paths.get(1).split("\\[")[0]; + if (wb instanceof XSSFWorkbook) { + ExternalLinksTable link = ((XSSFWorkbook) wb).getExternalLinksTable().get(externalLinkNumber); + File file = new File(Objects.requireNonNull(XLSCache.getWorkbookPath(wb))); + XSSFWorkbook childWb = (XSSFWorkbook) XLSCache.getWorkbook(file.getParent() + "/" + link.getLinkedFileName()); + if (childWb == null) + throw new DataLoadingException(String.format("WorkBook '%s' doesn't exist!", link.getLinkedFileName())); + for (int i = 0; i < childWb.getNumberOfSheets(); i++) { + XSSFSheet childSheet = childWb.getSheetAt(i); + for (XSSFTable table : childSheet.getTables()) { + if (table.getName().equals(tableName)) { + return createChildTable(childSheet, cell.getRowIndex()); + } + } + } + } else { + throw new DataLoadingException("Unsupported format. External links supports only for .xlsx documents."); + } + } else { + // Parse link to the cell with table name in the same doc(=Table1[[#This Row],[Header6]]) + List paths = Arrays.asList(cell.getCellFormula().replace("=", "").split("\\[")); + if (wb instanceof XSSFWorkbook) { + for (int i = 0; i < wb.getNumberOfSheets(); i++) { + XSSFSheet childSheet = (XSSFSheet) wb.getSheetAt(i); + for (XSSFTable table : childSheet.getTables()) { + if (table.getName().equals(paths.get(0))) { + return createChildTable(childSheet, cell.getRowIndex()); + } + } + } + } else { + throw new DataLoadingException("Unsupported format. Links with table name supports only for .xlsx documents."); + } + } + } else { + String cellValue = cell.getCellFormula().replace("=", "").replace("[", "").replace("]", "!").replace("'", ""); + List paths = Arrays.asList(cellValue.split("!")); + int rowNumber = 0; + Sheet childSheet = null; + + switch (paths.size()) { + // Parse link to the cell in the same sheet(=A4) + case 1: + rowNumber = Integer.parseInt(paths.get(0).replaceAll("\\D+", "")) - 1; + return createChildTable(sheet, rowNumber); + // Parse link to the cell in another sheet in the same doc(=SheetName!A4) + case 2: + childSheet = wb.getSheet(paths.get(0)); + if (childSheet == null) + throw new DataLoadingException(String.format("Sheet '%s' doesn't exist!", paths.get(0))); + rowNumber = Integer.parseInt(paths.get(1).replaceAll("\\D+", "")) - 1; + return createChildTable(childSheet, rowNumber); + // Parse link to the cell in another doc(=[2]SheetName!A4) + case 3: + if (wb instanceof XSSFWorkbook) { + ExternalLinksTable link = ((XSSFWorkbook) wb).getExternalLinksTable().get(Integer.parseInt(paths.get(0)) - 1); + File file = new File(Objects.requireNonNull(XLSCache.getWorkbookPath(wb))); + XSSFWorkbook childWb = (XSSFWorkbook) XLSCache.getWorkbook(file.getParent() + "/" + link.getLinkedFileName()); + + if (childWb == null) + throw new DataLoadingException(String.format("WorkBook '%s' doesn't exist!", paths.get(0))); + childSheet = childWb.getSheet(paths.get(1)); + if (childSheet == null) + throw new DataLoadingException(String.format("Sheet '%s' doesn't exist!", paths.get(0))); + rowNumber = Integer.parseInt(paths.get(2).replaceAll("\\D+", "")) - 1; + return createChildTable(childSheet, rowNumber); + } else { + throw new DataLoadingException("Unsupported format. External links supports only for .xlsx documents."); + } + default: + return null; + } + } + } + return null; + } + + private static XLSChildTable createChildTable(Sheet sheet, int rowNumber) { + XLSChildTable childTable = new XLSChildTable(); + childTable.setHeaders(sheet.getRow(0)); + childTable.addDataRow(sheet.getRow(rowNumber)); + return childTable; + } + + private static XLSTable prepareDataTable(String executeColumn, String executeValue) { + return executeColumn != null && executeValue != null ? new XLSTable(executeColumn, executeValue) : new XLSTable(); + } +} diff --git a/src/main/java/com/zebrunner/carina/utils/parser/xls/XLSTable.java b/src/main/java/com/zebrunner/carina/utils/parser/xls/XLSTable.java new file mode 100644 index 0000000..b3874e3 --- /dev/null +++ b/src/main/java/com/zebrunner/carina/utils/parser/xls/XLSTable.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright 2020-2022 Zebrunner Inc (https://www.zebrunner.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package com.zebrunner.carina.utils.parser.xls; + +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.invoke.MethodHandles; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +public class XLSTable extends AbstractTable { + private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static final String FK_PREFIX = "FK_LINK_"; + + public XLSTable() { + super(); + } + + public XLSTable(String executeColumn, String executeValue) { + super(executeColumn, executeValue); + } + + public void setHeaders(Row row) { + headers.clear(); + for (int i = 0; i < row.getLastCellNum(); i++) { + headers.add(XLSParser.getCellValue(row.getCell(i))); + } + } + + public void addDataRow(Row row, Workbook wb, Sheet sheet) { + if (row == null) { + // don't add any data row if it is null. It seems like there is empty row in xls file + return; + } + addDataRow(rowIndex -> XLSParser.getCellValue(row.getCell(rowIndex)), row, wb, sheet); + } + + @Override + public void addDataRow(List row) { + if (row == null) { + return; + } + addDataRow(index -> row.size() > index ? row.get(index) : null, null, null, null); + } + + private void addDataRow(Function cellValueGetter, Row row, Workbook wb, Sheet sheet) { + if ((executeColumn != null && executeValue != null && headers.contains(executeColumn)) + && (!executeValue.equalsIgnoreCase(cellValueGetter.apply(headers.indexOf(executeColumn))))) { + return; + } + + XLSChildTable childRow = null; + + Map dataMap = new HashMap<>(); + LOGGER.debug("Loading data from row: "); + for (int i = 0; i < headers.size(); i++) { + String header = headers.get(i); + if (header.startsWith(FK_PREFIX)) { + if (row != null && wb != null && sheet != null) { + childRow = XLSParser.parseCellLinks(row.getCell(i), wb, sheet); + } else { + LOGGER.warn("FK_LINK_ prefix is not currently supported for spreadsheets"); + } + } + + dataMap.put(header, cellValueGetter.apply(i)); + LOGGER.debug("{}: {}", header, dataMap.get(header)); + } + + // If row has foreign key than merge headers and data + merge(childRow, dataMap); + + LOGGER.debug("Merged row: "); + for (String header : headers) { + LOGGER.debug("{}: {}", header, dataMap.get(header)); + } + + dataRows.add(dataMap); + } + + private void merge(XLSChildTable childRow, Map dataMap) { + if (childRow != null) { + LOGGER.debug("Loading data from child row: "); + for (int i = 0; i < childRow.getHeaders().size(); i++) { + String currentHeader = childRow.getHeaders().get(i); + + if (StringUtils.isBlank(dataMap.get(currentHeader))) { + // Merge headers + if (!this.headers.contains(currentHeader)) + this.headers.add(currentHeader); + + // Merge data + dataMap.put(currentHeader, childRow.getDataRows().get(0).get(currentHeader)); + } + LOGGER.debug("{}: {}", currentHeader, childRow.getDataRows().get(0).get(currentHeader)); + } + } + } +} From eab135f0a1f733118d4d0869a98f577e795ccc90 Mon Sep 17 00:00:00 2001 From: Andrei Kamarouski Date: Thu, 22 Jun 2023 14:26:17 +0300 Subject: [PATCH 3/7] changes --- pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pom.xml b/pom.xml index 7428efc..5466f55 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,7 @@ 11 1.0.5.REFACTORING-SNAPSHOT 1.7 + 4.1.2 1.7.30 3.5 7.7.1 @@ -115,6 +116,13 @@ ${commons-lang3.version} + + org.apache.poi + poi-ooxml + ${apache-poi.version} + + + org.testng testng From c826c36ac6504ae01069bf773db05e05bf526612 Mon Sep 17 00:00:00 2001 From: Andrei Kamarouski Date: Thu, 22 Jun 2023 14:28:42 +0300 Subject: [PATCH 4/7] changes --- .../DataProviderParameterGenerator.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/main/java/com/zebrunner/carina/dataprovider/DataProviderParameterGenerator.java b/src/main/java/com/zebrunner/carina/dataprovider/DataProviderParameterGenerator.java index d113339..a303ae1 100644 --- a/src/main/java/com/zebrunner/carina/dataprovider/DataProviderParameterGenerator.java +++ b/src/main/java/com/zebrunner/carina/dataprovider/DataProviderParameterGenerator.java @@ -23,7 +23,6 @@ import com.zebrunner.carina.utils.config.StandardConfigurationOption; import com.zebrunner.carina.utils.exception.InvalidArgsException; import com.zebrunner.carina.utils.parser.xls.XLSParser; -import com.zebrunner.carina.utils.resources.L10N; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -110,19 +109,6 @@ public static Object process(String param) { String key = param.substring(start, end); return StringUtils.replace(param, matcher.group(), getValueFromXLS(key)); } - - matcher = L10N_PATTERN.matcher(param); - String initStrL10N = param; - while (matcher.find()) { - int start = param.indexOf(SpecialKeywords.L10N + ":") + 5; - int end = param.indexOf('}'); - String key = param.substring(start, end); - param = StringUtils.replace(param, matcher.group(), L10N.getText(key)); - } - // in case if L10N pattern was applied - if (!initStrL10N.equalsIgnoreCase(param)) { - return param; - } } catch (Exception e) { LOGGER.error(e.getMessage()); } From 7034141d58db99a0de7ccf3d279bafacda5bfdef Mon Sep 17 00:00:00 2001 From: Andrei Kamarouski Date: Thu, 22 Jun 2023 14:57:04 +0300 Subject: [PATCH 5/7] changes --- pom.xml | 11 ----------- .../dataprovider/DataProviderParameterGenerator.java | 1 - 2 files changed, 12 deletions(-) diff --git a/pom.xml b/pom.xml index 5466f55..015bc1f 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,6 @@ 1.7 4.1.2 1.7.30 - 3.5 7.7.1 3.0.1 3.8.0 @@ -91,10 +90,6 @@ org.seleniumhq.selenium * - - io.rest-assured - * - @@ -110,12 +105,6 @@ ${slf4j.version} - - org.apache.commons - commons-lang3 - ${commons-lang3.version} - - org.apache.poi poi-ooxml diff --git a/src/main/java/com/zebrunner/carina/dataprovider/DataProviderParameterGenerator.java b/src/main/java/com/zebrunner/carina/dataprovider/DataProviderParameterGenerator.java index a303ae1..85e9f48 100644 --- a/src/main/java/com/zebrunner/carina/dataprovider/DataProviderParameterGenerator.java +++ b/src/main/java/com/zebrunner/carina/dataprovider/DataProviderParameterGenerator.java @@ -44,7 +44,6 @@ public class DataProviderParameterGenerator { private static final Pattern GENERATEN_PATTERN = Pattern.compile(SpecialKeywords.GENERATEN); private static final Pattern TESTDATA_PATTERN = Pattern.compile(SpecialKeywords.TESTDATA); private static final Pattern ENV_PATTERN = Pattern.compile(SpecialKeywords.ENV); - private static final Pattern L10N_PATTERN = Pattern.compile(SpecialKeywords.L10N_PATTERN); private static final Pattern EXCEL_PATTERN = Pattern.compile(SpecialKeywords.EXCEL); private static String uuid; From a56db048af0fd250d14168c808a04e3ab080a29e Mon Sep 17 00:00:00 2001 From: Andrei Kamarouski Date: Tue, 27 Jun 2023 23:16:41 +0300 Subject: [PATCH 6/7] change version of carina-utils to 1.1.5.P1-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 015bc1f..bcf384f 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ UTF-8 11 - 1.0.5.REFACTORING-SNAPSHOT + 1.1.5.P1-SNAPSHOT 1.7 4.1.2 1.7.30 From 559db7994fe4a4f0491572220568470092682194 Mon Sep 17 00:00:00 2001 From: Andrei Kamarouski Date: Thu, 29 Jun 2023 14:05:08 +0300 Subject: [PATCH 7/7] bump up carina-utils to 1.1.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bcf384f..eabfb5c 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ UTF-8 11 - 1.1.5.P1-SNAPSHOT + 1.1.5 1.7 4.1.2 1.7.30