Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .circleci/ci/it-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const qpPath = '/home/circleci/cq';
const buildPath = '/home/circleci/build';
const { TYPE, BROWSER, AEM, PRERELEASE, FT, CORE_COMPONENTS, WCM_COMPONENTS} = process.env;
const isLatestAddon = AEM === 'addon-latest';
const jacocoAgent = '/home/circleci/.m2/repository/org/jacoco/org.jacoco.agent/0.8.3/org.jacoco.agent-0.8.3-runtime.jar';
const jacocoAgent = '/home/circleci/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar';

try {
// # Define the image name
Expand All @@ -39,7 +39,7 @@ try {

//todo: remove this later, once aem image is released, since sites rotary aem base image has "2.25.4"
//let wcmVersion = ci.sh('mvn help:evaluate -Dexpression=core.wcm.components.version -q -DforceStdout', true);
let wcmVersion = "2.30.2";
let wcmVersion = "2.32.4";
ci.stage("Integration Tests");
ci.dir(qpPath, () => {
// Connect to QP
Expand Down Expand Up @@ -105,7 +105,7 @@ try {
${ci.addQpFileDependency(config.modules['core-forms-components-it-tests-core'])} \
${ci.addQpFileDependency(config.modules['core-forms-components-it-tests-apps'])} \
${ci.addQpFileDependency(config.modules['core-forms-components-it-tests-content'])} \
--vm-options \\\"-Xmx4096m -XX:MaxPermSize=1024m -Djava.awt.headless=true -javaagent:${jacocoAgent}=destfile=crx-quickstart/jacoco-it.exec\\\" \
--vm-options \\\"-Xmx4096m -Djava.awt.headless=true -javaagent:${jacocoAgent}=destfile=crx-quickstart/jacoco-it.exec\\\" \
${preleaseOpts}`);
});

Expand Down
2 changes: 1 addition & 1 deletion .circleci/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
entrypoint: tail -f /dev/null # Keeps the container running

circleci-aem-cloudready:
image: docker-adobe-cif-release.dr-uw2.adobeitc.com/circleci-aem-cloudready:23385-openjdk11
image: docker-adobe-cif-release.dr-uw2.adobeitc.com/circleci-aem-cloudready:27293-v2-openjdk21
depends_on:
- circleci-qp
# Add any additional configurations or environment variables if needed
Expand Down
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ AEM Core Forms Components — Adaptive Forms v2 component library.
Module structure, Java model hierarchy, form JSON model, IT infrastructure, Cypress patterns, JCR content XML patterns.
- **Runtime internals**: [`docs/architecture/runtime-internals.md`](docs/architecture/runtime-internals.md)
Deep dive into the form initialization pipeline, repeatable container/InstanceManager mechanics, FT_FORMS-24358 items-array export, common crash signatures, and debugging checklist.
- **Component versioning**: [`docs/architecture/component-versioning.md`](docs/architecture/component-versioning.md)
When a change needs a new component version (`v1` → `v2`) vs. a fix-in-place, and the full checklist of what to update when bumping a version (clientlib self-containment, `_cq_dialog`/`_cq_styleConfig` inheritance, `pom.xml`, runtime clientlib embed lists, etc.).

### E2E Testing
- **Feature Toggle Tests**: [`docs/e2e-testing/feature-toggles.md`](docs/e2e-testing/feature-toggles.md)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,6 @@ private FormConstants() {
/** The resource type for date picker v1 */
public static final String RT_FD_FORM_DATE_PICKER_V1 = RT_FD_FORM_PREFIX + "datepicker/v1/datepicker";

/** The resource type for date picker v2 */
public static final String RT_FD_FORM_DATE_PICKER_V2 = RT_FD_FORM_PREFIX + "datepicker/v2/datepicker";

/** The resource type for number input v1 */
public static final String RT_FD_FORM_NUMBER_INPUT_V1 = RT_FD_FORM_PREFIX + "numberinput/v1/numberinput";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@
adaptables = { SlingHttpServletRequest.class, Resource.class },
adapters = { DatePicker.class,
ComponentExporter.class },
resourceType = {
FormConstants.RT_FD_FORM_DATE_PICKER_V1,
FormConstants.RT_FD_FORM_DATE_PICKER_V2
})
resourceType = { FormConstants.RT_FD_FORM_DATE_PICKER_V1 })
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class DatePickerImpl extends AbstractFieldImpl implements DatePicker {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ public enum FormMetaDataType {
PREFILL_ACTION("prefillServiceProvider"),
LANG("lang"),
FORMATTERS("formatters"),
SSV_CLOUD_CONFIG("ssvCloudServiceConfiguration");
SSV_CLOUD_CONFIG("ssvCloudServiceConfiguration"),
FILE_ATTACHMENT_VALIDATOR("fileAttachmentValidator");

private String value;

Expand Down Expand Up @@ -181,12 +182,13 @@ private List<Resource> getDataSourceResources(SlingHttpServletRequest request, R
FormMetaData formMetaData = resourceResolver.adaptTo(FormMetaData.class);
if (formMetaData != null) {
Iterator<FormsManager.ComponentDescription> metaDataList = null;
I18n i18n = new I18n(request.getResourceBundle(request.getLocale()));
switch (type) {
case FORMATTERS:
case LANG:
ContentPolicy policy = ComponentUtils.getPolicy((String) request.getAttribute(Value.CONTENTPATH_ATTRIBUTE),
resourceResolver);
resources.add(getResourceForDropdownDisplay(resourceResolver, "Select", ""));
resources.add(getResourceForDropdownDisplay(resourceResolver, i18n.get("Select"), ""));
if (policy != null) {
ValueMap props = policy.getProperties();
if (props != null) {
Expand All @@ -204,7 +206,7 @@ private List<Resource> getDataSourceResources(SlingHttpServletRequest request, R
}
}
}
resources.add(getResourceForDropdownDisplay(resourceResolver, "Custom", "custom"));
resources.add(getResourceForDropdownDisplay(resourceResolver, i18n.get("Custom"), "custom"));
break;
case SUBMIT_ACTION:
// filter the submit actions by uniqueness and data model
Expand All @@ -223,7 +225,12 @@ private List<Resource> getDataSourceResources(SlingHttpServletRequest request, R
case PREFILL_ACTION:
metaDataList = formMetaData.getPrefillActions();
// Add an explicit empty option so authors can clear an already selected prefill service.
I18n i18n = new I18n(request.getResourceBundle(request.getLocale()));
resources.add(getResourceForDropdownDisplay(resourceResolver, i18n.get("None"), ""));
resources.addAll(this.getResourceListFromComponentDescription(metaDataList, resourceResolver));
break;
case FILE_ATTACHMENT_VALIDATOR:
metaDataList = formMetaData.getFileAttachmentValidators();
// Add an explicit empty option so authors can clear an already selected file attachment validator.
resources.add(getResourceForDropdownDisplay(resourceResolver, i18n.get("None"), ""));
resources.addAll(this.getResourceListFromComponentDescription(metaDataList, resourceResolver));
break;
Expand Down
19 changes: 19 additions & 0 deletions bundles/af-core/src/test/java/com/adobe/cq/forms/core/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest;
import org.jetbrains.annotations.NotNull;

Expand Down Expand Up @@ -252,4 +253,22 @@ public static Method getPrivateMethod(Class clazz, String privateMethodName) {
return null;
}
}

/**
* Removes the request's {@link SlingBindings} attribute so the AF locale-resolution code in
* {@code GuideUtils} takes its {@code bindings == null} branches.
* <p>
* Call this in tests that set an AF language parameter before adapting/using a model. Newer
* {@code aem-forms-sdk-api} versions resolve the locale via {@code GuideUtils}, and when a
* {@link SlingBindings} is present they look up services through {@code bindings.getSling()}:
* {@code getSanitizedLocale(...)} fetches the runtime-only Granite {@code ToggleRouter} (absent
* from the test classpath → {@link NoClassDefFoundError}), and {@code getLocaleParamFromRequest(...)}
* fetches {@code GuideLocalizationService} via {@code getSling()} without null-guarding it. Both
* are skipped entirely when the {@code SlingBindings} attribute is absent, and the locale is then
* taken from the {@code afAcceptLang} request parameter (which these tests set) — the same result
* expected in production for that parameter, since no {@code ToggleRouter} is registered in tests.
*/
public static void disableLocaleFeatureToggleLookup(MockSlingHttpServletRequest request) {
request.setAttribute(SlingBindings.class.getName(), null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public String getCustomFunctionUrl(Resource resource) {
return null;
}

@Override
public String getCustomFunctionFranklinProxyUrl(Resource resource) {
return null;
}

@Override
public HCaptchaConfiguration getHCaptchaCloudConfiguration(Resource resource) throws GuideException {
return hCaptchaConfiguration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ public String getCustomFunctionUrl(Resource resource) {
return null;
}

@Override
public String getCustomFunctionFranklinProxyUrl(Resource resource) {
return null;
}

@Override
public HCaptchaConfiguration getHCaptchaCloudConfiguration(Resource resource) throws GuideException {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ void testTitleWithLocale() throws Exception {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put(GuideConstants.AF_LANGUAGE_PARAMETER, "de");
request.setParameterMap(paramMap);
Utils.disableLocaleFeatureToggleLookup(request);
Title title = request.adaptTo(Title.class);
assertEquals("Title", title.getText());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ public String getCustomFunctionUrl(Resource resource) {
return null;
}

@Override
public String getCustomFunctionFranklinProxyUrl(Resource resource) {
return null;
}

@Override
public HCaptchaConfiguration getHCaptchaCloudConfiguration(Resource resource) throws GuideException {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ private FormContainer getFormContainerWithLocaleUnderTest(String resourcePath) t
Map<String, Object> paramMap = new HashMap<>();
paramMap.put(GuideConstants.AF_LANGUAGE_PARAMETER, "de");
request.setParameterMap(paramMap);
Utils.disableLocaleFeatureToggleLookup(request);
context.currentResource().adaptTo(FormContainer.class);
return request.adaptTo(FormContainer.class);
}
Expand All @@ -507,6 +508,7 @@ private FormContainer getFormContainerWithRTLLocaleUnderTest(String resourcePath
Map<String, Object> paramMap = new HashMap<>();
paramMap.put(GuideConstants.AF_LANGUAGE_PARAMETER, "ar-ae");
request.setParameterMap(paramMap);
Utils.disableLocaleFeatureToggleLookup(request);
context.currentResource().adaptTo(FormContainer.class);
return request.adaptTo(FormContainer.class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ void testTitleWithLocale() throws Exception {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put(GuideConstants.AF_LANGUAGE_PARAMETER, "de");
request.setParameterMap(paramMap);
Utils.disableLocaleFeatureToggleLookup(request);
FormTitle title = request.adaptTo(FormTitle.class);
assertEquals("Title", title.getText());
}
Expand Down
26 changes: 26 additions & 0 deletions docs/architecture/component-versioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Component Versioning

When a change requires a new component version (`v1` → `v2`), and what to check when bumping.

## When to bump

Bump when the change breaks a contract external code depends on:

- DOM structure (elements added/removed/reordered, class renames) that could break authored CSS or test selectors.
- JS runtime contract (clientlib category, init/attach lifecycle, public events, exported model/JSON shape).
- Authoring contract (dialog field renames that would corrupt existing authored content).

Do not bump for bug fixes, including accessibility fixes, or additive/non-breaking changes. Fix these forward in the existing version. Example: the datepicker `v1`→`v2` cycle (below) shows accessibility fixes being folded back into `v1` rather than kept as a `v2`-only fix.

## Checklist

Based on past version bumps in this repo (button/submit/reset v2, title v2, wizard v2, radiobutton v2, checkboxgroup v2, fileinput v2–v4, container v2, datepicker v2). None of these bumps completed every item below in a single commit — treat this as a review checklist, not a fixed template.

1. **New version folder**: own `.content.xml` (title suffixed `(v<N+1>)`), HTL, `README.md`. Done consistently in every past bump.
2. **Clientlib**: only needed if JS/CSS actually changed. Several past bumps (button, submit, reset, title, wizard v2) shipped no clientlib of their own and continued sharing `v1`'s. Where a clientlib was added (radiobutton, checkboxgroup, fileinput, container, datepicker v2), it was a full standalone copy — never a path into the previous version's clientlib folder.
3. **`_cq_dialog` / `_cq_design_dialog` / `_cq_styleConfig` / `_cq_template.xml`**: inherit via `sling:resourceSuperType` when unchanged (the common case). Add an own copy only where fields actually differ (e.g. fileinput v3/v4, container v2). Check `extraClientlibs`, `helpPath`, and `trackingFeature` values that hardcode a version number — these do not update via inheritance.
4. **Java model**: add `FormConstants.RT_FD_FORM_<COMPONENT>_V<N+1>` and update the Sling Model's `resourceType` array only if the exported model/JSON shape changed. Most past bumps (button, submit, reset, wizard, radiobutton, checkboxgroup, fileinput v4) did not touch Java at all — HTML/JS-only changes did not need it.
5. **`ui.af.apps/pom.xml`**: the `<replace token=".core-adaptiveform">` step controls which version is visible in the component browser. This was the step most often missed in past bumps and added later in bulk cleanup PRs — verify it explicitly rather than assuming it was done alongside the version folder.
6. **Runtime clientlib embed lists** (`ui.af.apps/.../core-forms-components-runtime-all/.content.xml`, `it/apps/.../custom-forms-components-runtime-all/.content.xml`): update only if step 2 added a new clientlib category. See the inline comment in those files for the replace-vs-add-alongside rule.
7. **IT content / examples**: update any `.content.xml` with `sling:resourceSuperType` pointing at the old version, if the new version becomes the default. This was skipped entirely in some past bumps (e.g. datepicker, throughout its `v2` add-and-removal).
8. **E2E spec**: add a Cypress spec for the new version's runtime behavior. Naming has not been fully consistent historically (`<component>v<N+1>.runtime.cy.js` in most cases; `.spec.js` in older ones).
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- When bumping a component from vN to vN+1: if the new version's clientlib is fully
self-contained (own resources and fixes, no regressions), replace the old category
with the new one; otherwise retain the old category alongside the new one.
See docs/architecture/component-versioning.md for the full checklist. -->
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="cq:ClientLibraryFolder"
allowProxy="{Boolean}true"
cssProcessor="[default:none,min:none]"
jsProcessor="[default:none,min:none]"
categories="[core.forms.components.it.runtime.all]"
embed="[core.forms.components.runtime.base,core.forms.components.it.container.v1.runtime,core.forms.components.datePicker.v1.runtime,core.forms.components.datePicker.v2.runtime,core.forms.components.textinput.v1.runtime,core.forms.components.numberinput.v1.runtime,core.forms.components.panelcontainer.v1.runtime,core.forms.components.radiobutton.v1.runtime,core.forms.components.text.v1.runtime,core.forms.components.checkboxgroup.v1.runtime,core.forms.components.button.v1.runtime,core.forms.components.image.v1.runtime,core.forms.components.dropdown.v1.runtime,core.forms.components.fileinput.v2.runtime,core.forms.components.accordion.v1.runtime,core.forms.components.tabs.v1.runtime,core.forms.components.wizard.v1.runtime,core.forms.components.verticaltabs.v1.runtime,core.forms.components.recaptcha.v1.runtime,core.forms.components.checkbox.v1.runtime,core.forms.components.fragment.v1.runtime,core.forms.components.switch.v1.runtime,core.forms.components.termsandconditions.v1.runtime, core.forms.components.it.textinput.v1.runtime, core.forms.components.hcaptcha.v1.runtime, core.forms.components.turnstile.v1.runtime]"/>
embed="[core.forms.components.runtime.base,core.forms.components.it.container.v1.runtime,core.forms.components.datePicker.v1.runtime,core.forms.components.textinput.v1.runtime,core.forms.components.numberinput.v1.runtime,core.forms.components.panelcontainer.v1.runtime,core.forms.components.radiobutton.v1.runtime,core.forms.components.text.v1.runtime,core.forms.components.checkboxgroup.v1.runtime,core.forms.components.button.v1.runtime,core.forms.components.image.v1.runtime,core.forms.components.dropdown.v1.runtime,core.forms.components.fileinput.v2.runtime,core.forms.components.accordion.v1.runtime,core.forms.components.tabs.v1.runtime,core.forms.components.wizard.v1.runtime,core.forms.components.verticaltabs.v1.runtime,core.forms.components.recaptcha.v1.runtime,core.forms.components.checkbox.v1.runtime,core.forms.components.fragment.v1.runtime,core.forms.components.switch.v1.runtime,core.forms.components.termsandconditions.v1.runtime, core.forms.components.it.textinput.v1.runtime, core.forms.components.hcaptcha.v1.runtime, core.forms.components.turnstile.v1.runtime]"/>
2 changes: 1 addition & 1 deletion parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1136,7 +1136,7 @@
<dependency>
<groupId>com.adobe.aem</groupId>
<artifactId>aem-forms-sdk-api</artifactId>
<version>2024.09.08.00-240800</version>
<version>2026.06.27.00-260700</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- When bumping a component from vN to vN+1: if the new version's clientlib is fully
self-contained (own resources and fixes, no regressions), replace the old category
with the new one; otherwise retain the old category alongside the new one.
See docs/architecture/component-versioning.md for the full checklist. -->
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="cq:ClientLibraryFolder"
allowProxy="{Boolean}true"
Expand Down
Loading
Loading