Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: adobe/aem-core-forms-components
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 4747ab859435ba077571df6b05db952c88c96f13
Choose a base ref
..
head repository: adobe/aem-core-forms-components
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a7b5e04f01dd2c9215a673d5fa2fc35946a1477e
Choose a head ref
Showing with 398 additions and 1,719 deletions.
  1. +26 −31 .github/workflows/exporter-validate-pr.yml
  2. +0 −20 ...f-core/src/main/java/com/adobe/cq/forms/core/components/internal/models/v1/form/PasswordImpl.java
  3. +1 −1 bundles/af-core/src/main/java/com/adobe/cq/forms/core/components/models/form/Password.java
  4. +20 −28 ...re/src/test/java/com/adobe/cq/forms/core/components/internal/models/v1/form/PasswordImplTest.java
  5. +26 −0 bundles/af-core/src/test/resources/form/password/exporter-password.json
  6. +1 −40 bundles/af-core/src/test/resources/form/password/test-content.json
  7. +0 −11 ...ontent/dam/formsanddocuments/core-components-it/samples/ruleeditor/navigate-in-panel/.content.xml
  8. +0 −23 .../dam/formsanddocuments/core-components-it/samples/ruleeditor/navigate-in-panel/basic/.content.xml
  9. +0 −23 .../dam/formsanddocuments/core-components-it/samples/ruleeditor/navigate-in-panel/blank/.content.xml
  10. +48 −94 ...c/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/basic/.content.xml
  11. +0 −3 ...nt/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/.content.xml
  12. +0 −611 ..._root/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/basic/.content.xml
  13. +0 −24 ..._root/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/blank/.content.xml
  14. +0 −14 it/core/pom.xml
  15. +3 −17 it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/HeadlessTransportHandler.java
  16. +23 −79 it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/OAuth2Client.java
  17. +7 −7 ui.frontend/package-lock.json
  18. +1 −1 ui.frontend/package.json
  19. +1 −17 ui.frontend/src/customFunctions.js
  20. +1 −26 ui.frontend/src/view/FormFileInputWidgetBase.js
  21. +1 −5 ui.tests/test-module/libs/commons/guideSelectors.js
  22. +0 −119 ui.tests/test-module/specs/ruleeditor/authoring/navigatePanel.authoring.cy.js
  23. 0 ui.tests/test-module/specs/ruleeditor/{runtime → }/customFunction.runtime.cy.js
  24. +5 −154 ui.tests/test-module/specs/ruleeditor/{authoring → }/ruleEditor.authoring.cy.js
  25. +234 −0 ui.tests/test-module/specs/ruleeditor/ruleEditor.runtime.cy.js
  26. +0 −107 ui.tests/test-module/specs/ruleeditor/runtime/navigatePanel.runtime.cy.js
  27. +0 −120 ui.tests/test-module/specs/ruleeditor/runtime/ruleEditorSanity.runtime.cy.js
  28. +0 −38 ui.tests/test-module/specs/ruleeditor/runtime/saveHandler.runtime.cy.js
  29. +0 −106 ui.tests/test-module/specs/ruleeditor/runtime/submitHandler.runtime.cy.js
  30. 0 ui.tests/test-module/specs/ruleeditor/{runtime → }/uichange.runtime.cy.js
57 changes: 26 additions & 31 deletions .github/workflows/exporter-validate-pr.yml
Original file line number Diff line number Diff line change
@@ -15,12 +15,12 @@ jobs:

- name: Fetch Base and Head References
run: |
git fetch origin ${{ github.base_ref }} --depth=1 || true
git fetch origin ${{ github.head_ref }} --depth=1 || true
git fetch origin ${{ github.base_ref }}:${{ github.base_ref }}
git fetch origin ${{ github.head_ref }}:${{ github.head_ref }}
- name: Validate JSON Changes
run: |
changed_files=$(git diff --name-only origin/${{ github.base_ref }} origin/${{ github.head_ref }} | grep 'exporter.*\.json$')
changed_files=$(git diff --name-only ${{ github.base_ref }} ${{ github.head_ref }} | grep 'exporter.*\.json$')
if [ -z "$changed_files" ]; then
echo "No exporter JSON files have changed."
@@ -29,39 +29,34 @@ jobs:
# Loop through each changed file
for file in $changed_files; do
# Check if the file exists in both branches
if git cat-file -e origin/${{ github.base_ref }}:$file 2>/dev/null && git cat-file -e origin/${{ github.head_ref }}:$file 2>/dev/null; then
# Fetch the base and head versions of the file
base_file=$(git show origin/${{ github.base_ref }}:$file)
head_file=$(git show origin/${{ github.head_ref }}:$file)
# Fetch the base and head versions of the file
base_file=$(git show ${{ github.base_ref }}:$file)
head_file=$(git show ${{ github.head_ref }}:$file)
# Compare the JSON keys
base_keys=$(echo "$base_file" | jq -r 'paths | map(tostring) | join(".")' | sed 's/\./\\./g')
head_keys=$(echo "$head_file" | jq -r 'paths | map(tostring) | join(".")' | sed 's/\./\\./g')
# Compare the JSON keys
base_keys=$(echo "$base_file" | jq -r 'paths | map(tostring) | join(".")')
head_keys=$(echo "$head_file" | jq -r 'paths | map(tostring) | join(".")')
# Check for removed keys
removed_keys=$(comm -23 <(echo "$base_keys" | sort) <(echo "$head_keys" | sort))
# Check for removed keys
removed_keys=$(comm -23 <(echo "$base_keys" | sort) <(echo "$head_keys" | sort))
if [ -n "$removed_keys" ]; then
echo "Backward incompatibility change detected in $file. The following keys were removed:"
echo "$removed_keys"
if [ -n "$removed_keys" ]; then
echo "Backward incompatibility change detected in $file. The following keys were removed:"
echo "$removed_keys"
exit 1
fi
# Check for changed values
for key in $base_keys; do
base_value=$(echo "$base_file" | jq -r ".$key")
head_value=$(echo "$head_file" | jq -r ".$key")
if [ "$base_value" != "$head_value" ]; then
echo "Backward incompatibility change detected in $file. The value of key '$key' was changed from '$base_value' to '$head_value'."
exit 1
fi
# Check for changed values
for key in $base_keys; do
base_value=$(echo "$base_file" | jq -r ".$key")
head_value=$(echo "$head_file" | jq -r ".$key")
if [ "$base_value" != "$head_value" ]; then
echo "Backward incompatibility change detected in $file. The value of key '$key' was changed from '$base_value' to '$head_value'."
exit 1
fi
done
else
echo "Skipping file $file as it exists in one branch but not the other."
fi
done
done
echo "All exporter JSON files have only additions. No backward incompatibility changes detected."
shell: bash
shell: bash
Original file line number Diff line number Diff line change
@@ -65,16 +65,6 @@ public Integer getMaxLength() {
return maxLength;
}

