Skip to content

Commit

Permalink
Merge pull request #533 from exadel-inc/release-2.5.2
Browse files Browse the repository at this point in the history
[Tech] Merged release-2.5.2 branch to master
  • Loading branch information
smiakchilo authored Jun 17, 2024
2 parents bc18e28 + 95b3237 commit f2de770
Show file tree
Hide file tree
Showing 77 changed files with 2,747 additions and 1,077 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The ToolKit provides the fastest way to supplement an AEM component based on a S

The ToolKit has many options to make authoring interfaces rich, flexible, and responsive with minimal effort. No deep knowledge of Granite™, Coral™, or AEM clientlibs is needed. A scope of fine interactivity features is ready out of the box with minimal to no manual setup.

The ToolKit is compliant with the newest facilities of AEM 6.4/6.5, Granite UI, and Coral v.3+, and has support for Coral v.2.
The ToolKit is compliant with the newest facilities of AEM 6.5, Granite UI, and Coral v.3+, and has support for Coral v.2.

The ToolKit is developed in the course of Exadel™ Marketing Technology Practice (the MarTech) as a part of **Exadel Toolbox** initiative.

Expand Down
2 changes: 1 addition & 1 deletion all/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>com.exadel.etoolbox</groupId>
<artifactId>etoolbox-authoring-kit</artifactId>
<version>2.5.1</version>
<version>2.5.2</version>
</parent>

<artifactId>etoolbox-authoring-kit-all</artifactId>
Expand Down
4 changes: 2 additions & 2 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>com.exadel.etoolbox</groupId>
<artifactId>etoolbox-authoring-kit</artifactId>
<version>2.5.1</version>
<version>2.5.2</version>
</parent>

<artifactId>etoolbox-authoring-kit-core</artifactId>
Expand Down Expand Up @@ -136,7 +136,7 @@
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,48 @@
package com.exadel.aem.toolkit.api.annotations.meta;

import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import com.google.common.base.CaseFormat;

import com.exadel.aem.toolkit.core.CoreConstants;

/**
* Enumerates transformations of a string value that can be applied when rendering as an attribute of a Granite UI entity
*/
public enum StringTransformation {

/**
* No transformation is applied (default value)
*/
NONE(null),

/**
* The string is converted to uppercase
*/
UPPERCASE(String::toUpperCase),

/**
* The string is converted to lowercase
*/
LOWERCASE(String::toLowerCase),
CAMELCASE(string -> CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, string.toLowerCase())),
CAPITALIZE(string -> WordUtils.capitalizeFully(string, ' ', '-'));

/**
* The string is split into words delimited by a space, a hyphen, or an underscore. The words are then merged
* into a {@code camelCase} string
*/
CAMELCASE(StringTransformation::toCamelCase),

/**
* The string is split into words delimited by a space, a hyphen, or an underscore. The first letter of each
* word is capitalized. The words are then merged with a space between them
*/
CAPITALIZE(StringTransformation::capitalize);

private static final char CHAR_SPACE = ' ';
private static final char CHAR_HYPHEN = '-';
private static final char CHAR_UNDERSCORE = '_';

private final UnaryOperator<String> transformation;

Expand All @@ -52,4 +79,42 @@ public String apply(String value) {
return transformation.apply(value);
}

/* ---------------
Transformations
--------------- */

/**
* Converts a string to camelCase
* @param value The string to be transformed
* @return Resulting string value
*/
private static String toCamelCase(String value) {
if (StringUtils.isBlank(value)) {
return value;
}
String[] words = value.contains(CoreConstants.SEPARATOR_HYPHEN)
? StringUtils.split(value, " -")
: StringUtils.split(value, " _");
return words[0].toLowerCase() + Stream.of(words)
.skip(1)
.map(StringUtils::lowerCase)
.map(StringUtils::capitalize)
.collect(Collectors.joining());
}

