diff --git a/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeParameterizedProcessorImpl.java b/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeParameterizedProcessorImpl.java index cf4861296..30ff16661 100644 --- a/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeParameterizedProcessorImpl.java +++ b/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeParameterizedProcessorImpl.java @@ -7,13 +7,14 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.text.StringSubstitutor; import org.jsmart.zerocode.core.domain.ScenarioSpec; +import org.jsmart.zerocode.core.utils.TokenUtils; import org.slf4j.Logger; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.IntStream; import static org.jsmart.zerocode.core.constants.ZerocodeConstants.DSL_FORMAT; import static org.jsmart.zerocode.core.di.provider.CsvParserProvider.LINE_SEPARATOR; @@ -115,10 +116,13 @@ private ScenarioSpec resolveParamsCsv(ScenarioSpec scenario, int paramIndex) { return scenario; } - Map valuesMap = new HashMap<>(); + String[] headers = retrieveCsvHeaders(parameterizedCsvList.get(0)); + + paramIndex = headers == null ? paramIndex : paramIndex+1; + String csvLine = parameterizedCsvList.get(paramIndex); - resolveCsvLine(valuesMap, csvLine); + Map valuesMap = resolveCsvLine(csvLine, headers); String resultantStepJson = replaceWithValues(stepJson, valuesMap); @@ -129,11 +133,25 @@ private ScenarioSpec resolveParamsCsv(ScenarioSpec scenario, int paramIndex) { } } - private void resolveCsvLine(Map valuesMap, String csvLine) { + private String[] retrieveCsvHeaders(String csvHeaderLine) { + String[] parsedHeaderLine = csvParser.parseLine(csvHeaderLine + LINE_SEPARATOR); + boolean hasHeader = parsedHeaderLine.length > 0 && Arrays.stream(parsedHeaderLine).allMatch(s -> s.matches("^\\|.*\\|$")); + return !hasHeader ? null : Arrays.stream(parsedHeaderLine).map(s -> s.substring(1,s.length()-1)).toArray(String[]::new); + } + + private Map resolveCsvLine(String csvLine, String[] headers) { + Map valuesMap = new HashMap<>(); String[] parsedLine = csvParser.parseLine(csvLine + LINE_SEPARATOR); - AtomicLong index = new AtomicLong(0); - Arrays.stream(parsedLine) - .forEach(thisValue -> valuesMap.put(index.getAndIncrement() + "", thisValue)); + IntStream.range(0, parsedLine.length).forEach(i -> valuesMap.put(i + "", parsedLine[i])); + + if (headers != null){ + IntStream.range(0, headers.length).forEach(i -> { + if(!headers[i].contains(" ") && !headers[i].isEmpty()){ + valuesMap.put("PARAM."+headers[i], TokenUtils.resolveKnownTokens(parsedLine[i]).toString()); + } + }); + } + return valuesMap; } private String replaceWithValues(String stepJson, Map valuesMap) { diff --git a/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java b/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java index 93ee71ca4..ca5518366 100644 --- a/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java +++ b/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java @@ -10,9 +10,13 @@ import static java.util.Optional.ofNullable; import static org.apache.commons.collections4.CollectionUtils.isEmpty; import static org.jsmart.zerocode.core.constants.ZerocodeConstants.KAFKA_TOPIC; + +import org.jsmart.zerocode.core.domain.Parameterized; import org.jsmart.zerocode.core.domain.ScenarioSpec; import org.jsmart.zerocode.core.domain.Step; import org.jsmart.zerocode.core.domain.builders.ZeroCodeExecReportBuilder; + +import static org.jsmart.zerocode.core.di.provider.CsvParserProvider.LINE_SEPARATOR; import static org.jsmart.zerocode.core.domain.builders.ZeroCodeExecReportBuilder.newInstance; import org.jsmart.zerocode.core.domain.builders.ZeroCodeIoWriteBuilder; import org.jsmart.zerocode.core.engine.assertion.FieldAssertionMatcher; @@ -30,7 +34,6 @@ import org.jsmart.zerocode.core.utils.ApiTypeUtils; import static org.jsmart.zerocode.core.utils.ApiTypeUtils.apiType; import static org.jsmart.zerocode.core.utils.RunnerUtils.getFullyQualifiedUrl; -import static org.jsmart.zerocode.core.utils.RunnerUtils.getParameterSize; import static org.jsmart.zerocode.core.utils.SmartUtils.prettyPrintJson; import org.junit.runner.Description; import org.junit.runner.notification.RunNotifier; @@ -42,6 +45,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Arrays; import java.util.function.BiConsumer; @Singleton @@ -551,6 +555,24 @@ private int deriveScenarioLoopTimes(ScenarioSpec scenario) { return scenarioLoopTimes; } + private int getParameterSize(Parameterized parameterized) { + if (parameterized == null) { + return 0; + } + + List valueSource = parameterized.getValueSource(); + List csvSource = parameterized.getCsvSource(); + int csvSourceSize = 0; + + if (csvSource != null && !csvSource.isEmpty()){ + String[] parsedHeaderLine = csvParser.parseLine(csvSource.get(0) + LINE_SEPARATOR); + boolean hasHeader = parsedHeaderLine.length > 0 && Arrays.stream(parsedHeaderLine).allMatch(s -> s.matches("^\\|.*\\|$")); + csvSourceSize = hasHeader ? csvSource.size() -1 : csvSource.size(); + } + + return valueSource != null ? valueSource.size() : csvSourceSize; + } + private List compareStepResults(Step thisStep, String actualResult, String expectedResult, String resolvedScenarioState) { List failureResults = new ArrayList<>(); diff --git a/core/src/main/java/org/jsmart/zerocode/core/utils/RunnerUtils.java b/core/src/main/java/org/jsmart/zerocode/core/utils/RunnerUtils.java index 9465182b8..9c0516f8a 100644 --- a/core/src/main/java/org/jsmart/zerocode/core/utils/RunnerUtils.java +++ b/core/src/main/java/org/jsmart/zerocode/core/utils/RunnerUtils.java @@ -120,18 +120,6 @@ public static int loopCount(Step thisStep) { return stepLoopTimes > 0 ? stepLoopTimes: MIN_COUNT; } - public static int getParameterSize(Parameterized parameterized) { - if (parameterized == null) { - return 0; - } - - List valueSource = parameterized.getValueSource(); - List csvSource = parameterized.getCsvSource(); - - return valueSource != null ? valueSource.size() : - (csvSource != null ? csvSource.size() : 0); - } - public static void handleTestCompleted(RunListener reportListener, Logger logger) { if (CHARTS_AND_CSV.equals(getProperty(ZEROCODE_JUNIT))) { /** diff --git a/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeParameterizedProcessorImplTest.java b/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeParameterizedProcessorImplTest.java index 3bdc973f8..6f2e03a85 100644 --- a/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeParameterizedProcessorImplTest.java +++ b/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeParameterizedProcessorImplTest.java @@ -1,10 +1,12 @@ package org.jsmart.zerocode.core.engine.preprocessor; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.univocity.parsers.csv.CsvParser; import jakarta.inject.Inject; import org.jsmart.zerocode.core.di.main.ApplicationMainModule; import org.jsmart.zerocode.core.domain.ScenarioSpec; +import org.jsmart.zerocode.core.domain.Step; import org.jsmart.zerocode.core.utils.SmartUtils; import org.jukito.JukitoRunner; import org.jukito.TestModule; @@ -75,7 +77,7 @@ public void testProcessParameterized_values() throws Exception { @Test public void testProcessParameterized_csv() throws Exception { String jsonDocumentAsString = smartUtils - .getJsonDocumentAsString("unit_test_files/engine_unit_test_jsons/11_scenario_parameterized_csv.json"); + .getJsonDocumentAsString("unit_test_files/engine_unit_test_jsons/11.1_scenario_parameterized_csv.json"); ScenarioSpec scenarioSpec = mapper.readValue(jsonDocumentAsString, ScenarioSpec.class); ScenarioSpec scenarioSpecResolved = parameterizedProcessor.resolveParameterized(scenarioSpec, 0); @@ -87,4 +89,23 @@ public void testProcessParameterized_csv() throws Exception { assertThat(scenarioSpecResolved.getSteps().get(0).getAssertions().get("status").asInt(), is(400)); } + + @Test + public void testProcessParameterized_csv_with_named_random() throws Exception { + String jsonDocumentAsString = smartUtils + .getJsonDocumentAsString("unit_test_files/engine_unit_test_jsons/11.2_scenario_parameterized_csv_with_named_random.json"); + ScenarioSpec scenarioSpec = mapper.readValue(jsonDocumentAsString, ScenarioSpec.class); + + ScenarioSpec scenarioSpecResolved = parameterizedProcessor.resolveParameterized(scenarioSpec, 0); + Step step = scenarioSpecResolved.getSteps().get(0); + assertThat(step.getUrl(), is("/anUrl/${RANDOM.NUMBER}/${RANDOM.NUMBER}")); + JsonNode queryParams = step.getRequest().get("queryParams"); + assertThat(queryParams.get("id1"), is(queryParams.get("id2"))); + assertThat(queryParams.get("addressId1"), is(queryParams.get("addressId2"))); + assertThat(scenarioSpecResolved.getSteps().get(0).getAssertions().get("status").asInt(), is(200)); + + scenarioSpecResolved = parameterizedProcessor.resolveParameterized(scenarioSpec, 1); + assertThat(scenarioSpecResolved.getSteps().get(0).getUrl(), is("/anUrl/11/22")); + assertThat(scenarioSpecResolved.getSteps().get(0).getAssertions().get("status").asInt(), is(400)); + } } \ No newline at end of file diff --git a/core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java b/core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java index d8e22b66f..247d04efb 100644 --- a/core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java +++ b/core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java @@ -76,7 +76,7 @@ public void willGetJsonFileIntoA_JavaString() throws Exception { @Test public void willReadAllfileNamesFrom_TestResource() { List allTestCaseFiles = SmartUtils.getAllEndPointFiles("unit_test_files/engine_unit_test_jsons"); - assertThat(allTestCaseFiles.size(), is(22)); + assertThat(allTestCaseFiles.size(), is(23)); assertThat(allTestCaseFiles.get(0), is("unit_test_files/engine_unit_test_jsons/00_test_json_single_step_verifications.json")); } diff --git a/core/src/test/java/org/jsmart/zerocode/parameterized/ParameterisedCsvDemoTest.java b/core/src/test/java/org/jsmart/zerocode/parameterized/ParameterisedCsvDemoTest.java index 8f36a1d43..bf97a0930 100644 --- a/core/src/test/java/org/jsmart/zerocode/parameterized/ParameterisedCsvDemoTest.java +++ b/core/src/test/java/org/jsmart/zerocode/parameterized/ParameterisedCsvDemoTest.java @@ -14,4 +14,9 @@ public class ParameterisedCsvDemoTest { @JsonTestCase("integration_test_files/parameterized/parameterized_sample_csv_test.json") public void testParameterizedCsv() throws Exception { } + + @Test + @JsonTestCase("integration_test_files/parameterized/parameterized_sample_csv_with_headers_test.json") + public void testParameterizedCsvWithHeaders() throws Exception { + } } diff --git a/core/src/test/resources/integration_test_files/parameterized/parameterized_sample_csv_with_headers_test.json b/core/src/test/resources/integration_test_files/parameterized/parameterized_sample_csv_with_headers_test.json new file mode 100644 index 000000000..2b1eff2a3 --- /dev/null +++ b/core/src/test/resources/integration_test_files/parameterized/parameterized_sample_csv_with_headers_test.json @@ -0,0 +1,29 @@ +{ + "scenarioName": "Parameterized test scenario demo", + "steps": [ + { + "name": "get_user", + "url": "", + "operation": "", + "request": { + "status": "${2}", + "body": { + "login": "octocat-${PARAM.id}-${PARAM.AddressId}" + } + }, + "assertions": { + "status": "${$.get_user.response.status}", + "body": { + "login": "octocat-${PARAM.id}-${PARAM.AddressId}" + } + } + } + ], + "parameterized": { + "csvSource":[ + "|id|, |AddressId|, |status|", + "${RANDOM.NUMBER}, ${RANDOM.NUMBER}, 200", + "11, 22, 400" + ] + } +} \ No newline at end of file diff --git a/core/src/test/resources/unit_test_files/engine_unit_test_jsons/11_scenario_parameterized_csv.json b/core/src/test/resources/unit_test_files/engine_unit_test_jsons/11.1_scenario_parameterized_csv.json similarity index 100% rename from core/src/test/resources/unit_test_files/engine_unit_test_jsons/11_scenario_parameterized_csv.json rename to core/src/test/resources/unit_test_files/engine_unit_test_jsons/11.1_scenario_parameterized_csv.json diff --git a/core/src/test/resources/unit_test_files/engine_unit_test_jsons/11.2_scenario_parameterized_csv_with_named_random.json b/core/src/test/resources/unit_test_files/engine_unit_test_jsons/11.2_scenario_parameterized_csv_with_named_random.json new file mode 100644 index 000000000..7ec924900 --- /dev/null +++ b/core/src/test/resources/unit_test_files/engine_unit_test_jsons/11.2_scenario_parameterized_csv_with_named_random.json @@ -0,0 +1,29 @@ +{ + "scenarioName": "For deserialize only", + "ignoreStepFailures": true, + "steps": [ + { + "name": "step1", + "url": "/anUrl/${0}/${1}", + "operation": "GET", + "request": { + "queryParams": { + "id1": "${PARAM.id}", + "id2": "${PARAM.id}", + "addressId1": "${PARAM.AddressId}", + "addressId2": "${PARAM.AddressId}" + } + }, + "assertions": { + "status": "${PARAM.status}" + } + } + ], + "parameterized": { + "csvSource": [ + "|id|, |AddressId|, |status|", + "${RANDOM.NUMBER}, ${RANDOM.NUMBER}, 200", + "11, 22, 400" + ] + } +} diff --git a/http-testing/src/test/java/org/jsmart/zerocode/testhelp/tests/helloworldnamedcsvparam/HelloWorldNamedCsvParamsTest.java b/http-testing/src/test/java/org/jsmart/zerocode/testhelp/tests/helloworldnamedcsvparam/HelloWorldNamedCsvParamsTest.java new file mode 100644 index 000000000..e670a25d7 --- /dev/null +++ b/http-testing/src/test/java/org/jsmart/zerocode/testhelp/tests/helloworldnamedcsvparam/HelloWorldNamedCsvParamsTest.java @@ -0,0 +1,18 @@ +package org.jsmart.zerocode.testhelp.tests.helloworldnamedcsvparam; + +import org.jsmart.zerocode.core.domain.Scenario; +import org.jsmart.zerocode.core.domain.TargetEnv; +import org.jsmart.zerocode.core.runner.ZeroCodeUnitRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + +@TargetEnv("github_host.properties") +@RunWith(ZeroCodeUnitRunner.class) +public class HelloWorldNamedCsvParamsTest { + + @Test + @Scenario("parameterized_csv/hello_world_test_named_parameterized.json") + public void testNamedParameterized() throws Exception { + } + +} diff --git a/http-testing/src/test/resources/parameterized_csv/hello_world_test_named_parameterized.json b/http-testing/src/test/resources/parameterized_csv/hello_world_test_named_parameterized.json new file mode 100644 index 000000000..cb25c51e6 --- /dev/null +++ b/http-testing/src/test/resources/parameterized_csv/hello_world_test_named_parameterized.json @@ -0,0 +1,30 @@ +{ + "scenarioName": "Named Parameterized Scenario for ---> ${0}", + "steps": [ + { + "name": "get_user_details", + "url": "/users/${PARAM.USERID}", + "method": "GET", + "request": { + }, + "assertions": { + "status": 200, + "body": { + "login" : "${PARAM.USERID}", + "type" : "User", + "name" : "${PARAM.USERNAME}", + "location" : "${PARAM.ADDRESS}", + "id" : "(int)${PARAM.USERNO}", + "site_admin" : "(boolean)${PARAM.ISADMIN}" + } + } + } + ], + "parameterized": { + "csvSource":[ + "|USERID|, |USERNAME|, |ADDRESS|, |USERNO|, |ISADMIN|", + "octocat, The Octocat, San Francisco, 583231, false", + "siddhagalaxy, Sidd, UK, 33847730, false" + ] + } +}