diff --git a/webtau-core/src/main/java/org/testingisdocumenting/webtau/data/traceable/TraceableValue.java b/webtau-core/src/main/java/org/testingisdocumenting/webtau/data/traceable/TraceableValue.java index fccb0adb2..46c42e448 100644 --- a/webtau-core/src/main/java/org/testingisdocumenting/webtau/data/traceable/TraceableValue.java +++ b/webtau-core/src/main/java/org/testingisdocumenting/webtau/data/traceable/TraceableValue.java @@ -1,4 +1,5 @@ /* + * Copyright 2021 webtau maintainers * Copyright 2019 TWO SIGMA OPEN SOURCE, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,8 +20,8 @@ import java.util.function.Supplier; public class TraceableValue { - private static ThreadLocal isTracingDisabled = ThreadLocal.withInitial(() -> Boolean.FALSE); - private static ThreadLocal isAlwaysFuzzyPassedTracing = ThreadLocal.withInitial(() -> Boolean.FALSE); + private static final ThreadLocal isTracingDisabled = ThreadLocal.withInitial(() -> Boolean.FALSE); + private static final ThreadLocal isAlwaysFuzzyPassedTracing = ThreadLocal.withInitial(() -> Boolean.FALSE); private CheckLevel checkLevel; private final Object value; diff --git a/webtau-docs/znai/HTTP/data-node.md b/webtau-docs/znai/HTTP/data-node.md index afb4d0e69..fc9da0520 100644 --- a/webtau-docs/znai/HTTP/data-node.md +++ b/webtau-docs/znai/HTTP/data-node.md @@ -23,7 +23,7 @@ to get values required for consequent test calls. If you have a list of objects like `complexList` above, you can access all its children property value with `complexList.k2`. -:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "groovy children key shortcut", bodyOnly: true} +:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "children key shortcut", bodyOnly: true} # Path based properties access @@ -56,34 +56,34 @@ Special values inside assertion block have convenient methods `each` to iterate over a list -:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "groovy each on simple list", bodyOnly: true, title: "List of simple values"} +:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "each on simple list", bodyOnly: true, title: "List of simple values"} -:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "groovy each on complex list", bodyOnly: true, title: "List of complex values"} +:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "each on complex list", bodyOnly: true, title: "List of complex values"} # Find `find` to find a single value -:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "groovy find on list", bodyOnly: true} +:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "find on list", bodyOnly: true} and `findAll` to find all the values matching predicate -:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "groovy findAll on list", bodyOnly: true} +:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "findAll on list", bodyOnly: true} Note: While values inside a predicate are normal values, the result of `find` and `findAll` is still `DataNode` -:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "groovy find on list of objects", bodyOnly: true} +:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "find on list of objects", bodyOnly: true} # Collect Use `collect` to transform a collection of items -:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "groovy transform list", bodyOnly: true} +:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "transform list", bodyOnly: true} # Combine Methods `find` and `collect` can be chained -:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "groovy findAll, collect, and sum", bodyOnly: true} +:include-groovy: org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy {entry: "findAll, collect, and sum", bodyOnly: true} diff --git a/webtau-http-groovy/src/main/groovy/org/testingisdocumenting/webtau/http/datanode/GroovyDataNode.groovy b/webtau-http-groovy/src/main/groovy/org/testingisdocumenting/webtau/http/datanode/GroovyDataNode.groovy index 8f8955209..2ec998b83 100644 --- a/webtau-http-groovy/src/main/groovy/org/testingisdocumenting/webtau/http/datanode/GroovyDataNode.groovy +++ b/webtau-http-groovy/src/main/groovy/org/testingisdocumenting/webtau/http/datanode/GroovyDataNode.groovy @@ -17,6 +17,7 @@ package org.testingisdocumenting.webtau.http.datanode +import org.testingisdocumenting.webtau.data.traceable.CheckLevel import org.testingisdocumenting.webtau.data.traceable.TraceableValue import org.testingisdocumenting.webtau.expectation.ActualPath import org.testingisdocumenting.webtau.http.datacoverage.DataNodeToMapOfValuesConverter @@ -118,13 +119,26 @@ class GroovyDataNode implements DataNodeExpectations, DataNode { DataNode find(Closure predicate) { def result = node.elements().find(delegateToNodeAndRemovedDataNodeFromClosure(predicate)) - return (result instanceof DataNode) ? - new GroovyDataNode(result) : - result + + if (result instanceof DataNode) { + if (result.singleValue) { + result.traceableValue.updateCheckLevel(CheckLevel.FuzzyPassed) + } + return new GroovyDataNode(result) + } + + return result } DataNode findAll(Closure predicate) { def list = node.elements().findAll(delegateToNodeAndRemovedDataNodeFromClosure(predicate)) + + list.each { + if (it.isSingleValue()) { + it.traceableValue.updateCheckLevel(CheckLevel.FuzzyPassed) + } + } + return wrapIntoDataNode('findAll', list) } diff --git a/webtau-http-groovy/src/test/groovy/org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy b/webtau-http-groovy/src/test/groovy/org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy index f15228775..1d770ab4d 100644 --- a/webtau-http-groovy/src/test/groovy/org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy +++ b/webtau-http-groovy/src/test/groovy/org/testingisdocumenting/webtau/http/HttpGroovyTest.groovy @@ -515,14 +515,14 @@ class HttpGroovyTest extends HttpTestBase { } @Test - void "groovy each on simple list"() { + void "each on simple list"() { http.get("/end-point") { list.each { it.shouldBe > 0 } } } @Test - void "groovy each on complex list"() { + void "each on complex list"() { http.get("/end-point") { complexList.each { k2.shouldBe > 0 } } @@ -541,7 +541,7 @@ class HttpGroovyTest extends HttpTestBase { } @Test - void "groovy find on list"() { + void "find on list"() { def found = http.get("/end-point") { return list.find { it > 1 } } @@ -551,7 +551,19 @@ class HttpGroovyTest extends HttpTestBase { } @Test - void "groovy find on body that is not a list"() { + void "find should mark value as fuzzy checked"() { + http.get("/end-point") { + return list.find { it > 1 } + } + + def listElements = http.lastValidationResult.bodyNode.get("list").elements() + listElements.get(0).traceableValue.checkLevel.should == CheckLevel.None + listElements.get(1).traceableValue.checkLevel.should == CheckLevel.FuzzyPassed + listElements.get(2).traceableValue.checkLevel.should == CheckLevel.None + } + + @Test + void "find on body that is not a list"() { def found = http.get("/end-point") { return body.find { it > 1 } } @@ -560,7 +572,7 @@ class HttpGroovyTest extends HttpTestBase { } @Test - void "groovy findAll on list"() { + void "findAll on list"() { def found = http.get("/end-point") { return list.findAll { it > 1 } } @@ -570,7 +582,19 @@ class HttpGroovyTest extends HttpTestBase { } @Test - void "groovy findAll on body that is not a list"() { + void "findAll on list should mark values as fuzzy checked"() { + def found = http.get("/end-point") { + return list.findAll { it > 1 } + } + + def listElements = http.lastValidationResult.bodyNode.get("list").elements() + listElements.get(0).traceableValue.checkLevel.should == CheckLevel.None + listElements.get(1).traceableValue.checkLevel.should == CheckLevel.FuzzyPassed + listElements.get(2).traceableValue.checkLevel.should == CheckLevel.FuzzyPassed + } + + @Test + void "findAll on body that is not a list"() { def found = http.get("/end-point") { return body.findAll { it > 1 } } @@ -579,7 +603,7 @@ class HttpGroovyTest extends HttpTestBase { } @Test - void "groovy find on list of objects"() { + void "find on list of objects"() { def id = http.get("/end-point") { def found = complexList.find { assert k1.getClass() == String @@ -594,7 +618,7 @@ class HttpGroovyTest extends HttpTestBase { } @Test - void "groovy transform list"() { + void "transform list"() { def transformed = http.get("/end-point") { return list.collect { "world#${it}" } } @@ -604,7 +628,7 @@ class HttpGroovyTest extends HttpTestBase { } @Test - void "groovy transform list by referencing node"() { + void "transform list by referencing node"() { def ids = http.get("/end-point") { return complexList.collect { it.id } } @@ -613,7 +637,7 @@ class HttpGroovyTest extends HttpTestBase { } @Test - void "groovy transform on body that is not a list"() { + void "transform on body that is not a list"() { def transformed = http.get("/end-point") { return body.collect { "world#${it}" } } @@ -622,7 +646,7 @@ class HttpGroovyTest extends HttpTestBase { } @Test - void "groovy findAll, collect, and sum"() { + void "findAll, collect, and sum"() { def sum = http.get("/end-point") { return complexList .findAll { k1.startsWith('v1') } @@ -634,7 +658,7 @@ class HttpGroovyTest extends HttpTestBase { } @Test - void "groovy children key shortcut"() { + void "children key shortcut"() { http.get("/end-point") { complexList.k2.should == [30, 40] }