Skip to content

Commit ea6eaa9

Browse files
matchers: add "change" value matcher (#1559)
1 parent 2782f95 commit ea6eaa9

20 files changed

Lines changed: 521 additions & 32 deletions

File tree

webtau-browser/src/main/java/org/testingisdocumenting/webtau/browser/page/PageElement.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import org.testingisdocumenting.webtau.browser.page.path.filter.ByTextPageElementsFilter;
3535
import org.testingisdocumenting.webtau.browser.page.path.filter.NearbyPageElementFilter;
3636
import org.testingisdocumenting.webtau.browser.page.path.finder.*;
37+
import org.testingisdocumenting.webtau.data.snapshot.SnapshotValue;
38+
import org.testingisdocumenting.webtau.data.snapshot.SnapshotValueAware;
3739
import org.testingisdocumenting.webtau.data.ValuePath;
3840
import org.testingisdocumenting.webtau.data.render.PrettyPrintable;
3941
import org.testingisdocumenting.webtau.data.render.PrettyPrinter;
@@ -61,6 +63,7 @@ public class PageElement implements
6163
ActualValueExpectations,
6264
PrettyPrintable,
6365
ActualPathAndDescriptionAware,
66+
SnapshotValueAware,
6467
VisibleStateAware {
6568
private final static TokenizedMessage HIDDEN_MESSAGE_STRING_VALUE = tokenizedMessage().string("*****");
6669

@@ -83,6 +86,7 @@ public class PageElement implements
8386
private final AdditionalBrowserInteractions additionalBrowserInteractions;
8487
private final PageElementPath path;
8588
private final TokenizedMessage pathDescription;
89+
private final SnapshotValue snapshotValue;
8690

8791
private final boolean isMarkedAsAll;
8892

@@ -106,6 +110,7 @@ public PageElement(WebDriver driver,
106110
this.offsetWidth = new PageElementValue<>(this, "offsetWidth", fetchIntElementPropertyFunc("offsetWidth"));
107111
this.clientHeight = new PageElementValue<>(this, "clientHeight", fetchIntElementPropertyFunc("clientHeight"));
108112
this.clientWidth = new PageElementValue<>(this, "clientWidth", fetchIntElementPropertyFunc("clientWidth"));
113+
this.snapshotValue = SnapshotValue.EMPTY;
109114
}
110115

111116
public PageElementValue<String> attribute(String name) {
@@ -569,6 +574,27 @@ private void checkNotNullAndExecuteScriptOnElement(String actionLabel, String sc
569574
((JavascriptExecutor) driver).executeScript(script, argsList.toArray(new Object[0]));
570575
}
571576

577+
@Override
578+
public void takeSnapshot() {
579+
snapshotValue.take(extractActualValue());
580+
}
581+
582+
@Override
583+
public Object snapshotValue() {
584+
return snapshotValue.required();
585+
}
586+
587+
@Override
588+
public Object currentValue() {
589+
return extractActualValue();
590+
}
591+
592+
private Object extractActualValue() {
593+
return isMarkedAsAll ?
594+
valuesList.get() :
595+
value.get();
596+
}
597+
572598
private interface ActionsProvider {
573599
void perform(Actions actions, WebElement element);
574600
}

webtau-core-groovy/src/main/groovy/org/testingisdocumenting/webtau/data/expectation/ExpectationExtension.groovy

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,22 @@ class ExpectationExtension {
4343
throw new IllegalStateException(SHOULD_BE_REPLACED_MESSAGE)
4444
}
4545

46+
static void waitTo(actual, ValueMatcher valueMatcher) {
47+
new ActualValue(actual).waitTo(valueMatcher)
48+
}
49+
50+
static void waitToBe(actual, ValueMatcher valueMatcher) {
51+
wait(actual, valueMatcher)
52+
}
53+
54+
static void waitToNot(actual, ValueMatcher valueMatcher) {
55+
new ActualValue(actual).waitToNot(valueMatcher)
56+
}
57+
58+
static void waitToNotBe(actual, ValueMatcher valueMatcher) {
59+
waitToNot(actual, valueMatcher)
60+
}
61+
4662
static Object getWaitTo(actual) {
4763
throw new IllegalStateException(SHOULD_BE_REPLACED_MESSAGE)
4864
}

webtau-core-groovy/src/test/groovy/org/testingisdocumenting/webtau/expectation/code/ChangeCodeMatcherGroovyTest.groovy renamed to webtau-core-groovy/src/test/groovy/org/testingisdocumenting/webtau/expectation/code/ValueChangeCodeMatcherGroovyTest.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import org.testingisdocumenting.webtau.data.DbEntity
2121

2222
import static org.testingisdocumenting.webtau.Matchers.*
2323

24-
class ChangeCodeMatcherGroovyTest {
24+
class ValueValueChangeCodeMatcherGroovyTest {
2525
@Test
2626
void "change java bean single property"() {
2727
def dbEntity = new DbEntity()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2024 webtau maintainers
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.testingisdocumenting.webtau.expectation.equality
18+
19+
import org.junit.Test
20+
21+
import static org.testingisdocumenting.webtau.Matchers.*
22+
23+
class SnapshotChangeValueMatcherGroovyTest {
24+
@Test
25+
void "should change"() {
26+
def value = new SnapshotAwareDummyValue()
27+
value.takeSnapshot()
28+
29+
value.doOperation()
30+
// value-should-change
31+
value.should change
32+
// value-should-change
33+
}
34+
35+
@Test
36+
void "wait change"() {
37+
def value = new SnapshotAwareDummyValue()
38+
value.takeSnapshot()
39+
value.enableAutoIncrement()
40+
41+
// value-wait-to-change
42+
value.waitTo change
43+
// value-wait-to-change
44+
}
45+
}

webtau-core/src/main/java/org/testingisdocumenting/webtau/Matchers.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import org.testingisdocumenting.webtau.data.converters.ObjectProperties;
2121
import org.testingisdocumenting.webtau.data.live.LiveValue;
2222
import org.testingisdocumenting.webtau.expectation.*;
23-
import org.testingisdocumenting.webtau.expectation.code.ChangeCodeMatcher;
23+
import org.testingisdocumenting.webtau.expectation.code.ValueChangeCodeMatcher;
2424
import org.testingisdocumenting.webtau.expectation.code.ThrowExceptionMatcher;
2525
import org.testingisdocumenting.webtau.expectation.contain.ContainAllMatcher;
2626
import org.testingisdocumenting.webtau.expectation.contain.ContainExactlyMatcher;
@@ -44,6 +44,11 @@ public class Matchers {
4444
*/
4545
public static final ValueMatcher anyValue = new AnyValueMatcher();
4646

47+
/**
48+
* snapshot based value change matcher. Check/wait for value to change/remain the same
49+
*/
50+
public static final ValueMatcher change = new SnapshotChangeValueMatcher();
51+
4752
/**
4853
* visible matcher to check if UI element is visible
4954
* @see #hidden
@@ -62,7 +67,7 @@ public class Matchers {
6267
* actual(value).should(beGreaterThan(10));
6368
* actual(value).shouldNot(beGreaterThan(10));
6469
* </pre>
65-
* Note: In Groovy you can do <code>value.should beGreaterThan(10)</code>
70+
* Note: In Groovy you can do <code>value.shouldBe > 10</code>
6671
* @param actual value to assert against
6772
* @return Object to chain a matcher against
6873
*/
@@ -76,7 +81,7 @@ public static <E> ActualValueExpectations actual(E actual) {
7681
* actual(price, "price").should(beGreaterThan(10));
7782
* actual(price, "price").shouldNot(beGreaterThan(10));
7883
* </pre>
79-
* Note: In Groovy you can do <code>price.should beGreaterThan(10)</code>
84+
* Note: In Groovy you can do <code>price.shouldBe > 10</code>
8085
* @param actual value to assert against
8186
* @param path path to use in the reporting
8287
* @return Object to chain a matcher against
@@ -432,8 +437,8 @@ public static ThrowExceptionMatcher throwException(Class<?> expectedClass, Value
432437
* @param valueSupplier value supplier to get before/after values for comparison
433438
* @return code matcher instance
434439
*/
435-
public static ChangeCodeMatcher change(String label, Supplier<Object> valueSupplier) {
436-
return new ChangeCodeMatcher(label, valueSupplier);
440+
public static ValueChangeCodeMatcher change(String label, Supplier<Object> valueSupplier) {
441+
return new ValueChangeCodeMatcher(label, valueSupplier);
437442
}
438443

439444
/**
@@ -447,10 +452,10 @@ public static ChangeCodeMatcher change(String label, Supplier<Object> valueSuppl
447452
* @param object object which properties will be extracted for before/after comparison
448453
* @return code matcher instance
449454
*/
450-
public static ChangeCodeMatcher change(String label, Object object) {
455+
public static ValueChangeCodeMatcher change(String label, Object object) {
451456
// case for Groovy closures to avoid them being treated as Java Beans
452457
if (object instanceof Callable) {
453-
return new ChangeCodeMatcher(label, () -> {
458+
return new ValueChangeCodeMatcher(label, () -> {
454459
try {
455460
return ((Callable<?>) object).call();
456461
} catch (Exception e) {
@@ -459,6 +464,6 @@ public static ChangeCodeMatcher change(String label, Object object) {
459464
});
460465
}
461466

462-
return new ChangeCodeMatcher(label, () -> new ObjectProperties(object));
467+
return new ValueChangeCodeMatcher(label, () -> new ObjectProperties(object));
463468
}
464469
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2024 webtau maintainers
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.testingisdocumenting.webtau.data.snapshot;
18+
19+
/**
20+
* Snapshot value that maintains a state whether it was taken or not
21+
*/
22+
public class SnapshotValue {
23+
private Object value;
24+
private boolean isTaken;
25+
26+
public static final SnapshotValue EMPTY = new SnapshotValue(null, false);
27+
28+
public void take(Object value) {
29+
this.isTaken = true;
30+
this.value = value;
31+
}
32+
33+
private SnapshotValue(Object value, boolean isTaken) {
34+
this.value = value;
35+
this.isTaken = isTaken;
36+
}
37+
38+
public void reset() {
39+
isTaken = false;
40+
value = null;
41+
}
42+
43+
public Object required() {
44+
if (!isTaken) {
45+
throw new IllegalStateException("snapshot value was not taken, use takeSnapshot() prior to this call");
46+
}
47+
48+
return value;
49+
}
50+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2024 webtau maintainers
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.testingisdocumenting.webtau.data.snapshot;
18+
19+
public interface SnapshotValueAware {
20+
void takeSnapshot();
21+
Object snapshotValue();
22+
Object currentValue();
23+
}

webtau-core/src/main/java/org/testingisdocumenting/webtau/expectation/code/ChangeCodeMatcher.java renamed to webtau-core/src/main/java/org/testingisdocumenting/webtau/expectation/code/ValueChangeCodeMatcher.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@
2626

2727
import static org.testingisdocumenting.webtau.WebTauCore.*;
2828

29-
public class ChangeCodeMatcher implements CodeMatcher {
29+
public class ValueChangeCodeMatcher implements CodeMatcher {
3030
private CompareToComparator comparator;
3131
private final Supplier<Object> valueSupplier;
3232
private final String label;
3333

34-
public ChangeCodeMatcher(String label, Supplier<Object> valueSupplier) {
34+
public ValueChangeCodeMatcher(String label, Supplier<Object> valueSupplier) {
3535
this.label = label;
3636
this.valueSupplier = valueSupplier;
3737
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2024 webtau maintainers
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.testingisdocumenting.webtau.expectation.equality;
18+
19+
import org.testingisdocumenting.webtau.data.snapshot.SnapshotValueAware;
20+
import org.testingisdocumenting.webtau.data.ValuePath;
21+
import org.testingisdocumenting.webtau.expectation.ValueMatcher;
22+
import org.testingisdocumenting.webtau.reporter.TokenizedMessage;
23+
24+
import static org.testingisdocumenting.webtau.WebTauCore.*;
25+
26+
public class SnapshotChangeValueMatcher implements ValueMatcher {
27+
private static final String VALUE_MUST_IMPLEMENT = "actual value must implement SnapshotValueAware interface";
28+
private CompareToComparator comparator;
29+
30+
@Override
31+
public TokenizedMessage matchingTokenizedMessage(ValuePath actualPath, Object actual) {
32+
comparator = CompareToComparator.comparator();
33+
return tokenizedMessage().matcher("to change");
34+
}
35+
36+
@Override
37+
public TokenizedMessage matchedTokenizedMessage(ValuePath actualPath, Object actual) {
38+
return tokenizedMessage().matcher("changed");
39+
}
40+
41+
@Override
42+
public TokenizedMessage mismatchedTokenizedMessage(ValuePath actualPath, Object actual) {
43+
return comparator.generateEqualMatchReport();
44+
}
45+
46+
@Override
47+
public boolean matches(ValuePath actualPath, Object actual) {
48+
comparator.resetReportData();
49+
50+
if (!(actual instanceof SnapshotValueAware snapshotAware)) {
51+
throw new IllegalArgumentException(VALUE_MUST_IMPLEMENT);
52+
}
53+
54+
var before = snapshotAware.snapshotValue();
55+
var after = snapshotAware.currentValue();
56+
57+
return comparator.compareIsNotEqual(actualPath, after, before);
58+
}
59+
60+
@Override
61+
public TokenizedMessage negativeMatchingTokenizedMessage(ValuePath actualPath, Object actual) {
62+
comparator = CompareToComparator.comparator(CompareToComparator.AssertionMode.NOT_EQUAL);
63+
return tokenizedMessage().matcher("to not change");
64+
}
65+
66+
@Override
67+
public TokenizedMessage negativeMatchedTokenizedMessage(ValuePath actualPath, Object actual) {
68+
return tokenizedMessage().matcher("didn't change");
69+
}
70+
71+
@Override
72+
public TokenizedMessage negativeMismatchedTokenizedMessage(ValuePath actualPath, Object actual) {
73+
return comparator.generateNotEqualMatchReport();
74+
}
75+
76+
@Override
77+
public boolean negativeMatches(ValuePath actualPath, Object actual) {
78+
comparator.resetReportData();
79+
80+
if (!(actual instanceof SnapshotValueAware snapshotAware)) {
81+
throw new IllegalArgumentException(VALUE_MUST_IMPLEMENT);
82+
}
83+
84+
var before = snapshotAware.snapshotValue();
85+
var after = snapshotAware.currentValue();
86+
87+
return comparator.compareIsEqual(actualPath, after, before);
88+
}
89+
}

webtau-core/src/test/java/org/testingisdocumenting/webtau/expectation/code/ChangeCodeMatcherJavaTest.java renamed to webtau-core/src/test/java/org/testingisdocumenting/webtau/expectation/code/ValueChangeCodeMatcherJavaTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import static org.testingisdocumenting.webtau.Matchers.*;
2323
import static org.testingisdocumenting.webtau.testutils.TestConsoleOutput.*;
2424

25-
public class ChangeCodeMatcherJavaTest {
25+
public class ValueChangeCodeMatcherJavaTest {
2626
@Test
2727
public void changeJavaBeanSingleProperty() {
2828
var dbEntity = new DbEntity();

0 commit comments

Comments
 (0)