diff --git a/it/config/src/main/content/jcr_root/apps/system/config/com.adobe.granite.toggle.impl.dev.DynamicToggleProviderImpl.cfg.json b/it/config/src/main/content/jcr_root/apps/system/config/com.adobe.granite.toggle.impl.dev.DynamicToggleProviderImpl.cfg.json index 682f52b610..8842ce8f0c 100644 --- a/it/config/src/main/content/jcr_root/apps/system/config/com.adobe.granite.toggle.impl.dev.DynamicToggleProviderImpl.cfg.json +++ b/it/config/src/main/content/jcr_root/apps/system/config/com.adobe.granite.toggle.impl.dev.DynamicToggleProviderImpl.cfg.json @@ -20,6 +20,7 @@ "FT_FORMS-14545", "FT_SITES-19631", "FT_FORMS-14255", - "FT_FORMS-14068" + "FT_FORMS-14068", + "FT_FORMS-16351" ] } diff --git a/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/ruleeditor/navigate-in-panel/.content.xml b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/ruleeditor/navigate-in-panel/.content.xml new file mode 100644 index 0000000000..290e53b989 --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/ruleeditor/navigate-in-panel/.content.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/ruleeditor/navigate-in-panel/basic/.content.xml b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/ruleeditor/navigate-in-panel/basic/.content.xml new file mode 100644 index 0000000000..73f2fabd0b --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/ruleeditor/navigate-in-panel/basic/.content.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/ruleeditor/navigate-in-panel/blank/.content.xml b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/ruleeditor/navigate-in-panel/blank/.content.xml new file mode 100644 index 0000000000..fb45b800fa --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/dam/formsanddocuments/core-components-it/samples/ruleeditor/navigate-in-panel/blank/.content.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/basic/.content.xml b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/basic/.content.xml index c4fcf4fd80..94f0869c82 100644 --- a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/basic/.content.xml +++ b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/basic/.content.xml @@ -1,60 +1,106 @@ + jcr:primaryType="cq:Page"> + cq:deviceGroups="[/etc/mobile/groups/responsive]" + cq:lastModified="{Date}2024-10-23T06:16:03.852Z" + cq:lastModifiedBy="admin" + cq:template="/conf/core-components-examples/settings/wcm/templates/af-blank-v2" + jcr:language="en" + jcr:primaryType="cq:PageContent" + jcr:title="basic" + sling:configRef="/conf/forms/core-components-it/samples/ruleeditor/basic/" + sling:resourceType="forms-components-examples/components/page"> - + sling:resourceType="forms-components-examples/components/form/container" + clientLibRef="corecomponent.it.customfunction,corecomponent.it.customfunction2" + dorType="none" + fieldType="form" + schemaType="none" + textIsRich="true" + thankYouMessage="Thank you for submitting the form." + thankYouOption="page" + themeRef="/libs/fd/af/themes/canvas" + title="basic"> + + + + + + + + + - + \ No newline at end of file diff --git a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/.content.xml b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/.content.xml new file mode 100644 index 0000000000..a0ac99e384 --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/.content.xml @@ -0,0 +1,3 @@ + + diff --git a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/basic/.content.xml b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/basic/.content.xml new file mode 100644 index 0000000000..b360bf824e --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/basic/.content.xml @@ -0,0 +1,611 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/blank/.content.xml b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/blank/.content.xml new file mode 100644 index 0000000000..79292d6e30 --- /dev/null +++ b/it/content/src/main/content/jcr_root/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/blank/.content.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/it/core/pom.xml b/it/core/pom.xml index c88f559f74..3ef2cf1334 100644 --- a/it/core/pom.xml +++ b/it/core/pom.xml @@ -86,6 +86,7 @@ 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)", * @@ -162,6 +163,19 @@ Import-Package: javax.annotation;version=0.0.0,* core-forms-components-af-core 3.0.70 + + + io.jsonwebtoken + jjwt-api + 0.11.2 + provided + + + io.jsonwebtoken + jjwt-impl + 0.11.2 + provided + diff --git a/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/HeadlessTransportHandler.java b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/HeadlessTransportHandler.java index 1d743239c7..33acd89eb1 100644 --- a/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/HeadlessTransportHandler.java +++ b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/HeadlessTransportHandler.java @@ -170,14 +170,28 @@ 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_client_secret", - "https://example.com/api/publish", + "your_private_key", + "your_certificate_thumbprint", + "your_resource_uri", httpClient ); - oauth2Client.publishOrDeleteFormModelJson(formModelJson, requestSupplier); + oauth2Client.publishOrDeleteFormModelJson(formModelJson, "https://example.com/api/publish", HttpPost::new); **/ } else { LOG.info("[HeadlessTransportHandler] No adaptive form container found for resource {}. Skipping", resource.getPath()); diff --git a/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/OAuth2Client.java b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/OAuth2Client.java index 9f999479ee..78ff020fe2 100644 --- a/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/OAuth2Client.java +++ b/it/core/src/main/java/com/adobe/cq/forms/core/components/it/service/OAuth2Client.java @@ -15,6 +15,14 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 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; @@ -27,34 +35,48 @@ 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 clientSecret; - private final String apiEndpoint; + private final String privateKey; + private final String certificateThumbprint; + private final String resource; private final CloseableHttpClient httpClient; private String accessToken; private long tokenExpirationTime; private final ReentrantLock lock = new ReentrantLock(); - public OAuth2Client(String tokenEndpoint, String clientId, String clientSecret, String apiEndpoint, CloseableHttpClient httpClient) { + public OAuth2Client(String tokenEndpoint, String clientId, String privateKey, String certificateThumbprint, String resource, CloseableHttpClient httpClient) { this.tokenEndpoint = tokenEndpoint; this.clientId = clientId; - this.clientSecret = clientSecret; - this.apiEndpoint = apiEndpoint; + this.privateKey = privateKey; + this.certificateThumbprint = certificateThumbprint; + this.resource = resource; this.httpClient = httpClient; } - public void publishOrDeleteFormModelJson(String formModelJson, Function requestSupplier) throws IOException { + public void publishOrDeleteFormModelJson(String formModelJson, String apiEndpoint, Function requestSupplier) throws IOException { String token = getValidToken(); HttpRequestBase request = requestSupplier.apply(apiEndpoint); request.setHeader("Authorization", "Bearer " + token); @@ -94,7 +116,12 @@ private String getValidToken() throws IOException { private String fetchOAuth2Token() throws IOException { HttpPost post = new HttpPost(tokenEndpoint); post.setHeader("Content-Type", "application/x-www-form-urlencoded"); - post.setEntity(new StringEntity("grant_type=client_credentials&client_id=" + clientId + "&client_secret=" + clientSecret)); + + 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)); try (CloseableHttpResponse response = httpClient.execute(post)) { if (response.getStatusLine().getStatusCode() == 200) { @@ -107,18 +134,7 @@ private String fetchOAuth2Token() throws IOException { } private String refreshOAuth2Token() throws IOException { - 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()); - } - } + return fetchOAuth2Token(); // Assuming the same flow for refresh token } private String parseToken(String responseBody) { @@ -130,9 +146,49 @@ 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); } } -} +} \ No newline at end of file diff --git a/ui.frontend/package-lock.json b/ui.frontend/package-lock.json index 6e84edfe2e..225f96ffc5 100644 --- a/ui.frontend/package-lock.json +++ b/ui.frontend/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@aemforms/af-core": "^0.22.111", - "@aemforms/af-custom-functions": "1.0.10", + "@aemforms/af-custom-functions": "1.0.12", "@aemforms/af-formatters": "^0.22.109" }, "devDependencies": { @@ -70,9 +70,9 @@ } }, "node_modules/@aemforms/af-custom-functions": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@aemforms/af-custom-functions/-/af-custom-functions-1.0.10.tgz", - "integrity": "sha512-n3w9tHkJOI5ISVYAK2cCi5k/oTu3rGgByDmMIgOH1+Ry4mL9nM3cxBTKEkPF8Y8JiKF1aUHIKM+MeP6u5PiiUA==" + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@aemforms/af-custom-functions/-/af-custom-functions-1.0.12.tgz", + "integrity": "sha512-O8ADuD8+6hOMj2EJ3Zaq3hCsmzhTTC9acgQtFvHMYOnII0dyKx1WOgNwDIQ++/CapVqLSmzkTyDvy4iLPtsrLA==" }, "node_modules/@aemforms/af-formatters": { "version": "0.22.111", @@ -11085,9 +11085,9 @@ } }, "@aemforms/af-custom-functions": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@aemforms/af-custom-functions/-/af-custom-functions-1.0.10.tgz", - "integrity": "sha512-n3w9tHkJOI5ISVYAK2cCi5k/oTu3rGgByDmMIgOH1+Ry4mL9nM3cxBTKEkPF8Y8JiKF1aUHIKM+MeP6u5PiiUA==" + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@aemforms/af-custom-functions/-/af-custom-functions-1.0.12.tgz", + "integrity": "sha512-O8ADuD8+6hOMj2EJ3Zaq3hCsmzhTTC9acgQtFvHMYOnII0dyKx1WOgNwDIQ++/CapVqLSmzkTyDvy4iLPtsrLA==" }, "@aemforms/af-formatters": { "version": "0.22.111", diff --git a/ui.frontend/package.json b/ui.frontend/package.json index 432ecdea9d..c2f8ad33c6 100644 --- a/ui.frontend/package.json +++ b/ui.frontend/package.json @@ -25,6 +25,6 @@ "dependencies": { "@aemforms/af-core": "^0.22.111", "@aemforms/af-formatters": "^0.22.109", - "@aemforms/af-custom-functions": "1.0.10" + "@aemforms/af-custom-functions": "1.0.12" } } diff --git a/ui.frontend/src/customFunctions.js b/ui.frontend/src/customFunctions.js index 681ab3d2df..8633feeb0a 100644 --- a/ui.frontend/src/customFunctions.js +++ b/ui.frontend/src/customFunctions.js @@ -98,5 +98,21 @@ 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 + 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 }; diff --git a/ui.frontend/src/view/FormFileInputWidgetBase.js b/ui.frontend/src/view/FormFileInputWidgetBase.js index 11104593ac..c34eeef9c0 100644 --- a/ui.frontend/src/view/FormFileInputWidgetBase.js +++ b/ui.frontend/src/view/FormFileInputWidgetBase.js @@ -41,7 +41,32 @@ class FormFileInputWidgetBase { "dll": "application/x-msdownload", "exe": "application/x-msdownload", "msi": "application/x-msdownload", - "msg": "application/vnd.ms-outlook" + "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" } initialFileValueFileNameMap; diff --git a/ui.tests/test-module/libs/commons/guideSelectors.js b/ui.tests/test-module/libs/commons/guideSelectors.js index 16540133ca..40b0f31bb7 100644 --- a/ui.tests/test-module/libs/commons/guideSelectors.js +++ b/ui.tests/test-module/libs/commons/guideSelectors.js @@ -167,6 +167,7 @@ var selectors = { ruleEditor : { action : { + configure: "#EditableToolbar [data-action='CONFIGURE']", editRule : "#EditableToolbar [data-action='editexpression']", createRuleButton : "#create-rule-button", saveRule : ".exp-Save-Button", @@ -181,16 +182,19 @@ 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" + PARAMETER : ".Parameters .choice-model.u-coral-clearFix.EXPRESSION", + STRING_LITERAL : ".choice-model.u-coral-clearFix .STRING_LITERAL", }, 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']", diff --git a/ui.tests/test-module/specs/fileinput/fileinputstring.runtime.cy.js b/ui.tests/test-module/specs/fileinput/fileinputstring.runtime.cy.js index 18e41b1244..6fa604fe30 100644 --- a/ui.tests/test-module/specs/fileinput/fileinputstring.runtime.cy.js +++ b/ui.tests/test-module/specs/fileinput/fileinputstring.runtime.cy.js @@ -90,6 +90,15 @@ describe("Form with File Input - Prefill & Submit tests", () => { const submitBtn = "submit1673953138924"; const pagePath = "/content/forms/af/core-components-it/samples/fileinput/fileinputstring/basic.html" + let toggle_array = []; + + before(() => { + cy.fetchFeatureToggles().then((response) => { + if (response.status === 200) { + toggle_array = response.body.enabled; + } + }); + }); beforeEach(() => { cy.wrap(prefillId).as('prefillId'); @@ -113,72 +122,76 @@ describe("Form with File Input - Prefill & Submit tests", () => { fileInputs.forEach((fileInput, idx) => { - it(`${fileInput.type} - attach files, check model, view, preview attachment and submit the form`, () => { - cy.previewForm(pagePath, { - onBeforeLoad: (win) => { - cy.stub(win, 'open'); // creating a stub to check file preview - } - }); - - // attach the file - cy.attachFile(fileInput.selector, fileInput.fileNames); - if(fileInput.multiple) - cy.attachFile(fileInput.selector, ['sample2.txt']); - - // submit the form - cy.get(".cmp-adaptiveform-button__widget").click(); - - // check for successful submission - submitTest(); - }) - - it(`${fileInput.type} - view prefill of submitted form, make changes to attachments and submit`, () => { - cy.get("@prefillId").then(id => { + // these test are based on don't replace data of FA which is present as string inside main form data + // hence checking if FT is explicitly enabled + if (toggle_array.includes('FT_FORMS-16351')) { + it(`${fileInput.type} - attach files, check model, view, preview attachment and submit the form`, () => { cy.previewForm(pagePath, { - params: [`prefillId=${id}`], - onBeforeLoad(win) { + onBeforeLoad: (win) => { cy.stub(win, 'open'); // creating a stub to check file preview } }); - // check if files were prefilled - checkFileNamesInFileAttachmentView(fileInput.selector, fileInput.fileNames); - - checkFilePreviewInFileAttachment(fileInput.selector); - - // check if guideBridge API returns file attachments correctly - getFormObjTest(['empty.pdf', ...(fileInput.multiple ? ['sample2.txt', 'sample.txt']: []) ]).then(() => { - // add new files after preview to both the component - cy.attachFile(fileInput.selector, ['sample2.txt']).then(() => { - // check if guideBridge API returns correctly after prefill and attaching more files - getFormObjTest(['sample2.txt', ...(fileInput.multiple ? ['sample.txt', 'empty.pdf', 'sample2.txt']: []) ]).then(() => { - // submit the form - cy.get(".cmp-adaptiveform-button__widget").click(); - // check if submission is success - submitTest(); - }) + // attach the file + cy.attachFile(fileInput.selector, fileInput.fileNames); + if (fileInput.multiple) + cy.attachFile(fileInput.selector, ['sample2.txt']); + + // submit the form + cy.get(".cmp-adaptiveform-button__widget").click(); + + // check for successful submission + submitTest(); + }) + + it(`${fileInput.type} - view prefill of submitted form, make changes to attachments and submit`, () => { + cy.get("@prefillId").then(id => { + cy.previewForm(pagePath, { + params: [`prefillId=${id}`], + onBeforeLoad(win) { + cy.stub(win, 'open'); // creating a stub to check file preview + } }); - }); - }); - }); + // check if files were prefilled + checkFileNamesInFileAttachmentView(fileInput.selector, fileInput.fileNames); + + checkFilePreviewInFileAttachment(fileInput.selector); + + // check if guideBridge API returns file attachments correctly + getFormObjTest(['empty.pdf', ...(fileInput.multiple ? ['sample2.txt', 'sample.txt'] : [])]).then(() => { + // add new files after preview to both the component + cy.attachFile(fileInput.selector, ['sample2.txt']).then(() => { + // check if guideBridge API returns correctly after prefill and attaching more files + getFormObjTest(['sample2.txt', ...(fileInput.multiple ? ['sample.txt', 'empty.pdf', 'sample2.txt'] : [])]).then(() => { + // submit the form + cy.get(".cmp-adaptiveform-button__widget").click(); + // check if submission is success + submitTest(); + }) + }); + }); - it(`${fileInput.type} - prefill of submitted prefilled form`, () => { - cy.get("@prefillId").then(id => { - cy.previewForm(pagePath, { - params: [`prefillId=${id}`], - onBeforeLoad(win) { - cy.stub(win, 'open'); // creating a stub to check file preview - } }); + }); - // check if files were prefilled - checkFileNamesInFileAttachmentView(fileInput.selector, ['sample2.txt', ...(fileInput.multiple ? ['sample.txt', 'empty.pdf']: []) ]); - getFormObjTest(['sample2.txt', ...(fileInput.multiple ? ['sample.txt', 'empty.pdf', 'sample2.txt']: []) ]); + it(`${fileInput.type} - prefill of submitted prefilled form`, () => { + cy.get("@prefillId").then(id => { + cy.previewForm(pagePath, { + params: [`prefillId=${id}`], + onBeforeLoad(win) { + cy.stub(win, 'open'); // creating a stub to check file preview + } + }); - }); + // check if files were prefilled + checkFileNamesInFileAttachmentView(fileInput.selector, ['sample2.txt', ...(fileInput.multiple ? ['sample.txt', 'empty.pdf'] : [])]); + getFormObjTest(['sample2.txt', ...(fileInput.multiple ? ['sample.txt', 'empty.pdf', 'sample2.txt'] : [])]); - }); + }); + + }); + } }) }); diff --git a/ui.tests/test-module/specs/ruleeditor/authoring/navigatePanel.authoring.cy.js b/ui.tests/test-module/specs/ruleeditor/authoring/navigatePanel.authoring.cy.js new file mode 100644 index 0000000000..849dfed11b --- /dev/null +++ b/ui.tests/test-module/specs/ruleeditor/authoring/navigatePanel.authoring.cy.js @@ -0,0 +1,119 @@ +const commons = require('../../../libs/commons/commons'), + sitesSelectors = require('../../../libs/commons/sitesSelectors'), + formsSelectors = require('../../../libs/commons/guideSelectors'), + afConstants = require('../../../libs/commons/formsConstants'); + +describe('Rule editor navigate-in-panel rule authoring',function(){ + let toggle_array = []; + + before(() => { + cy.fetchFeatureToggles().then((response) => { + if (response.status === 200) { + toggle_array = response.body.enabled; + } + }); + }); + + context('Open Forms Editor', function() { + const formPath = "/content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/blank", + formContainerPath = formPath + afConstants.FORM_EDITOR_FORM_CONTAINER_SUFFIX, + buttonEditPath = formContainerPath + "/" + afConstants.components.forms.resourceType.formbutton.split("/").pop(), + panelEditPath = formContainerPath + "/" + afConstants.components.forms.resourceType.panelcontainer.split("/").pop(), + buttonEditPathSelector = "[data-path='" + buttonEditPath + "']"; + + it('should add rule to focus previousItem in Panel on button click', function () { + if (toggle_array.includes("FT_FORMS-10781")) { + 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 Panel", afConstants.components.forms.resourceType.panelcontainer); + cy.get(sitesSelectors.overlays.overlay.component + "[data-path='" + formContainerPath + "/panelcontainer/*']").should("exist"); + cy.wait(1000); + + cy.insertComponent(sitesSelectors.overlays.overlay.component + "[data-path='" + formContainerPath + "/panelcontainer/*']", + "Adaptive Form Text Box", afConstants.components.forms.resourceType.formtextinput); + cy.insertComponent(sitesSelectors.overlays.overlay.component + "[data-path='" + formContainerPath + "/panelcontainer/*']", + "Adaptive Form Text Box", afConstants.components.forms.resourceType.formtextinput); + + cy.insertComponent(sitesSelectors.overlays.overlay.component + "[data-path='" + formContainerPath + "/*']", + "Adaptive Form Button", afConstants.components.forms.resourceType.formbutton); + cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + buttonEditPathSelector); + + // 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); + createNavigateInPanelRule(); + + // check and close rule editor + cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.closeRuleEditor).should("exist"); + cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.closeRuleEditor).click(); + + cy.get(sitesSelectors.overlays.overlay.component + buttonEditPathSelector).should("exist"); + cy.selectLayer("Edit"); + cy.deleteComponentByPath(buttonEditPath); + cy.deleteComponentByPath(panelEditPath); + } + }) + }) + + const createNavigateInPanelRule = function() { + cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.createRuleButton).should("be.visible").click(); + cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.sideToggleButton + ":first").click(); + + // // Forms Objects option is not existing on side panel + // cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.sidePanelFormObjectTab).should("exist"); + // cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.sidePanelFormObjectTab).then($el => { + // expect($el.text().trim()).to.equal("Form Objects"); + // }) + // + // // Functions option is not existing on side panel + // cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.sidePanelFunctionObjectTab).should("exist"); + // cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.sidePanelFunctionObjectTab).then($el => { + // expect($el.text().trim()).to.equal("Functions"); + // }) + + 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(); + + // IS CLICKED option not existing in 'OPERATIONS' dropdown + cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.operator.IS_CLICKED).should("exist"); + cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.operator.IS_CLICKED).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.NAVIGATE_IN_PANEL).should("exist"); + cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.operator.NAVIGATE_IN_PANEL).click({force: true}); + + cy.getRuleEditorIframe().find(".PANEL_FOCUS_OPTION").should("exist"); + cy.getRuleEditorIframe().find(".PANEL_FOCUS_OPTION").click(); + + cy.getRuleEditorIframe().find(".PANEL_FOCUS_OPTION coral-selectlist [value='PREVIOUS_ITEM']").click({force: true}); + + cy.getRuleEditorIframe().find(".terminal-view.PANEL.VARIABLE").should("be.visible"); + cy.getRuleEditorIframe().find(".terminal-view.PANEL.VARIABLE").click(); + + cy.getRuleEditorIframe().find(".terminal-view.PANEL.VARIABLE coral-overlay.is-open .expression-selectlist coral-selectlist-item:first").click({force: true}); + + cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.saveRule).should("exist"); + cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.action.saveRule).click(); + + // check if rule is created + cy.getRuleEditorIframe().find(formsSelectors.ruleEditor.ruleSummary.CREATED_RULE).should("exist"); + } +}) diff --git a/ui.tests/test-module/specs/ruleeditor/ruleEditor.authoring.cy.js b/ui.tests/test-module/specs/ruleeditor/authoring/ruleEditor.authoring.cy.js similarity index 73% rename from ui.tests/test-module/specs/ruleeditor/ruleEditor.authoring.cy.js rename to ui.tests/test-module/specs/ruleeditor/authoring/ruleEditor.authoring.cy.js index b2875adb53..f07808d426 100644 --- a/ui.tests/test-module/specs/ruleeditor/ruleEditor.authoring.cy.js +++ b/ui.tests/test-module/specs/ruleeditor/authoring/ruleEditor.authoring.cy.js @@ -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,6 +76,113 @@ 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) { - 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'); - }); -}) diff --git a/ui.tests/test-module/specs/ruleeditor/customFunction.runtime.cy.js b/ui.tests/test-module/specs/ruleeditor/runtime/customFunction.runtime.cy.js similarity index 100% rename from ui.tests/test-module/specs/ruleeditor/customFunction.runtime.cy.js rename to ui.tests/test-module/specs/ruleeditor/runtime/customFunction.runtime.cy.js diff --git a/ui.tests/test-module/specs/ruleeditor/runtime/navigatePanel.runtime.cy.js b/ui.tests/test-module/specs/ruleeditor/runtime/navigatePanel.runtime.cy.js new file mode 100644 index 0000000000..b96c97a475 --- /dev/null +++ b/ui.tests/test-module/specs/ruleeditor/runtime/navigatePanel.runtime.cy.js @@ -0,0 +1,107 @@ +describe("Rule editor navigate in panel runtime", () => { + let toggle_array = []; + before(() => { + cy.fetchFeatureToggles().then((response) => { + if (response.status === 200) { + toggle_array = response.body.enabled; + } + }); + }); + + const formsPage = "content/forms/af/core-components-it/samples/ruleeditor/navigate-in-panel/basic.html" + let formContainer = null; + + it("should navigate to next item in panel", () => { + if(toggle_array.includes("FT_FORMS-10781")) { + cy.previewForm(formsPage); + cy.get('#textinput-b1a2a445a1-widget').click(); + cy.get('#button-46e2481a3f-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-46e2481a3f-widget > .cmp-adaptiveform-button__text').click(); + + cy.focused().should('have.attr', 'id', 'textinput-4ab644c5b9-widget'); + } + }); + + it("should navigate to previous item in panel", () => { + if(toggle_array.includes("FT_FORMS-10781")) { + cy.previewForm(formsPage); + cy.get('#textinput-4ab644c5b9-widget').click(); + cy.get('#button-56f02db62a-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-56f02db62a-widget > .cmp-adaptiveform-button__text').click(); + + cy.focused().should('have.attr', 'id', 'textinput-b1a2a445a1-widget'); + } + }); + + it("should navigate to next panel in wizard", () => { + if(toggle_array.includes("FT_FORMS-10781")) { + cy.previewForm(formsPage); + cy.get('#textinput-e2985267ed-widget').click(); + cy.get('#button-aae7ec869b-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-aae7ec869b-widget > .cmp-adaptiveform-button__text').click(); + + cy.focused().should('have.attr', 'id', 'textinput-adb1f55685-widget'); + } + }); + + it("should navigate to previous panel in wizard", () => { + if(toggle_array.includes("FT_FORMS-10781")) { + cy.previewForm(formsPage); + cy.get('#textinput-e2985267ed-widget').click(); + cy.get('#button-aae7ec869b-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-aae7ec869b-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-eb427c0e82-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-eb427c0e82-widget > .cmp-adaptiveform-button__text').click(); + + cy.focused().should('have.attr', 'id', 'textinput-e2985267ed-widget'); + } + }); + + it("should navigate to next panel in HorizontalTabs", () => { + if(toggle_array.includes("FT_FORMS-10781")) { + cy.previewForm(formsPage); + cy.get('#textinput-cc08c3e84a-widget').click(); + cy.get('#button-4e44ce2042-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-4e44ce2042-widget > .cmp-adaptiveform-button__text').click(); + + cy.focused().should('have.attr', 'id', 'textinput-77fa9bd1dc-widget'); + } + }); + + it("should navigate to previous panel in HorizontalTabs", () => { + if(toggle_array.includes("FT_FORMS-10781")) { + cy.previewForm(formsPage); + cy.get('#textinput-cc08c3e84a-widget').click(); + cy.get('#button-4e44ce2042-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-4e44ce2042-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-e75d381952-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-e75d381952-widget > .cmp-adaptiveform-button__text').click(); + + cy.focused().should('have.attr', 'id', 'textinput-cc08c3e84a-widget'); + } + }); + + it("should navigate to next panel in VerticalTabs", () => { + if(toggle_array.includes("FT_FORMS-10781")) { + cy.previewForm(formsPage); + cy.get('#textinput-802818ca90-widget').click(); + cy.get('#button-ff091257b7-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-ff091257b7-widget > .cmp-adaptiveform-button__text').click(); + + cy.focused().should('have.attr', 'id', 'textinput-d227c4c481-widget'); + } + }); + + it("should navigate to previous panel in VerticalTabs", () => { + if(toggle_array.includes("FT_FORMS-10781")) { + cy.previewForm(formsPage); + cy.get('#textinput-802818ca90-widget').click(); + cy.get('#button-ff091257b7-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-ff091257b7-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-2c88dbcca7-widget > .cmp-adaptiveform-button__text').click(); + cy.get('#button-2c88dbcca7-widget > .cmp-adaptiveform-button__text').click(); + + cy.focused().should('have.attr', 'id', 'textinput-802818ca90-widget'); + } + }); +}) diff --git a/ui.tests/test-module/specs/ruleeditor/runtime/ruleEditorSanity.runtime.cy.js b/ui.tests/test-module/specs/ruleeditor/runtime/ruleEditorSanity.runtime.cy.js new file mode 100644 index 0000000000..92a57af3a3 --- /dev/null +++ b/ui.tests/test-module/specs/ruleeditor/runtime/ruleEditorSanity.runtime.cy.js @@ -0,0 +1,120 @@ +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; + }); + }); + + 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; + }) + }) + + if (cy.af.isLatestAddon()) { + it("should validate start and end date", () => { + expect(formContainer, "formcontainer is initialized").to.not.be.null; + console.log(formContainer._fields); + const [startDate, startDateFieldView] = Object.entries(formContainer._fields)[0]; + cy.get(`#${startDate}`).find("input").clear().type(getCurrentDateOffsetBy(-1)).blur().then(x => { + const startDateModel = formContainer._model.getElement(startDate); + expect(startDateModel.getState().valid).to.equal(true); + }); + const [endDate, endDateFieldView] = Object.entries(formContainer._fields)[1]; + cy.get(`#${endDate}`).find("input").clear().type(getCurrentDateOffsetBy(+1)).blur().then(x => { + const endDateModel = formContainer._model.getElement(endDate); + expect(endDateModel.getState().valid).to.equal(true); + }); + + //invalid start value + cy.get(`#${startDate}`).find("input").clear().type(getCurrentDateOffsetBy(+1)).blur().then(x => { + const startDateModel = formContainer._model.getElement(startDate); + expect(startDateModel.getState().valid).to.equal(false); + }); + }) + } + + function getCurrentDateOffsetBy(days) { + const today = new Date(); + today.setDate(today.getDate() + days); + + const day = String(today.getDate()).padStart(2, '0'); // Get day and pad with leading zero if necessary + const month = String(today.getMonth() + 1).padStart(2, '0'); // Get month (0-based index) and pad with leading zero if necessary + const year = today.getFullYear(); // Get full year + + return `${year}-${month}-${day}`; // Format the date as yyyy-mm-dd + } + + /** + * 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 (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)[2]; + cy.get(`#${textbox1}`).find("div > label").should('have.text', "Changed Label") + } + }) +}) + +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'); + }); +}) diff --git a/ui.tests/test-module/specs/ruleeditor/runtime/saveHandler.runtime.cy.js b/ui.tests/test-module/specs/ruleeditor/runtime/saveHandler.runtime.cy.js new file mode 100644 index 0000000000..88f0d4dc0e --- /dev/null +++ b/ui.tests/test-module/specs/ruleeditor/runtime/saveHandler.runtime.cy.js @@ -0,0 +1,38 @@ +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'); + }); + } + }) +}) \ No newline at end of file diff --git a/ui.tests/test-module/specs/ruleeditor/runtime/submitHandler.runtime.cy.js b/ui.tests/test-module/specs/ruleeditor/runtime/submitHandler.runtime.cy.js new file mode 100644 index 0000000000..6183a34b4c --- /dev/null +++ b/ui.tests/test-module/specs/ruleeditor/runtime/submitHandler.runtime.cy.js @@ -0,0 +1,106 @@ +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 (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 (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 (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 (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!") + }); + } + }); +}) \ No newline at end of file diff --git a/ui.tests/test-module/specs/ruleeditor/uichange.runtime.cy.js b/ui.tests/test-module/specs/ruleeditor/runtime/uichange.runtime.cy.js similarity index 100% rename from ui.tests/test-module/specs/ruleeditor/uichange.runtime.cy.js rename to ui.tests/test-module/specs/ruleeditor/runtime/uichange.runtime.cy.js