@Override
public Long getMinimum() {
return minimum;
}

@Override
public Long getMaximum() {
return maximum;
}

@Override
public String getFormat() {
return displayFormat;
@@ -85,16 +75,6 @@ public String getPattern() {
return pattern;
}

@Override
public Long getExclusiveMaximum() {
return (Long) exclusiveMaximumValue;
}

@Override
public Long getExclusiveMinimum() {
return (Long) exclusiveMinimumVaue;
}

@PostConstruct
private void initTextInput() {
exclusiveMaximumValue = ComponentUtils.getExclusiveValue(exclusiveMaximum, maximum, null);
Original file line number Diff line number Diff line change
@@ -24,6 +24,6 @@
* @since com.adobe.cq.forms.core.components.models.form 5.9.6
*/
@ConsumerType
public interface Password extends Field, StringConstraint, NumberConstraint {
public interface Password extends Field, StringConstraint {

}
Original file line number Diff line number Diff line change
@@ -16,6 +16,8 @@

package com.adobe.cq.forms.core.components.internal.models.v1.form;

import com.adobe.cq.forms.core.components.internal.form.FormConstants;
import com.adobe.cq.forms.core.components.models.form.TextInput;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -28,6 +30,7 @@
import com.adobe.cq.forms.core.context.FormsCoreComponentTestContext;
import io.wcm.testing.mock.aem.junit5.AemContext;
import io.wcm.testing.mock.aem.junit5.AemContextExtension;
import org.mockito.Mockito;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -40,9 +43,9 @@ public class PasswordImplTest {
private static final String PATH_PASSWORD_DATALAYER = CONTENT_ROOT + "/password-datalayer";
private static final String PATH_PASSWORD_CUSTOMIZED = CONTENT_ROOT + "/password-customized";

private static final String PATH_PASSWORD = CONTENT_ROOT + "/password";

private static final String PATH_PASSWORD_PATTERN = CONTENT_ROOT + "/password-pattern";
private static final String PATH_NUMBER_PASSWORD_EXCLUSIVE = CONTENT_ROOT + "/number-password-exclusive";
private static final String PATH_NUMBER_PASSWORD_INPUT = CONTENT_ROOT + "/number-password";

private final AemContext context = FormsCoreComponentTestContext.newAemContext();

@@ -51,6 +54,21 @@ void setUp() {
context.load().json(BASE + FormsCoreComponentTestContext.TEST_CONTENT_JSON, CONTENT_ROOT);
}

@Test
void testExportedType() {
Password password = Utils.getComponentUnderTest(PATH_PASSWORD_CUSTOMIZED, Password.class, context);
assertEquals(FormConstants.RT_FD_FORM_PASSWORD_V1, password.getExportedType());
TextInput textInputMock = Mockito.mock(TextInput.class);
Mockito.when(textInputMock.getExportedType()).thenCallRealMethod();
assertEquals("", textInputMock.getExportedType());
}

@Test
void testJSONExport() throws Exception {
Password password = Utils.getComponentUnderTest(PATH_PASSWORD_PATTERN, Password.class, context);
Utils.testJSONExport(password, Utils.getTestExporterJSONPath(BASE, PATH_PASSWORD));
}

@Test
void testFieldType() {
Password password = Utils.getComponentUnderTest(PATH_PASSWORD_CUSTOMIZED, Password.class, context);
@@ -140,32 +158,6 @@ void testMaxLength() {
assertEquals(10, password.getMaxLength().intValue());
}

@Test
void testGetExclusiveMinimum() {
Password password = Utils.getComponentUnderTest(PATH_NUMBER_PASSWORD_EXCLUSIVE, Password.class, context);
assertNull(password.getMinimum());
assertEquals(8L, password.getExclusiveMinimum().longValue());
}

@Test
void testGetExclusiveMaximum() {
Password password = Utils.getComponentUnderTest(PATH_NUMBER_PASSWORD_EXCLUSIVE, Password.class, context);
assertNull(password.getMaximum());
assertEquals(16L, password.getExclusiveMaximum().longValue());
}

@Test
void testGetMinimum() {
Password password = Utils.getComponentUnderTest(PATH_NUMBER_PASSWORD_INPUT, Password.class, context);
assertEquals(8, password.getMinimum().intValue());
}

@Test
void testGetMaximum() {
Password password = Utils.getComponentUnderTest(PATH_NUMBER_PASSWORD_INPUT, Password.class, context);
assertEquals(16, password.getMaximum().intValue());
}

@Test
void testGetDisplayFormat() throws Exception {
Password password = Utils.getComponentUnderTest(PATH_PASSWORD_CUSTOMIZED, Password.class, context);
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"id": "password-91417957c6",
"fieldType": "password",
"name": "password1732174214265",
"visible": true,
"type": "string",
"enabled": true,
"readOnly": false,
"pattern": "^(?=.*\\d.*\\d)[A-Za-z\\d!@]+$",
"label": {
"value": ""
},
"events": {
"custom:setProperty": [
"$event.payload"
]
},
"properties": {
":type": "forms-components-examples/components/form/password",
"fd:dor": {
"dorExclusion": false
},
"fd:path": "/content/password-pattern"
},
":type": "nt:unstructured"
}
Original file line number Diff line number Diff line change
@@ -37,25 +37,6 @@
"visible": true
}
},
"number-password" : {
"jcr:primaryType": "nt:unstructured",
"sling:resourceType" : "core/fd/components/form/textinput/v1/textinput",
"name" : "abc",
"type" : "password",
"jcr:title" : "def",
"hideTitle" : false,
"description" : "dummy",
"visible" : false,
"assistPriority" : "custom",
"dataRef" : "a.b",
"custom" : "Custom screen reader text",
"typeMessage" : "incorrect type",
"tooltip": "test-short-description",
"fieldType": "text-input",
"maximum" : 16,
"minimum" : 8,
"default" : 150
},
"password-pattern" : {
"id": "password-91417957c6",
"fieldType": "password",
@@ -85,27 +66,7 @@
},
":type": "forms-components-examples/components/form/password"
},
"number-password-exclusive" : {
"jcr:primaryType": "nt:unstructured",
"sling:resourceType" : "core/fd/components/form/password/v1/password",
"name" : "abc",
"type" : "password",
"jcr:title" : "def",
"hideTitle" : false,
"description" : "dummy",
"visible" : false,
"assistPriority" : "custom",
"dataRef" : "a.b",
"custom" : "Custom screen reader text",
"typeMessage" : "incorrect type",
"tooltip": "test-short-description",
"fieldType": "text-input",
"maximum" : 16,
"minimum" : 8,
"exclusiveMinimum" : true,
"exclusiveMaximum" : true,
"default" : 150
},

"password-format" : {
"jcr:primaryType": "nt:unstructured",
"sling:resourceType" : "core/fd/components/form/password/v1/password",

This file was deleted.

This file was deleted.

This file was deleted.

Large diffs are not rendered by default.

This file was deleted.

This file was deleted.

This file was deleted.

14 changes: 0 additions & 14 deletions it/core/pom.xml
Original file line number Diff line number Diff line change
@@ -86,7 +86,6 @@
<Import-Package>
javax.annotation;version=0.0.0,
com.adobe.cq.forms.core.components.models.form;version="[1.0.0,10.0.0)",
io.jsonwebtoken;resolution:=optional;version="[0.0.0,1.0.0)",
*
</Import-Package>
</instructions>
@@ -163,19 +162,6 @@ Import-Package: javax.annotation;version=0.0.0,*
<artifactId>core-forms-components-af-core</artifactId>
<version>3.0.70</version>
</dependency>
<!-- Json web token dependencies for oauth2 flow -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>provided</scope>
</dependency>
</dependencies>

<!-- ====================================================================== -->
Original file line number Diff line number Diff line change
@@ -170,28 +170,14 @@ public ReplicationResult deliver(TransportContext transportContext, ReplicationT
// todo: publish this form model json to the external system
LOG.info("[HeadlessTransportHandler] Form Model JSON: {}", formModelJson);
/**
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setDefaultMaxPerRoute(100);
connectionManager.setMaxTotal(100);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(30000)
.setSocketTimeout(30000)
.setConnectionRequestTimeout(30000)
.build())
.build();
OAuth2Client oauth2Client = new OAuth2Client(
"https://example.com/oauth2/token",
"your_client_id",
"your_private_key",
"your_certificate_thumbprint",
"your_resource_uri",
"your_client_secret",
"https://example.com/api/publish",
httpClient
);
oauth2Client.publishOrDeleteFormModelJson(formModelJson, "https://example.com/api/publish", HttpPost::new);
oauth2Client.publishOrDeleteFormModelJson(formModelJson, requestSupplier);
**/
} else {
LOG.info("[HeadlessTransportHandler] No adaptive form container found for resource {}. Skipping", resource.getPath());
Original file line number Diff line number Diff line change
@@ -15,14 +15,6 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package com.adobe.cq.forms.core.components.it.service;

import java.io.IOException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
@@ -35,48 +27,34 @@
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;

// these bundles are not present on local cloud ready sdk
// to make this work, you have to download/install 0.11.2 from here, https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api/0.11.2
// making the import optional
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

/**
* Uses the OAuth2 Client Credentials flow with a signed JWT (client assertion) for certificate-based authentication.
* The token request payload includes grant_type, client_id, client_assertion_type, client_assertion, and resource.
* The signed JWT includes claims such as iss, sub, aud, jti, iat, and exp.
* The JWT is signed using a private key and includes a certificate thumbprint in the header.
*
* Uses a signed JWT (client assertion) with a private key and certificate thumbprint.
* Provides enhanced security by using certificate-based authentication and a signed JWT.
*/

public class OAuth2Client {
private static final Logger LOG = LoggerFactory.getLogger(OAuth2Client.class);

private final String tokenEndpoint;
private final String clientId;
private final String privateKey;
private final String certificateThumbprint;
private final String resource;
private final String clientSecret;
private final String apiEndpoint;
private final CloseableHttpClient httpClient;

private String accessToken;
private long tokenExpirationTime;
private final ReentrantLock lock = new ReentrantLock();

public OAuth2Client(String tokenEndpoint, String clientId, String privateKey, String certificateThumbprint, String resource, CloseableHttpClient httpClient) {
public OAuth2Client(String tokenEndpoint, String clientId, String clientSecret, String apiEndpoint, CloseableHttpClient httpClient) {
this.tokenEndpoint = tokenEndpoint;
this.clientId = clientId;
this.privateKey = privateKey;
this.certificateThumbprint = certificateThumbprint;
this.resource = resource;
this.clientSecret = clientSecret;
this.apiEndpoint = apiEndpoint;
this.httpClient = httpClient;
}

public void publishOrDeleteFormModelJson(String formModelJson, String apiEndpoint, Function<String, HttpRequestBase> requestSupplier) throws IOException {
public void publishOrDeleteFormModelJson(String formModelJson, Function<String, HttpRequestBase> requestSupplier) throws IOException {
String token = getValidToken();
HttpRequestBase request = requestSupplier.apply(apiEndpoint);
request.setHeader("Authorization", "Bearer " + token);
@@ -116,12 +94,7 @@ private String getValidToken() throws IOException {
private String fetchOAuth2Token() throws IOException {
HttpPost post = new HttpPost(tokenEndpoint);
post.setHeader("Content-Type", "application/x-www-form-urlencoded");

String clientAssertion = generateClientAssertion();

String payload = "grant_type=client_credentials&client_id=" + clientId + "&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=" + clientAssertion + "&resource=" + resource;

post.setEntity(new StringEntity(payload));
post.setEntity(new StringEntity("grant_type=client_credentials&client_id=" + clientId + "&client_secret=" + clientSecret));

try (CloseableHttpResponse response = httpClient.execute(post)) {
if (response.getStatusLine().getStatusCode() == 200) {
@@ -134,7 +107,18 @@ private String fetchOAuth2Token() throws IOException {
}

private String refreshOAuth2Token() throws IOException {
return fetchOAuth2Token(); // Assuming the same flow for refresh token
HttpPost post = new HttpPost(tokenEndpoint);
post.setHeader("Content-Type", "application/x-www-form-urlencoded");
post.setEntity(new StringEntity("grant_type=refresh_token&refresh_token=your_refresh_token&client_id=" + clientId + "&client_secret=" + clientSecret));

try (CloseableHttpResponse response = httpClient.execute(post)) {
if (response.getStatusLine().getStatusCode() == 200) {
String responseBody = EntityUtils.toString(response.getEntity());
return parseToken(responseBody);
} else {
throw new NotOk(response.getStatusLine().getStatusCode());
}
}
}

private String parseToken(String responseBody) {
@@ -146,49 +130,9 @@ private String parseToken(String responseBody) {
}
}

private String generateClientAssertion() {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);

// Create the JWT claims
JsonObject claims = Json.createObjectBuilder()
.add("iss", clientId)
.add("sub", clientId)
.add("aud", tokenEndpoint)
.add("jti", java.util.UUID.randomUUID().toString())
.add("iat", nowMillis / 1000)
.add("exp", (nowMillis / 1000) + 300) // 5 minutes expiration
.build();

// Create the JWT header
JsonObject header = Json.createObjectBuilder()
.add("alg", "RS256")
.add("x5t", certificateThumbprint)
.build();

// Sign the JWT
return Jwts.builder()
.setHeaderParam("x5t", certificateThumbprint)
.setClaims(claims)
.setHeaderParam("typ", "JWT")
.signWith(SignatureAlgorithm.RS256, getPrivateKey())
.compact();
}

private PrivateKey getPrivateKey() {
try {
byte[] keyBytes = Base64.getDecoder().decode(privateKey);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
} catch (Exception e) {
throw new RuntimeException("Failed to load private key", e);
}
}

private static class NotOk extends IOException {
NotOk(int status) {
super("status code = " + status);
}
}
}
}
14 changes: 7 additions & 7 deletions ui.frontend/package-lock.json
2 changes: 1 addition & 1 deletion ui.frontend/package.json
Original file line number Diff line number Diff line change
@@ -25,6 +25,6 @@
"dependencies": {
"@aemforms/af-core": "^0.22.111",
"@aemforms/af-formatters": "^0.22.109",
"@aemforms/af-custom-functions": "1.0.12"
"@aemforms/af-custom-functions": "1.0.10"
}
}
18 changes: 1 addition & 17 deletions ui.frontend/src/customFunctions.js
Original file line number Diff line number Diff line change
@@ -98,21 +98,5 @@ export const customFunctions = {
* @param {object} globals - An object containing read-only form instance, read-only target field instance and methods for form modifications.
* @returns {string} - The captcha token.
*/
fetchCaptchaToken: cf.fetchCaptchaToken,

/**
* Converts a date to the number of days since the Unix epoch (1970-01-01).
*
* If the input date is a number, it is assumed to represent the number of days since the epoch,
* including both integer and decimal parts. In this case, only the integer part is returned as the number of days.
*
* @param {string|Date|number} date - The date to convert.
* Can be:
* - An ISO string (yyyy-mm-dd)
* - A Date object
* - A number representing the days since the epoch, where the integer part is the number of days and the decimal part is the fraction of the day
*
* @returns {number} - The number of days since the Unix epoch
*/
dateToDaysSinceEpoch: cf.dateToDaysSinceEpoch
fetchCaptchaToken: cf.fetchCaptchaToken
};
27 changes: 1 addition & 26 deletions ui.frontend/src/view/FormFileInputWidgetBase.js
Original file line number Diff line number Diff line change
@@ -41,32 +41,7 @@ class FormFileInputWidgetBase {
"dll": "application/x-msdownload",
"exe": "application/x-msdownload",
"msi": "application/x-msdownload",
"msg": "application/vnd.ms-outlook",
"dwg": "image/vnd.dwg",
"jxr": "image/vnd.ms-photo",
"psd": "image/vnd.adobe.photoshop",
"ico": "image/vnd.microsoft.icon",
"cab": "application/vnd.ms-cab-compressed",
"deb": "application/vnd.debian.binary-package",
"sqlite": "application/vnd.sqlite3",
"inf2": "image/vnd.cns.inf2",
"djv": "image/vnd.djvu",
"djvu": "image/vnd.djvu",
"dxf": "image/vnd.dxf",
"fbs": "image/vnd.fastbidsheet",
"fpx": "image/vnd.fpx",
"fst": "image/vnd.fst",
"mmr": "image/vnd.fujixerox.edmics-mmr",
"rlc": "image/vnd.fujixerox.edmics-rlc",
"pgb": "image/vnd.globalgraphics.pgb",
"mix": "image/vnd.mix",
"mdi": "image/vnd.ms-modi",
"npx": "image/vnd.net-fpx",
"radiance": "image/vnd.radiance",
"sealed.png": "image/vnd.sealed.png",
"softseal.gif": "image/vnd.sealedmedia.softseal.gif",
"softseal.jpg": "image/vnd.sealedmedia.softseal.jpg",
"svf": "image/vnd.svf"
"msg": "application/vnd.ms-outlook"
}
initialFileValueFileNameMap;

6 changes: 1 addition & 5 deletions ui.tests/test-module/libs/commons/guideSelectors.js
Original file line number Diff line number Diff line change
@@ -167,7 +167,6 @@ var selectors = {

ruleEditor : {
action : {
configure: "#EditableToolbar [data-action='CONFIGURE']",
editRule : "#EditableToolbar [data-action='editexpression']",
createRuleButton : "#create-rule-button",
saveRule : ".exp-Save-Button",
@@ -182,19 +181,16 @@ var selectors = {
EVENT_AND_COMPARISON_OPERATOR : ".choice-model.u-coral-clearFix.EVENT_AND_COMPARISON_OPERATOR",
PRIMITIVE_EXPRESSION : ".choice-model.u-coral-clearFix.PRIMITIVE_EXPRESSION.choice-model-inline",
BLOCK_STATEMENT : ".choice-model.u-coral-clearFix.BLOCK_STATEMENT",
PARAMETER : ".Parameters .choice-model.u-coral-clearFix.EXPRESSION",
STRING_LITERAL : ".choice-model.u-coral-clearFix .STRING_LITERAL",
PARAMETER : ".Parameters .choice-model.u-coral-clearFix.EXPRESSION"
},
ruleSummary : {
CREATED_RULE: "#rule-summary table[handle='table'] tr[title='Button - Click']",
DATE_PICKER_RULE: "#rule-summary table[handle='table'] tr[title='Date Input - Validate']",
SUBMISSION_SUCCESS_RULE: "#rule-summary table[handle='table'] tr[title='FORM - Successful Submission']",
SUBMISSION_FAILURE_RULE: "#rule-summary table[handle='table'] tr[title='FORM - Error in Submission']",
CUSTOM_SUBMIT_FORM_RULE: "#rule-summary table[handle='table'] tr[title='Submit - Click']",
},
operator : {
CONTAINS : "coral-selectlist [value='CONTAINS']",
EQUALS_TO : "coral-selectlist [value='EQUALS_TO']",
HIDE : "coral-selectlist [value='HIDE_STATEMENT']",
SAVE_FORM: "coral-selectlist [value='SAVE_FORM']",
FUNCTION_CALL : "coral-selectlist [value='FUNCTION_CALL']",

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const commons = require('../../../libs/commons/commons'),
sitesSelectors = require('../../../libs/commons/sitesSelectors'),
formsSelectors = require('../../../libs/commons/guideSelectors'),
afConstants = require('../../../libs/commons/formsConstants');
const commons = require('../../libs/commons/commons'),
sitesSelectors = require('../../libs/commons/sitesSelectors'),
formsSelectors = require('../../libs/commons/guideSelectors'),
afConstants = require('../../libs/commons/formsConstants');

describe('Rule editor authoring sanity for core-components',function(){
let toggle_array = [];
@@ -76,113 +76,6 @@ describe('Rule editor authoring sanity for core-components',function(){
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.closeRuleEditor).click();
}

const createRuleToHideTextInputOnEqualityOperator = function() {
// Edit rule option not existing on button toolbar
cy.get(formsSelectors.ruleEditor.action.editRule).should("exist");
cy.initializeEventHandlerOnChannel("af-rule-editor-initialized").as("isRuleEditorInitialized");
cy.get(formsSelectors.ruleEditor.action.editRule).click();

// click on create option from rule editor header
cy.get("@isRuleEditorInitialized").its('done').should('equal', true);
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.createRuleButton).should("be.visible").click();

cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.sideToggleButton + ":first").click();

cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.choiceModels.STATEMENT + " .child-choice-name").should("exist");
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.choiceModels.STATEMENT + " .child-choice-name").click();

cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.choiceModels.STATEMENT + " .expeditor-customoverlay.is-open coral-selectlist-item[value='EVENT_SCRIPTS']")
.click({force: true});

// select the component for which rule is to written i.e. Button here
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.choiceModels.EVENT_AND_COMPARISON_OPERATOR + " .choice-view-default").should("exist");
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.choiceModels.EVENT_AND_COMPARISON_OPERATOR + " .choice-view-default").click();

// EQUALS option not existing in 'OPERATIONS' dropdown
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.operator.EQUALS_TO).should("exist");
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.operator.EQUALS_TO).click();

cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.choiceModels.STRING_LITERAL).type('abc');
cy.getRuleEditorIframe().find(".delete-else-button").click();

// check and click on dropdown to view the actions available
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.choiceModels.BLOCK_STATEMENT + " .choice-view-default").should("exist");
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.choiceModels.BLOCK_STATEMENT + " .choice-view-default").click();

// select HIDE action from dropdown
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.operator.HIDE).should("exist");
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.operator.HIDE).click();

cy.getRuleEditorIframe().find(".terminal-view.AFCOMPONENT.VARIABLE").should("be.visible");
cy.getRuleEditorIframe().find(".terminal-view.AFCOMPONENT.VARIABLE").click();

cy.getRuleEditorIframe().find(".terminal-view.AFCOMPONENT.VARIABLE coral-overlay.is-open .expression-selectlist coral-selectlist-item:first").click({force: true});

cy.intercept('POST', /content\/forms\/af\/core-components-it\/samples\/ruleeditor\/blank.*/).as('ruleEditorRequest');

cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.saveRule).should("exist");
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.saveRule).click();