/**
* Capitalizes the first letter of each word in the provided string
* @param value The string to be transformed
* @return Resulting string value
*/
private static String capitalize(String value) {
if (StringUtils.isBlank(value)) {
return value;
}
String[] words = StringUtils.split(value, " -_");
return Stream.of(words)
.map(StringUtils::lowerCase)
.map(StringUtils::capitalize)
.collect(Collectors.joining(StringUtils.SPACE));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@

import java.lang.reflect.Member;

/**
* Represents a {@link MemberSource} that belongs to a {@code Class} that is used as an inclusion into other classes
* (such as a {@code FieldSet} referenced by a class that defines a Touch UI dialog)
*/
public interface EmbeddedMemberSource extends MemberSource {

/**
* Retrieves an optional {@code Member} reference pointing to a member of a foreign Java class that triggered
* Retrieves an optional {@link MemberSource} reference pointing to a member of a foreign Java class that triggered
* rendering of the class that contains the current member. This is useful for rendering containers such as
* {@code FieldSet}s.
* <p><i>Ex.: Class named "{@code Foo}" contains the field {@code private FooFieldset fooFieldset;}. Class named
Expand All @@ -26,7 +31,19 @@ public interface EmbeddedMemberSource extends MemberSource {
* it will use the values {@code reportingClass = Foo.class} and {@code upstreamMember = Foo#fooFieldset}. These
* values can be then used to form up a rendering context for the current field {@code bar}: in particular, to get
* embeddable settings</i></p>
* @return A nullable {@code MemberSource} reference
*/
MemberSource getUpstreamSource();

/**
* Retrieves an optional {@link Member} reference pointing to a member of a foreign Java class that triggered
* rendering of the class that contains the current member. This is useful for rendering containers such as
* {@code FieldSet}s.
* @return A nullable {@code Member} reference
* @see #getUpstreamSource()
*/
Member getUpstreamMember();
default Member getUpstreamMember() {
MemberSource upstreamSource = getUpstreamSource();
return upstreamSource != null ? getUpstreamSource().adaptTo(Member.class) : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.exadel.aem.toolkit.core;
package com.exadel.aem.toolkit;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

import com.exadel.aem.toolkit.api.annotations.meta.StringTransformationTest;
import com.exadel.aem.toolkit.core.injectors.ChildInjectorTest;
import com.exadel.aem.toolkit.core.injectors.ChildrenInjectorTest;
import com.exadel.aem.toolkit.core.injectors.EToolboxListInjectorTest;
Expand Down Expand Up @@ -45,6 +46,8 @@
*/
@RunWith(Suite.class)
@SuiteClasses({
StringTransformationTest.class,

ListHelperTest.class,
ListPageUtilTest.class,
ListResourceUtilTest.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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.exadel.aem.toolkit.api.annotations.meta;

import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Test;

public class StringTransformationTest {

private static final String SAMPLE0 = "LoReM";
private static final String SAMPLE1 = "L0rem IPSum! Do1or Sit Amet";
private static final String SAMPLE2 = "Lorem IPSum dolor-sit-Amet";
private static final String SAMPLE3 = "Lorem_IPSum__dolor_Sit_Amet";

@Test
public void shouldConvertToLowerCase() {
Assert.assertEquals("l0rem ipsum! do1or sit amet", StringTransformation.LOWERCASE.apply(SAMPLE1));
}

@Test
public void shouldConvertToUpperCase() {
Assert.assertEquals("L0REM IPSUM! DO1OR SIT AMET", StringTransformation.UPPERCASE.apply(SAMPLE1));
}

@Test
public void shouldConvertToCamelCase() {
Assert.assertEquals(StringUtils.EMPTY, StringTransformation.CAMELCASE.apply(StringUtils.EMPTY));
Assert.assertEquals("lorem", StringTransformation.CAMELCASE.apply(SAMPLE0));
Assert.assertEquals("l0remIpsum!Do1orSitAmet", StringTransformation.CAMELCASE.apply(SAMPLE1));
Assert.assertEquals("loremIpsumDolorSitAmet", StringTransformation.CAMELCASE.apply(SAMPLE2));
Assert.assertEquals("loremIpsumDolorSitAmet", StringTransformation.CAMELCASE.apply(SAMPLE3));
}

@Test
public void shouldCapitalize() {
Assert.assertEquals(StringUtils.EMPTY, StringTransformation.CAPITALIZE.apply(StringUtils.EMPTY));
Assert.assertEquals("Lorem", StringTransformation.CAPITALIZE.apply(SAMPLE0));
Assert.assertEquals("L0rem Ipsum! Do1or Sit Amet", StringTransformation.CAPITALIZE.apply(SAMPLE1));
Assert.assertEquals("Lorem Ipsum Dolor Sit Amet", StringTransformation.CAPITALIZE.apply(SAMPLE2));
Assert.assertEquals("Lorem Ipsum Dolor Sit Amet", StringTransformation.CAPITALIZE.apply(SAMPLE3));
}
}
47 changes: 44 additions & 3 deletions docs/content/dev-tools/component-management/customizing-toolkit.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,11 @@ Read more on debugging a Maven plugin e.g. [here](https://spin.atomicobject.com/

### Running integration tests

Starting from version _2.3.0_, the ToolKit supports integration tests powered by [Selenide](https://selenide.org). They are mainly for checking the browser-bound functions as well as checking the connection to a live AEM server and/or 3rd-party services on the Internet.
The ToolKit supports integration tests powered by [Selenide](https://selenide.org). They are mainly for checking the browser-bound functions and rely on the connection to a live AEM server and/or 3rd-party services on the Internet.

The integration tests are localed in the `it.tests` module. Important: unlike unit tests, integration tests are not run in frames of a "regular" build. To run them, you need to specify the dedicated Maven profile like the following:
```
mvn clean install -Pintegration
mvn clean test -Pintegration
```

As the integration tests start, a synthetic content package containing test data will be created and deployed to a live AEM instance (the one specified by the _aem.host_ and/or _aem.port_ properties). You can find the data in AEM under _/apps/etoolbox-authoring-kit-test_ and also in the same folders under _/conf_ and _/content_.
Expand All @@ -308,6 +308,47 @@ After the integration tests are run, the synthetic package and its content are r

A complete command line for running integration tests with different properties specified may look like the following:
```
mvn clean install -PautoInstallPackage -Pintegration -D"aem.host"=192.168.0.81 -D"aem.port"=8080 -D"aem.login"=siteadmin -D"aem.password"=MyPa$$w0rd -Dnouninstall=true
mvn clean test -Pintegration -D"aem.host"=192.168.0.81 -D"aem.port"=8080 -D"aem.login"=siteadmin -D"aem.password"=MyPa$$w0rd -Dnouninstall=true
```

### Doing regression testing

The ToolKit supports regression testing as well. Regression testing is done using an external AEM project. The aim is to make sure that a <newer> version of ToolKit (namely, the plugin) produces essentially the same XML markup as an <older> one. The <older> version is considered a "reference" and is used as a baseline for comparison.

To run regression tests, you need to specify the dedicated Maven profile like the following:
```
mvn test -Pregression "-Dproject=e:\projects\aem\my_project -Deak.version=${version}"
```
The focus here is the _-Dproject=..._ option. It is the command line to build the target project.

The ToolKit will navigate to the folder e:\projects\aem\my_project_ (and expectedly will find project's POM file in it). It will build the project (without doing unit tests and deployment) with the version of ToolKit that is specified in the project. It will collect the content packages created upon the build.

Then it will switch the version of ToolKit to the latest one and run the build for the second time. The content packages will be collected again. Then the "former" and the "newer" packages will be compared content-wise. If there are no significant disparities, the test is passed. Otherwise, the test is failed.

We assume that your project has a _Maven property_ that stores the version of ToolKit, and the dependencies within your project use that property (like `<dependency><groupId>com.exadel.etoolbox</groupId><artifactId>...</artifactId><version>${eak.version}</dependency>`). Name of the property does not matter. But you are expected
to mention it in the _-Dproject=..._ as shown above. The _${version}_ part is the placeholder that will be used by the regression code.

Within _-Dproject=..._, you can specify more property keys that are specific for your project if you need to. You can also specify the _-pl_ option to select particular modules for the build, or also some _-P_ keys to specify build profiles, etc. Do not forget to enclose _-Dproject=..._ in double quotes if there are spaces inside.

You can process more than one project at a time. Separate projects in the _-Dproject=..._ option with a semicolon.

We've said that the second part of the regression is building a target project with the current ToolKit version. If you however wish to test another version, specify it with the optional switch like `-Deak.version=1.2.3-SNAPSHOT`.`

Besides, you can override the _Maven_ executable used to build the target AEM project by specifying `-Dmaven.cmd=/path/to/mvn/or/a/cmd/file`. Optionally you can specify a local Maven repository address if it differs from _$HOME$/.m2_, with `-Dmaven.dir=/path/to/maven/repository`.

#### Filtering disparities

Certain disparities that may be found in regression testing are expected and, therefore, can be omitted. This is done with the _filters_ option. A filter is a JavaScript file composed per the EToolbox-Coconut documentation.

By default, the filters are looked for at the current project's path in the `eak.regression/filters` subdirectory. E.g., `/home/projects/my-aem-project/eak.regression/filters`.

This subdirectory can contain another subdir that refers to the "older" and "newer" ToolKit's versions delimited with _-to-_ like in the following example: `/home/projects/my-aem-project/eak.regression/filters/2.4.0-to-2.4.1-SNAPSHOT`. If such a subdirectory exists, the filters are borrowed from it. But if the there isn't a subdir matching the "older" and "newer" versions, the filters are borrowed from just `eak.regression/filters`.

The comparison is done inside a temp folder. By default, the folder is erased after the regression is done. If you want to keep it, specify `-Dnocleanup=true`.

#### Regression tips

Be sure to _build_ the ToolKit completely before doing the regression, or else include the _install_ phase into the regression Maven run.

Also, make sure that your target project(-s) are fully built with the newest changes, especially if you do regression with a selection of modules (via _-pl_). Otherwise there may occur a dependency resolution issue, or else some compilation errors.

Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ The easiest way to create a *MultiField* is with the `@Multiple` annotation. Jus

On the other hand, if you add `@Multiple` to a field marked with `@FieldSet`, a *composite* multifield will be created (much like the one you could have adding `@MultiField` annotation itself). Moreover, you can add `@Multiple` to a mere `@MultiField`-marked field and enjoy a sort of "multifield of multifields."

Please note, however, that `@Multiple` is primarily designed for easy, "quick give me a multifield out of my single widget" cases. For more complicated cases it lacks tweaking capabilities that `@MultiField` itself has.
<em>If you choose to use `@Multiple` together with `@FieldSet`, make sure that the `@DialogField` annotation is also present. In other `@FieldSet` use cases it is not necessary to add `@DialogField`.</em>

Please note that `@Multiple` is primarily designed for easy, "quick give me a multifield out of my single widget" cases. For more complicated cases it lacks tweaking capabilities that `@MultiField` itself has.

<hr/>
<h2 id="see-also" class="h3">See also</h2>
Expand Down
Loading

0 comments on commit f2de770

Please sign in to comment.