diff --git a/pom.xml b/pom.xml
index fcb11a7e..b6624c75 100644
--- a/pom.xml
+++ b/pom.xml
@@ -130,7 +130,7 @@ OF THE POSSIBILITY OF SUCH DAMAGE.
com.jcabi
jcabi-matchers
- 1.7.0
+ 1.8.0
test
@@ -194,6 +194,18 @@ OF THE POSSIBILITY OF SUCH DAMAGE.
3.0
test
+
+ org.yaml
+ snakeyaml
+ 2.3
+ test
+
+
+ org.eolang
+ jucs
+ 0.2.0
+ test
+
org.cactoos
cactoos
diff --git a/src/main/java/org/xembly/Directives.java b/src/main/java/org/xembly/Directives.java
index 5f2c7160..abb90fe6 100644
--- a/src/main/java/org/xembly/Directives.java
+++ b/src/main/java/org/xembly/Directives.java
@@ -75,16 +75,13 @@
* @since 0.1
* @checkstyle ClassFanOutComplexity (500 lines)
*/
-@EqualsAndHashCode(callSuper = false, of = "all")
-@SuppressWarnings
- (
- {
- "PMD.TooManyMethods",
- "PMD.CyclomaticComplexity",
- "PMD.GodClass",
- "PMD.StdCyclomaticComplexity"
- }
- )
+@EqualsAndHashCode(of = "all")
+@SuppressWarnings({
+ "PMD.TooManyMethods",
+ "PMD.CyclomaticComplexity",
+ "PMD.GodClass",
+ "PMD.StdCyclomaticComplexity"
+})
public final class Directives implements Iterable {
/**
diff --git a/src/test/java/org/xembly/XemblerTest.java b/src/test/java/org/xembly/XemblerTest.java
index 991b3652..02c4390a 100644
--- a/src/test/java/org/xembly/XemblerTest.java
+++ b/src/test/java/org/xembly/XemblerTest.java
@@ -29,20 +29,33 @@
*/
package org.xembly;
+import com.jcabi.matchers.XPathMatcher;
import com.jcabi.matchers.XhtmlMatchers;
+import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
+import com.jcabi.xml.XPathContext;
+import java.util.Collection;
+import java.util.Map;
import javax.xml.parsers.DocumentBuilderFactory;
import net.jqwik.api.ForAll;
import net.jqwik.api.Property;
import org.apache.commons.lang3.StringUtils;
import org.cactoos.experimental.Threads;
+import org.cactoos.iterable.Mapped;
+import org.cactoos.list.ListOf;
import org.cactoos.scalar.LengthOf;
import org.cactoos.scalar.Repeated;
+import org.eolang.jucs.ClasspathSource;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
+import org.hamcrest.core.AllOf;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
+import org.yaml.snakeyaml.Yaml;
/**
* Test case for {@link Xembler}.
@@ -261,4 +274,49 @@ void printsNamespaces() {
Matchers.containsString("x:color=\"green\"")
);
}
+
+ @ParameterizedTest
+ @ClasspathSource(value = "org/xembly/stories/", glob = "**.yml")
+ void checksYamlStories(final String story) {
+ MatcherAssert.assertThat(
+ "modifies XML document with a few directives",
+ story,
+ new XemblerTest.StoryMatcher()
+ );
+ }
+
+ /**
+ * Matcher for stories.
+ *
+ * @since 0.33.0
+ */
+ private static final class StoryMatcher extends BaseMatcher {
+ @Override
+ @SuppressWarnings("unchecked")
+ public boolean matches(final Object item) {
+ final Map yaml = new Yaml().load(
+ String.class.cast(item.toString())
+ );
+ final Directives directives = new Directives();
+ for (final String dir : (Iterable) yaml.get("directives")) {
+ directives.append(new Directives(dir));
+ }
+ final XML xml = new XMLDocument(yaml.get("before").toString());
+ new Xembler(directives).applyQuietly(xml.inner());
+ return new AllOf<>(
+ new ListOf<>(
+ new Mapped<>(
+ str -> new XPathMatcher<>(str, new XPathContext()),
+ (Collection) yaml.get("xpaths")
+ )
+ )
+ ).matches(XhtmlMatchers.xhtml(xml.inner()));
+ }
+
+ @Override
+ public void describeTo(final Description description) {
+ description.appendText("matches");
+ }
+ }
+
}
diff --git a/src/test/resources/org/xembly/stories/simple.yml b/src/test/resources/org/xembly/stories/simple.yml
new file mode 100644
index 00000000..a2cda8f0
--- /dev/null
+++ b/src/test/resources/org/xembly/stories/simple.yml
@@ -0,0 +1,37 @@
+# Copyright (c) 2013-2025, Yegor Bugayenko
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met: 1) Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer. 2) Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution. 3) Neither the name of the xembly.org nor
+# the names of its contributors may be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
+# NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+---
+before: |
+
+directives:
+ - XPATH "/root";
+ - ADD "hello";
+ - ATTR "foo", "bar";
+xpaths:
+ - /root/hello
+ - /root/hello[@foo='bar']