cy.wait('@ruleEditorRequest').then((interception) => {
expect(interception.response.statusCode).to.equal(201);
const submittedData = Object.fromEntries(new URLSearchParams(interception.request.body));
expect(submittedData[":content"]).contains("\"fd:events\":{\"change\":[\"if(contains($event.payload.changes[].propertyName, 'value'), if($field.$value == 'abc', {visible : false()}, {}), {})\"]}");
});

// check and close rule editor
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.closeRuleEditor).should("exist");
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.closeRuleEditor).click();
}

const createRuleToValidateDate = function() {
// Edit rule option not existing on button toolbar
cy.get(formsSelectors.ruleEditor.action.editRule).should("exist");
cy.initializeEventHandlerOnChannel("af-rule-editor-initialized").as("isRuleEditorInitialized");
cy.get(formsSelectors.ruleEditor.action.editRule).click();

// click on create option from rule editor header
cy.get("@isRuleEditorInitialized").its('done').should('equal', true);
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.createRuleButton).should("be.visible").click();

cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.sideToggleButton + ":first").click();

cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.choiceModels.STATEMENT + " .child-choice-name").should("exist");
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.choiceModels.STATEMENT + " .child-choice-name").click();

cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.choiceModels.STATEMENT + " .expeditor-customoverlay.is-open coral-selectlist-item[value='VALIDATE_EXPRESSION']")
.click({force: true});

// select the component for which rule is to written i.e. Button here
cy.getRuleEditorIframe().find(".COMPARISON_EXPRESSION .sequence-view-cell .EXPRESSION").first().click();
cy.getRuleEditorIframe().find(".COMPARISON_EXPRESSION .sequence-view-cell .EXPRESSION").first().find("coral-selectlist-item[title='Date Input']:first").click();

cy.getRuleEditorIframe().find(".COMPARISON_EXPRESSION .sequence-view-cell .OPERATOR").click();
cy.getRuleEditorIframe().find("coral-selectlist-item[value='IS_BEFORE']").click();

cy.getRuleEditorIframe().find(".COMPARISON_EXPRESSION .sequence-view-cell .EXPRESSION").last().click();
cy.getRuleEditorIframe().find(".COMPARISON_EXPRESSION .sequence-view-cell .EXPRESSION").last().find(".selectlist-header").click();
cy.getRuleEditorIframe().find("coral-selectlist-item[value='FUNCTION_CALL']").click();
cy.getRuleEditorIframe().find(".COMPARISON_EXPRESSION .sequence-view-cell .EXPRESSION").last().click();
cy.getRuleEditorIframe().find(".COMPARISON_EXPRESSION .sequence-view-cell .EXPRESSION").last().find("coral-selectlist-item[value='today']").click();

cy.intercept('POST', /content\/forms\/af\/core-components-it\/samples\/ruleeditor\/blank.*/).as('ruleEditorRequest');

cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.saveRule).should("exist");
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.saveRule).click();
cy.wait('@ruleEditorRequest').then((interception) => {
expect(interception.response.statusCode).to.equal(201);
const submittedData = Object.fromEntries(new URLSearchParams(interception.request.body));
expect(submittedData[":content"]).contains("dateToDaysSinceEpoch($field.$value)<dateToDaysSinceEpoch(today())");
});

// check if rule is created
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.ruleSummary.DATE_PICKER_RULE).should("exist");

// check and close rule editor
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.closeRuleEditor).should("exist");
cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.closeRuleEditor).click();
}

const createRuleToSaveFormOnButtonClick = function() {
// Edit rule option not existing on button toolbar
cy.get(formsSelectors.ruleEditor.action.editRule).should("exist");
@@ -334,11 +227,8 @@ describe('Rule editor authoring sanity for core-components',function(){
saveFormContainerPath = saveFormPath + afConstants.FORM_EDITOR_FORM_CONTAINER_SUFFIX,
textinputEditPath = formContainerPath + "/" + afConstants.components.forms.resourceType.formtextinput.split("/").pop(),
buttonEditPath = formContainerPath + "/" + afConstants.components.forms.resourceType.formbutton.split("/").pop(),
datePickerEditPath = formContainerPath + "/" + afConstants.components.forms.resourceType.datepicker.split("/").pop(),
saveButtonEditPath = saveFormContainerPath + "/" + afConstants.components.forms.resourceType.formbutton.split("/").pop(),
buttonEditPathSelector = "[data-path='" + buttonEditPath + "']",
textinputEditPathSelector = "[data-path='" + textinputEditPath + "']",
datePickerEditPathSelector = "[data-path='" + datePickerEditPath + "']",
saveButtonEditPathSelector = "[data-path='" + saveButtonEditPath + "']",
submitFormButtonEditPath = submitFormContainerPath + "/" + afConstants.components.forms.resourceType.submitButton.split("/").pop(),
submitFormButtonEditPathSelector = "[data-path='" + submitFormButtonEditPath + "']";
@@ -392,7 +282,7 @@ describe('Rule editor authoring sanity for core-components',function(){
* 12 Close rule editor
* 13 Check if button is visible
*/
it('should add rule on button to hide a text box', function () {
it('should add rule on button to disable a text box', function () {
cy.openAuthoring(formPath);
cy.selectLayer("Edit");
cy.get(sitesSelectors.overlays.overlay.component + "[data-path='" + formContainerPath + "/*']").should("exist");
@@ -411,45 +301,6 @@ describe('Rule editor authoring sanity for core-components',function(){
cy.deleteComponentByPath(buttonEditPath);
})

if (cy.af.isLatestAddon()) {
it('should add validation rule on date fields', function () {
cy.openAuthoring(formPath);
cy.selectLayer("Edit");
cy.get(sitesSelectors.overlays.overlay.component + "[data-path='" + formContainerPath + "/*']").should("exist");

cy.insertComponent(sitesSelectors.overlays.overlay.component + "[data-path='" + formContainerPath + "/*']",
"Adaptive Form Date Picker", afConstants.components.forms.resourceType.datepicker);
cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + datePickerEditPathSelector);

createRuleToValidateDate();
cy.get(sitesSelectors.overlays.overlay.component + datePickerEditPathSelector).should("exist");

cy.selectLayer("Edit");
cy.deleteComponentByPath(datePickerEditPath);
})
}

it('should add rule on texbox equality operator to hide a text box', function () {
cy.openAuthoring(formPath);
cy.selectLayer("Edit");
cy.get(sitesSelectors.overlays.overlay.component + "[data-path='" + formContainerPath + "/*']").should("exist");

cy.insertComponent(sitesSelectors.overlays.overlay.component + "[data-path='" + formContainerPath + "/*']",
"Adaptive Form Text Box", afConstants.components.forms.resourceType.formtextinput);
// cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + textinputEditPathSelector);
// cy.get(formsSelectors.ruleEditor.action.configure).should("exist");
// cy.get(formsSelectors.ruleEditor.action.configure).click();
// cy.get(".cmp-adaptiveform-base__editdialogbasic [name='./name']").clear().type("textinput");
// cy.get(".cq-dialog-actions.cq-dialog-submit").click();

cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + textinputEditPathSelector);

createRuleToHideTextInputOnEqualityOperator();
cy.get(sitesSelectors.overlays.overlay.component + textinputEditPathSelector).should("exist");

cy.selectLayer("Edit");
cy.deleteComponentByPath(textinputEditPath);
})

it('should add submission handler rules on form', function () {
if (cy.af.isLatestAddon() && toggle_array.includes("FT_FORMS-13209")) {
234 changes: 234 additions & 0 deletions ui.tests/test-module/specs/ruleeditor/ruleEditor.runtime.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
describe('Rule editor runtime sanity for core-components',function(){
const formPath = "/content/forms/af/core-components-it/samples/ruleeditor/basic.html";
let formContainer = null;
let toggle_array = [];

before(() => {
cy.fetchFeatureToggles().then((response) => {
if (response.status === 200) {
toggle_array = response.body.enabled;
}
});
});

/**
* initialization of form container before every test
* */
beforeEach(() => {
cy.previewForm(formPath).then(p => {
formContainer = p;
});
});

if (cy.af.isLatestAddon()) {
it("should have merged custom function list registered in FunctionRuntime from both clientlibs", () => {
expect(formContainer, "formcontainer is initialized").to.not.be.null;
let func;
cy.window().then(win => {
func = win.FormView.FunctionRuntime.customFunctions.testFunction1; // from corecomponent.it.customfunction
expect(func).to.not.be.null;
expect(func).to.not.be.undefined;

func = win.FormView.FunctionRuntime.customFunctions.testSubmitFormPreprocessor; // from corecomponent.it.customfunction
expect(func).to.not.be.null;
expect(func).to.not.be.undefined;

func = win.FormView.FunctionRuntime.customFunctions.testSetProperty; // from corecomponent.it.customfunction2
expect(func).to.not.be.null;
expect(func).to.not.be.undefined;
})
})
}

/**
* Runtime ruleSanity for button to change label of textbox
* [when button is clicked the textbox field label should change using custom function]
*/
it("should change textinput label on button click", () => {
if (cy.af.isLatestAddon() && toggle_array.includes("FT_FORMS-11541")) {
expect(formContainer, "formcontainer is initialized").to.not.be.null;
cy.get(`.cmp-adaptiveform-button__widget`).click()
const [textbox1, textBox1FieldView] = Object.entries(formContainer._fields)[0];
cy.get(`#${textbox1}`).find("div > label").should('have.text', "Changed Label")
}
})
})

describe("Rule editor submission handler runtime", () => {
let toggle_array = [];

before(() => {
cy.fetchFeatureToggles().then((response) => {
if (response.status === 200) {
toggle_array = response.body.enabled;
}
});
});
const submitSuccessHardcodedHandler = "content/forms/af/core-components-it/samples/ruleeditor/submit/submitsuccesshardcodedhandler.html"
const submitErrorHardcodedHandler = "content/forms/af/core-components-it/samples/ruleeditor/submit/submiterrorhardcodedhandler.html"
const submitDefaultSuccessHandler = "content/forms/af/core-components-it/samples/ruleeditor/submit/submitdefaultsuccesshandler.html"
const submitDefaultErrorHandler = "content/forms/af/core-components-it/samples/ruleeditor/submit/submitdefaulterrorhandler.html"
const submitCustomSuccessHandler = "content/forms/af/core-components-it/samples/ruleeditor/submit/submitcustomsuccesshandler.html"
const submitCustomErrorHandler = "content/forms/af/core-components-it/samples/ruleeditor/submit/submitcustomerrorhandler.html"
const bemBlock = 'cmp-button'
const IS = "adaptiveFormButton"
const selectors = {
submit: `[data-cmp-is="${IS}"]`
}

let formContainer = null;

it("Hardcoded submitSuccess handler should handle successful form submission", () => {
cy.previewForm(submitSuccessHardcodedHandler);
cy.get(`.cmp-adaptiveform-button__widget`).click().then(x => {
cy.get('body').should('contain', "Thank you for submitting the form.\n")
});
});

it("Default submitSuccess handler should handle successful form submission", () => {
if (cy.af.isLatestAddon() && toggle_array.includes("FT_FORMS-13209")) {
cy.previewForm(submitDefaultSuccessHandler);
cy.get(`.cmp-adaptiveform-button__widget`).click().then(x => {
cy.get('body').should('contain', "Thank you for submitting the form.\n")
});
}
});

it("Custom submitSuccess handler should handle successful form submission", () => {
if (cy.af.isLatestAddon() && toggle_array.includes("FT_FORMS-13209")) {
cy.previewForm(submitCustomSuccessHandler);
cy.get(`.cmp-adaptiveform-button__widget`).click().then(x => {
cy.get('.modal .success-message').should('contain', "Thank you for submitting the form.")
});
}
});

it("Hardcoded submitError handler should handle form submission error", () => {
cy.previewForm(submitErrorHardcodedHandler);

cy.window().then(win => {
let alertFired = false;

// Stub the window alert to capture the alert message and set alertFired to true
cy.stub(win, 'alert').callsFake((message) => {
expect(message).to.equal('Encountered an internal error while submitting the form.');
alertFired = true;
});

// Click the submit button
cy.get('.cmp-adaptiveform-button__widget').click().then(() => {
// Use cy.wrap to ensure Cypress waits for the promise to resolve
cy.wrap(null).should(() => {
expect(alertFired).to.be.true;
});
});
});
});

it("Default submitError handler should handle form submission error", () => {
if (cy.af.isLatestAddon() && toggle_array.includes("FT_FORMS-13209")) {
cy.previewForm(submitDefaultErrorHandler);

cy.window().then(win => {
let alertFired = false;

// Stub the window alert to capture the alert message and set alertFired to true
cy.stub(win, 'alert').callsFake((message) => {
expect(message).to.equal('Form submission failed!');
alertFired = true;
});

// Click the submit button
cy.get('.cmp-adaptiveform-button__widget').click().then(() => {
// Use cy.wrap to ensure Cypress waits for the promise to resolve
cy.wrap(null).should(() => {
expect(alertFired).to.be.true;
});
});
});
}
});

it("Custom submitError handler should handle form submission error", () => {
if (cy.af.isLatestAddon() && toggle_array.includes("FT_FORMS-13209")) {
cy.previewForm(submitCustomErrorHandler);
let alertFired = false;
cy.get(`.cmp-adaptiveform-button__widget`).click().then(x => {
cy.get('.modal .error-message').should('contain', "Custom Form submission failed!")
});
}
});
})

describe("Rule editor save handler runtime", () => {

let toggle_array = [];

before(() => {
cy.fetchFeatureToggles().then((response) => {
if (response.status === 200) {
toggle_array = response.body.enabled;
}
});
});

const saveRunTime = "content/forms/af/core-components-it/samples/ruleeditor/save/saveruntime.html"

it("should save formData on button click", () => {
if (toggle_array.includes("FT_FORMS-11581")) {

const saveApiResponse = {
'draftId': 'ABC'
};
// Rule when button is clicked then save call should trigger
cy.intercept('POST' , '**/adobe/forms/af/save/*', saveApiResponse).as('afSave');

cy.previewForm(saveRunTime);

cy.get(`.cmp-adaptiveform-button__widget`).click();

cy.wait('@afSave').then(({request, response}) => {
// Check the request payload
expect(request.body).to.be.not.null;

expect(response.statusCode).to.equal(200);
expect(response.body).to.be.not.null;
expect(response.body.draftId).to.equal('ABC');
});
}
})
})


describe('Rule editor properties on form initialize test', () => {
const pagePath = "content/forms/af/core-components-it/samples/ruleeditor/set_property_test.html"
let formContainer = null;

before(() => {
cy.previewForm(pagePath).then(p => {
formContainer = p;
})
});

it("Check properties are properly set on form initialize", () => {
const [checkBoxId, checkBoxFieldView] = Object.entries(formContainer._fields)[0]
const [fileInputId, fileInputView] = Object.entries(formContainer._fields)[1]

const checkProperties = (id, bemBlock, labelSelector, expectedLabel, expectedDescription) => {
cy.get(`#${id}`).invoke('attr', 'data-cmp-required').should('eq', 'true');
cy.get(`#${id} ${labelSelector}`)
.should('have.text', expectedLabel);
// cy.get(`#${id}__longdescription p`)
// .should('have.text', expectedDescription);

cy.get(`#${id}`).find(`.${bemBlock}__questionmark`).click();
// long description should be shown
cy.get(`#${id}`).find(`.${bemBlock}__longdescription`).invoke('attr', 'data-cmp-visible')
.should('not.exist');
cy.get(`#${id}`).find(`.${bemBlock}__longdescription`)
.should('contain.text', expectedDescription);
}
checkProperties(checkBoxId, 'cmp-adaptiveform-checkboxgroup', '.cmp-adaptiveform-checkboxgroup__label', 'Updated CheckBox', 'This is a long description of checkboxgroup');
checkProperties(fileInputId, 'cmp-adaptiveform-fileinput', '.cmp-adaptiveform-fileinput__label', 'Updated File Input Label', 'File Input Description');
});
})

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.