diff --git a/.circleci/ci/it-tests.js b/.circleci/ci/it-tests.js
index 128f25a0d4..ec56d09b48 100644
--- a/.circleci/ci/it-tests.js
+++ b/.circleci/ci/it-tests.js
@@ -24,7 +24,7 @@ const config = ci.restoreConfiguration();
console.log(config);
const qpPath = '/home/circleci/cq';
const buildPath = '/home/circleci/build';
-const { TYPE, BROWSER, AEM } = process.env;
+const { TYPE, BROWSER, AEM, COMMERCE_ENDPOINT, COMMERCE_INTEGRATION_TOKEN } = process.env;
const CORE_BUNDLE = 'com.adobe.commerce.cif.core-cif-components-core';
const ADDON_BUNDLE = 'com.adobe.cq.cif.commerce-addon-bundle';
@@ -96,6 +96,101 @@ const prepareAemForCifTests = () => {
throw new Error(`Timed out after ${AEM_READY_TIMEOUT_MS / 1000}s waiting for AEM to be ready.`);
};
+// ---------------------------------------------------------------------------
+// IT site OSGi bootstrap (see it/site/README.md — "CircleCI integration tests")
+//
+// ui.config is correct in Git but the embedded ui.config subpackage is not always
+// active on pipeline Quickstart before it/http runs. Without this block, AEM uses
+// CIF defaults (url_key URLs, no GraphQL caches). Values below must match:
+// it/site/ui.config/.../GraphqlClientImpl~default.cfg.json
+// it/site/ui.config/.../UrlProviderImpl.cfg.json
+// ---------------------------------------------------------------------------
+const IT_SITE_GRAPHQL_CACHE_CONFIGURATIONS = [
+ 'cif-components-it-site/components/commerce/navigation:true:5:300',
+ 'com.adobe.cq.commerce.core.search.services.SearchFilterService:true:10:300',
+ 'cif-components-it-site/components/commerce/breadcrumb:true:1000:300',
+ 'cif-components-it-site/components/commerce/product:true:50:1000',
+ 'cif-components-it-site/components/commerce/productcollection:true:50:1000',
+ 'cif-components-it-site/components/commerce/productlist:true:50:300'
+];
+
+const encodeOsgiFormBody = (formData) => {
+ const parts = [];
+ for (const [key, value] of Object.entries(formData)) {
+ if (Array.isArray(value)) {
+ for (const item of value) {
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(item)}`);
+ }
+ } else {
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
+ }
+ }
+ return parts.join('&');
+};
+
+const postOsgiConfig = (configPath, formData) => {
+ ci.sh(`curl -sf 'http://localhost:4502/system/console/configMgr/${configPath}' \
+ -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \
+ -u 'admin:admin' \
+ --data-raw '${encodeOsgiFormBody(formData)}'`);
+};
+
+// IT site commerce pages use GraphqlClientImpl~default. Apply full IT ui.config (not url/httpMethod only).
+const configureItSiteGraphqlClient = () => {
+ if (!COMMERCE_ENDPOINT) {
+ console.log('Skipping GraphqlClientImpl~default: COMMERCE_ENDPOINT is not set');
+ return;
+ }
+
+ const propertyNames = [
+ 'identifier',
+ 'url',
+ 'httpMethod',
+ 'connectionTimeout',
+ 'socketTimeout',
+ 'maxHttpConnections',
+ 'requestPoolTimeout',
+ 'acceptSelfSignedCertificates',
+ 'allowHttpProtocol',
+ 'cacheConfigurations'
+ ];
+ const formData = {
+ apply: true,
+ action: 'ajaxConfigManager',
+ factoryPid: 'com.adobe.cq.commerce.graphql.client.impl.GraphqlClientImpl',
+ identifier: 'default',
+ url: COMMERCE_ENDPOINT,
+ httpMethod: 'POST',
+ connectionTimeout: '5000',
+ socketTimeout: '5000',
+ maxHttpConnections: '20',
+ requestPoolTimeout: '2000',
+ acceptSelfSignedCertificates: 'true',
+ allowHttpProtocol: 'true',
+ cacheConfigurations: IT_SITE_GRAPHQL_CACHE_CONFIGURATIONS,
+ propertylist: propertyNames.join(',')
+ };
+ if (AEM === 'classic' || AEM === 'lts') {
+ formData.allowInsecure = 'true';
+ propertyNames.push('allowInsecure');
+ formData.propertylist = propertyNames.join(',');
+ }
+
+ postOsgiConfig('com.adobe.cq.commerce.graphql.client.impl.GraphqlClientImpl~default', formData);
+};
+
+// Mirrors it/site/ui.config/.../UrlProviderImpl.cfg.json (CI ui.config package is not always applied).
+const configureItSiteUrlProvider = () => {
+ postOsgiConfig('com.adobe.cq.commerce.core.components.internal.services.UrlProviderImpl', {
+ apply: true,
+ action: 'ajaxConfigManager',
+ productPageUrlFormat: '{{page}}.html/{{url_path}}.html#{{variant_sku}}',
+ enableContextAwareProductUrls: 'true',
+ categoryPageUrlFormat: '{{page}}.html/{{url_path}}.html',
+ propertylist: 'productPageUrlFormat,enableContextAwareProductUrls,categoryPageUrlFormat'
+ });
+};
+
try {
ci.stage("Integration Tests");
let wcmVersion = ci.sh('mvn help:evaluate -Dexpression=core.wcm.components.version -q -DforceStdout', true);
@@ -103,12 +198,22 @@ try {
let excludedCategory;
if (AEM === 'classic') {
excludedCategory = 'junit.category.IgnoreOn65';
+ } else if (AEM === 'lts') {
+ excludedCategory = 'junit.category.IgnoreOnLts';
} else {
- // LTS and cloud-ready: exclude @Category(IgnoreOnCloud) tests (same as master for LTS).
- // IgnoreOnLts does not exist yet; add it when we have LTS-only @Category annotations.
excludedCategory = 'junit.category.IgnoreOnCloud';
}
+ // Build it/site with the appropriate profile
+ ci.dir('it/site', () => {
+ const profile = (AEM === 'classic' || AEM === 'lts') ? ' -Pclassic' : '';
+ ci.sh(`mvn -B clean install${profile}`);
+ });
+
+ let itSitePackage = (AEM === 'classic' || AEM === 'lts')
+ ? ci.addQpFileDependency(config.modules['cif-components-it-site.all-classic'])
+ : ci.addQpFileDependency(config.modules['cif-components-it-site.all']);
+
ci.dir(qpPath, () => {
// Connect to QP
ci.sh('./qp.sh -v bind --server-hostname localhost --server-port 55555');
@@ -148,6 +253,7 @@ try {
${ci.addQpFileDependency(config.modules['core-cif-components-examples-apps'])} \
${ci.addQpFileDependency(config.modules['core-cif-components-examples-content'])} \
${ci.addQpFileDependency(config.modules['core-cif-components-it-tests-content'])} \
+ ${itSitePackage} \
--vm-options \\\"-Xmx1536m ${maxMetaspace} -Djava.awt.headless=true -javaagent:${process.env.JACOCO_AGENT}=destfile=crx-quickstart/jacoco-it.exec,output=tcpserver,port=6300\\\"`);
});
@@ -175,15 +281,22 @@ try {
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
.join('&')}'`);
+ configureItSiteGraphqlClient();
+ configureItSiteUrlProvider();
+
// Run integration tests
if (TYPE === 'integration') {
+ const commerceEndpoint = COMMERCE_ENDPOINT ? `-DCOMMERCE_ENDPOINT="${COMMERCE_ENDPOINT}"` : '';
+ const integrationToken = COMMERCE_INTEGRATION_TOKEN ? `-DCOMMERCE_INTEGRATION_TOKEN="${COMMERCE_INTEGRATION_TOKEN}"` : '';
ci.dir('it/http', () => {
ci.sh(`mvn clean verify -U -B \
-Ptest-all \
-Dexclude.category=${excludedCategory} \
-Dsling.it.instance.url.1=http://localhost:4502 \
-Dsling.it.instance.runmode.1=author \
- -Dsling.it.instances=1`);
+ -Dsling.it.instances=1 \
+ ${commerceEndpoint} \
+ ${integrationToken}`);
});
}
diff --git a/.gitignore b/.gitignore
index 4bcdf07823..4511e6d567 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
# Ignore things generated by Java
*.class
+.java-version
.classpath
.project
.settings
@@ -44,6 +45,7 @@ hs_err_pid*
# Javascript things
**/node_modules/**
+**/node/**
**/karma-junit/**
**/coverage/**
**/test-results/**
diff --git a/it/http/README.md b/it/http/README.md
index de14167ebc..21f160ef0b 100644
--- a/it/http/README.md
+++ b/it/http/README.md
@@ -4,4 +4,4 @@ This folder contains integration tests that test all the components based on the
To execute the tests, simply run `mvn clean verify -Ptest-all`
-You can also execute the tests in your favorite IDE.
\ No newline at end of file
+You can also execute the tests in your favorite IDE.
diff --git a/it/http/src/test/java/com/adobe/cq/commerce/it/http/CacheInvalidationIT.java b/it/http/src/test/java/com/adobe/cq/commerce/it/http/CacheInvalidationIT.java
new file mode 100644
index 0000000000..56ee1ed930
--- /dev/null
+++ b/it/http/src/test/java/com/adobe/cq/commerce/it/http/CacheInvalidationIT.java
@@ -0,0 +1,890 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Copyright 2026 Adobe
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package com.adobe.cq.commerce.it.http;
+
+import java.io.IOException;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.SlingHttpResponse;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import junit.category.IgnoreOn65;
+import junit.category.IgnoreOnCloud;
+import junit.category.IgnoreOnLts;
+
+/**
+ * Integration tests for the CIF cache invalidation servlet at {@code /bin/cif/invalidate-cache}.
+ *
+ *
+ * Tests fall into two tiers:
+ *
+ *
Servlet availability — verifies each payload type is accepted (no Magento writes).
+ *
Full cache workflow — updates Magento data via REST, confirms AEM serves stale cached
+ * data, posts an invalidation request, then confirms AEM serves fresh data on both the category
+ * listing and the product detail page. Requires {@code COMMERCE_ENDPOINT} (Magento base URL) and
+ * {@code COMMERCE_INTEGRATION_TOKEN} to be set as system properties or environment variables.
+ *
+ *
+ *
+ * Workflow tests run against three independent product/category pairs — one per AEM target
+ * (Classic/6.5, LTS, Cloud) — selected by JUnit category. This mirrors the Venia reference
+ * implementation and prevents cross-environment Magento state conflicts when tests run
+ * back-to-back against a shared Magento backend.
+ *
+ *
+ * Prerequisites:
+ *
+ *
CIF Core Components bundle active ({@code core-cif-components-core})
+ *
CIF addon installed — provides the {@code /bin/cif/invalidate-cache} servlet
+ *
{@code InvalidateCacheNotificationImpl} factory config deployed via {@code it/site/ui.config}
+ *
{@code InvalidateCacheSupport} OSGi config deployed via {@code it/site/ui.config}
+ *
{@code GraphqlClientImpl~default} config with cache entries for productlist and product components
+ *
+ */
+public class CacheInvalidationIT extends ItSiteTestBase {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CacheInvalidationIT.class);
+
+ private static final String CACHE_INVALIDATION_ENDPOINT = "/bin/cif/invalidate-cache";
+
+ /** How long to poll AEM after cache invalidation (override with {@code -DCIF_IT_AEM_POLL_TIMEOUT_MS=60000}). */
+ private static final long AEM_POLL_TIMEOUT_MS = resolveAemPollTimeoutMs();
+
+ private static final long AEM_POLL_INTERVAL_MS = 1_000L;
+
+ private static long resolveAemPollTimeoutMs() {
+ String prop = System.getProperty("CIF_IT_AEM_POLL_TIMEOUT_MS");
+ if (prop != null && !prop.isEmpty()) {
+ return Long.parseLong(prop);
+ }
+ return 30_000L;
+ }
+
+ // ---- per-environment test data --------------------------------------
+
+ /**
+ * Immutable holder for a single environment's product + category test fixture.
+ * Each fixture targets a different Magento product/category so concurrent or back-to-back
+ * test runs against a shared Magento backend do not corrupt each other's state.
+ */
+ private static final class TestData {
+ final String productSku;
+ final String originalProductName;
+ final String categoryUid;
+ final int categoryId;
+ final String categoryUrlPath;
+ final String originalCategoryName;
+ final String categoryPageUrl;
+
+ TestData(String productSku, String originalProductName,
+ String categoryUid, int categoryId, String categoryUrlPath, String originalCategoryName) {
+ this.productSku = productSku;
+ this.originalProductName = originalProductName;
+ this.categoryUid = categoryUid;
+ this.categoryId = categoryId;
+ this.categoryUrlPath = categoryUrlPath;
+ this.originalCategoryName = originalCategoryName;
+ this.categoryPageUrl = IT_SITE_ROOT + "/products/category-page.html/" + categoryUrlPath + ".html";
+ }
+ }
+
+ // Each fixture uses a 2-level category url_path so the leaf category name appears in the
+ // PDP breadcrumb (the IT site's breadcrumb component uses structureDepth=2).
+ // The PDP URL is discovered at runtime from the product card href on the category page
+ // — see discoverPdpUrl() — so the test works regardless of how each AEM instance is
+ // configured to build product URLs.
+
+ // Classic / AEM 6.5 — Blouses & Shirts
+ private static final TestData CLASSIC = new TestData(
+ "VT01", "Penelope Peasant Blouse",
+ "MjM=", 23, "venia-tops/venia-blouses", "Blouses & Shirts");
+
+ // LTS — Pants & Shorts
+ private static final TestData LTS = new TestData(
+ "VP01", "Selena Pants",
+ "MzI=", 32, "venia-bottoms/venia-pants", "Pants & Shorts");
+
+ // Cloud — Scarves
+ private static final TestData CLOUD = new TestData(
+ "VA01", "Dulcea Infinity Scarf",
+ "MTQ=", 14, "venia-accessories/venia-scarves", "Scarves");
+
+ // ---- Magento REST connection ----------------------------------------
+
+ // COMMERCE_ENDPOINT is the Magento base URL (without /graphql), e.g. https://mcprod.example.com
+ // REST writes go to COMMERCE_ENDPOINT/rest/V1 — must point to a writable Magento instance
+ private static final String COMMERCE_ENDPOINT = resolveCommerceEndpoint();
+ private static final String INTEGRATION_TOKEN = resolveIntegrationToken();
+
+ private static String resolveCommerceEndpoint() {
+ String prop = System.getProperty("COMMERCE_ENDPOINT");
+ if (prop != null && !prop.isEmpty()) {
+ return prop;
+ }
+ return System.getenv("COMMERCE_ENDPOINT");
+ }
+
+ private static String resolveIntegrationToken() {
+ String prop = System.getProperty("COMMERCE_INTEGRATION_TOKEN");
+ if (prop != null && !prop.isEmpty()) {
+ return prop;
+ }
+ return System.getenv("COMMERCE_INTEGRATION_TOKEN");
+ }
+
+ private static String commerceRestBase() {
+ if (COMMERCE_ENDPOINT == null) {
+ return null;
+ }
+ String base = COMMERCE_ENDPOINT;
+ if (base.endsWith("/graphql")) {
+ base = base.substring(0, base.length() - "/graphql".length());
+ }
+ return base.replaceAll("/+$", "") + "/rest/V1";
+ }
+
+ /**
+ * Workflow tests write to Magento via REST; fail fast with a clear message when credentials
+ * are not configured. Servlet availability tests do not call this.
+ */
+ private static void requireCommerceCredentials() {
+ Assert.assertNotNull(
+ "Workflow tests require COMMERCE_ENDPOINT as a system property or environment variable "
+ + "(Magento base URL, e.g. https://mcprod.example.com). Pass -DCOMMERCE_ENDPOINT=… to Maven "
+ + "or export COMMERCE_ENDPOINT=….",
+ COMMERCE_ENDPOINT);
+ Assert.assertFalse(
+ "Workflow tests require a non-empty COMMERCE_ENDPOINT.",
+ COMMERCE_ENDPOINT.isEmpty());
+ Assert.assertNotNull(
+ "Workflow tests require COMMERCE_INTEGRATION_TOKEN as a system property or environment "
+ + "variable. Pass -DCOMMERCE_INTEGRATION_TOKEN=… to Maven or export "
+ + "COMMERCE_INTEGRATION_TOKEN=….",
+ INTEGRATION_TOKEN);
+ Assert.assertFalse(
+ "Workflow tests require a non-empty COMMERCE_INTEGRATION_TOKEN.",
+ INTEGRATION_TOKEN.isEmpty());
+ }
+
+ // ---- payload helpers ------------------------------------------------
+
+ private String productSkusPayload(String... skus) {
+ StringBuilder sb = new StringBuilder("{\"productSkus\":[");
+ for (int i = 0; i < skus.length; i++) {
+ if (i > 0)
+ sb.append(",");
+ sb.append("\"").append(skus[i]).append("\"");
+ }
+ sb.append("],\"storePath\":\"").append(IT_SITE_ROOT).append("\"}");
+ return sb.toString();
+ }
+
+ private String categoryUidsPayload(String... uids) {
+ StringBuilder sb = new StringBuilder("{\"categoryUids\":[");
+ for (int i = 0; i < uids.length; i++) {
+ if (i > 0)
+ sb.append(",");
+ sb.append("\"").append(uids[i]).append("\"");
+ }
+ sb.append("],\"storePath\":\"").append(IT_SITE_ROOT).append("\"}");
+ return sb.toString();
+ }
+
+ private String cacheNamesPayload(String... names) {
+ StringBuilder sb = new StringBuilder("{\"cacheNames\":[");
+ for (int i = 0; i < names.length; i++) {
+ if (i > 0)
+ sb.append(",");
+ sb.append("\"").append(names[i]).append("\"");
+ }
+ sb.append("],\"storePath\":\"").append(IT_SITE_ROOT).append("\"}");
+ return sb.toString();
+ }
+
+ private String regexPatternsPayload(String... patterns) {
+ StringBuilder sb = new StringBuilder("{\"regexPatterns\":[");
+ for (int i = 0; i < patterns.length; i++) {
+ if (i > 0)
+ sb.append(",");
+ sb.append("\"").append(patterns[i]).append("\"");
+ }
+ sb.append("],\"storePath\":\"").append(IT_SITE_ROOT).append("\"}");
+ return sb.toString();
+ }
+
+ private String invalidateAllPayload() {
+ return "{\"invalidateAll\":true,\"storePath\":\"" + IT_SITE_ROOT + "\"}";
+ }
+
+ // ---- page / REST helpers --------------------------------------------
+
+ /**
+ * Reads the product name for {@code data.productSku} from the product collection on the
+ * category page. Tries the title span first, then the {@code title} attribute, then the
+ * data-layer JSON as a final fallback.
+ */
+ private String getProductNameFromCategoryPage(TestData data) throws ClientException {
+ SlingHttpResponse response = adminAuthor.doGet(data.categoryPageUrl, 200);
+ Document doc = Jsoup.parse(response.getContent());
+ Elements items = doc.select(".productcollection__item[data-product-sku=" + data.productSku + "]");
+ if (items.isEmpty()) {
+ return null;
+ }
+ Element item = items.first();
+ Elements titleEl = item.select(".productcollection__item-title span");
+ if (!titleEl.isEmpty()) {
+ return titleEl.first().text().trim();
+ }
+ String titleAttr = item.attr("title");
+ if (titleAttr != null && !titleAttr.isEmpty()) {
+ return titleAttr.trim();
+ }
+ String dataLayer = item.attr("data-cmp-data-layer");
+ if (dataLayer != null && !dataLayer.isEmpty()) {
+ try {
+ JsonNode json = OBJECT_MAPPER.readTree(dataLayer.replace(""", "\""));
+ JsonNode firstValue = json.fields().next().getValue();
+ if (firstValue.has("dc:title")) {
+ return firstValue.get("dc:title").asText();
+ }
+ } catch (Exception ignored) {
+ // fall through
+ }
+ }
+ return null;
+ }
+
+ private String getCategoryNameFromPage(TestData data) throws ClientException {
+ SlingHttpResponse response = adminAuthor.doGet(data.categoryPageUrl, 200);
+ Document doc = Jsoup.parse(response.getContent());
+ Elements elements = doc.select(".category__title");
+ return elements.isEmpty() ? null : elements.first().text();
+ }
+
+ /**
+ * Discovers the PDP URL for the given SKU from the product card {@code href} on the category
+ * page so tests follow whatever {@link com.adobe.cq.commerce.core.components.internal.services.UrlProviderImpl}
+ * produces on this AEM instance. Appends {@code wcmmode=disabled} to avoid the author-mode
+ * "Product name" i18n placeholder on the PDP.
+ */
+ private String discoverPdpUrl(TestData data) throws ClientException {
+ SlingHttpResponse response = adminAuthor.doGet(data.categoryPageUrl, 200);
+ Document doc = Jsoup.parse(response.getContent());
+ Elements items = doc.select(".productcollection__item[data-product-sku=" + data.productSku + "]");
+ if (items.isEmpty()) {
+ throw new AssertionError("Cannot derive PDP URL: product card for SKU "
+ + data.productSku + " not found on " + data.categoryPageUrl);
+ }
+ String href = items.first().attr("href");
+ if (href == null || href.isEmpty()) {
+ throw new AssertionError("Cannot derive PDP URL: product card for SKU "
+ + data.productSku + " has no href attribute");
+ }
+
+ return href + (href.contains("?") ? "&" : "?") + "wcmmode=disabled";
+ }
+
+ /**
+ * Reads the product name from the PDP using the same selector as {@link ProductComponentIT},
+ * {@code .productFullDetail__productName > span}.
+ */
+ private String getProductNameFromPdp(TestData data) throws ClientException {
+ SlingHttpResponse response = adminAuthor.doGet(discoverPdpUrl(data), 200);
+ Document doc = Jsoup.parse(response.getContent());
+ Elements nameEl = doc.select(".productFullDetail__productName > span");
+ return nameEl.isEmpty() ? null : nameEl.first().text().trim();
+ }
+
+ /**
+ * Verifies the PDP actually resolves to a real product (not the {@code "Product name"}
+ * i18n placeholder that AEM renders when the product context isn't loaded). Fails the
+ * test fast with a setup-vs-cache-invalidation disambiguating message so future
+ * failures aren't misdiagnosed as cache-invalidation problems.
+ */
+ private void assertPdpResolves(TestData data) throws ClientException {
+ String name = getProductNameFromPdp(data);
+ Assert.assertNotEquals(
+ "PDP for SKU " + data.productSku + " did not resolve the product — got the "
+ + "'Product name' i18n placeholder. This is an AEM URL-routing / WCM-mode "
+ + "issue, not a cache invalidation failure.",
+ "Product name", name);
+ }
+
+ /**
+ * Concatenates every breadcrumb item on the PDP. Used to verify the leaf category name
+ * appears in the product page breadcrumb after a category cache invalidation. Works only
+ * when {@code data.categoryUrlPath} is 2 segments deep (the IT site breadcrumb is
+ * configured with {@code structureDepth=2} and skips deeper leaves).
+ */
+ private String getPdpBreadcrumbText(TestData data) throws ClientException {
+ SlingHttpResponse response = adminAuthor.doGet(discoverPdpUrl(data), 200);
+ Document doc = Jsoup.parse(response.getContent());
+ Elements items = doc.select(".cmp-breadcrumb__item");
+ StringBuilder sb = new StringBuilder();
+ for (Element item : items) {
+ if (sb.length() > 0)
+ sb.append(" | ");
+ sb.append(item.text().trim());
+ }
+ return sb.toString();
+ }
+
+ private void updateProductName(String sku, String name) throws IOException {
+ String url = commerceRestBase() + "/products/" + sku;
+ String body = "{\"product\":{\"name\":\"" + name + "\"}}";
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpPut request = new HttpPut(url);
+ request.setHeader("Authorization", "Bearer " + INTEGRATION_TOKEN);
+ request.setHeader("Content-Type", "application/json");
+ request.setEntity(new StringEntity(body, ContentType.APPLICATION_JSON));
+ HttpResponse response = client.execute(request);
+ EntityUtils.consume(response.getEntity());
+ Assert.assertEquals("Magento product update (PUT /products/" + sku + ") should return 200",
+ 200, response.getStatusLine().getStatusCode());
+ }
+ }
+
+ private void updateCategoryName(int categoryId, String name) throws IOException {
+ String url = commerceRestBase() + "/categories/" + categoryId;
+ String body = "{\"category\":{\"name\":\"" + name + "\"}}";
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpPut request = new HttpPut(url);
+ request.setHeader("Authorization", "Bearer " + INTEGRATION_TOKEN);
+ request.setHeader("Content-Type", "application/json");
+ request.setEntity(new StringEntity(body, ContentType.APPLICATION_JSON));
+ HttpResponse response = client.execute(request);
+ EntityUtils.consume(response.getEntity());
+ Assert.assertEquals("Magento category update (PUT /categories/" + categoryId + ") should return 200",
+ 200, response.getStatusLine().getStatusCode());
+ }
+ }
+
+ /**
+ * Restores the Magento product name on close so mutations are always paired with cleanup,
+ * including when a test fails or is interrupted between setup steps.
+ */
+ private final class TemporaryProductName implements AutoCloseable {
+ private final TestData data;
+
+ private TemporaryProductName(TestData data) {
+ this.data = data;
+ }
+
+ @Override
+ public void close() {
+ try {
+ updateProductName(data.productSku, data.originalProductName);
+ } catch (Throwable t) {
+ LOG.warn("Failed to restore Magento product name for SKU {}: {}", data.productSku, t.toString(), t);
+ }
+ }
+ }
+
+ /**
+ * Restores the Magento category name on close; see {@link TemporaryProductName}.
+ */
+ private final class TemporaryCategoryName implements AutoCloseable {
+ private final TestData data;
+
+ private TemporaryCategoryName(TestData data) {
+ this.data = data;
+ }
+
+ @Override
+ public void close() {
+ try {
+ updateCategoryName(data.categoryId, data.originalCategoryName);
+ } catch (Throwable t) {
+ LOG.warn("Failed to restore Magento category name for id {}: {}", data.categoryId, t.toString(), t);
+ }
+ }
+ }
+
+ private TemporaryProductName temporaryProductName(TestData data, String testName) throws IOException {
+ updateProductName(data.productSku, testName);
+ return new TemporaryProductName(data);
+ }
+
+ private TemporaryCategoryName temporaryCategoryName(TestData data, String testName) throws IOException {
+ updateCategoryName(data.categoryId, testName);
+ return new TemporaryCategoryName(data);
+ }
+
+ private void invalidateProductSkuQuietly(TestData data) {
+ try {
+ postJson(CACHE_INVALIDATION_ENDPOINT, productSkusPayload(data.productSku), 200);
+ } catch (Throwable t) {
+ LOG.warn("Failed to post product SKU cache invalidation for {}: {}", data.productSku, t.toString(), t);
+ }
+ }
+
+ private void invalidateCategoryUidQuietly(TestData data) {
+ try {
+ postJson(CACHE_INVALIDATION_ENDPOINT, categoryUidsPayload(data.categoryUid), 200);
+ } catch (Throwable t) {
+ LOG.warn("Failed to post category UID cache invalidation for {}: {}", data.categoryUid, t.toString(), t);
+ }
+ }
+
+ private void invalidateAllQuietly() {
+ try {
+ postJson(CACHE_INVALIDATION_ENDPOINT, invalidateAllPayload(), 200);
+ } catch (Throwable t) {
+ LOG.warn("Failed to post invalidate-all cache invalidation: {}", t.toString(), t);
+ }
+ }
+
+ // ---- retry helpers for Catalog Service eventual-consistency lag --------------------
+
+ /**
+ * Polls AEM's category title until it matches {@code expected} or {@link #AEM_POLL_TIMEOUT_MS} elapses.
+ */
+ private void waitForCategoryTitleOnAem(TestData data, String expected) throws Exception {
+ long deadline = System.currentTimeMillis() + AEM_POLL_TIMEOUT_MS;
+ String last = null;
+ while (System.currentTimeMillis() < deadline) {
+ last = getCategoryNameFromPage(data);
+ if (expected.equals(last)) {
+ return;
+ }
+ Thread.sleep(AEM_POLL_INTERVAL_MS);
+ }
+ Assert.assertEquals("Category title did not match expected value within " + AEM_POLL_TIMEOUT_MS
+ + "ms after invalidation", expected, last);
+ }
+
+ /**
+ * Polls the PDP breadcrumb until it contains {@code expectedSubstring} or the poll timeout elapses.
+ */
+ private void waitForCategoryNameInPdpBreadcrumb(TestData data, String expectedSubstring) throws Exception {
+ long deadline = System.currentTimeMillis() + AEM_POLL_TIMEOUT_MS;
+ String last = null;
+ while (System.currentTimeMillis() < deadline) {
+ last = getPdpBreadcrumbText(data);
+ if (last != null && last.contains(expectedSubstring)) {
+ return;
+ }
+ Thread.sleep(AEM_POLL_INTERVAL_MS);
+ }
+ Assert.assertTrue("PDP breadcrumb did not contain '" + expectedSubstring + "' within " + AEM_POLL_TIMEOUT_MS
+ + "ms after invalidation (last value: " + last + ")",
+ last != null && last.contains(expectedSubstring));
+ }
+
+ /**
+ * Confirms Magento itself reflects the updated category name via REST GET. Run immediately
+ * after {@link #updateCategoryName(int, String)} so the test fails fast if the Magento
+ * write didn't actually take effect — distinguishes a Magento problem from an AEM cache
+ * problem in downstream assertions.
+ */
+ private void verifyMagentoCategoryName(int categoryId, String expectedName) throws IOException {
+ String url = commerceRestBase() + "/categories/" + categoryId;
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpGet request = new HttpGet(url);
+ request.setHeader("Authorization", "Bearer " + INTEGRATION_TOKEN);
+ HttpResponse response = client.execute(request);
+ String body = EntityUtils.toString(response.getEntity());
+ Assert.assertEquals("Magento GET /categories/" + categoryId + " should return 200",
+ 200, response.getStatusLine().getStatusCode());
+ JsonNode root = OBJECT_MAPPER.readTree(body);
+ String actualName = root.path("name").asText(null);
+ Assert.assertEquals("Magento category " + categoryId + " did not reflect the expected name",
+ expectedName, actualName);
+ }
+ }
+
+ /**
+ * Symmetric to {@link #verifyMagentoCategoryName(int, String)} — confirms Magento itself
+ * reflects the updated product name via REST GET. Run immediately after
+ * {@link #updateProductName(String, String)}.
+ */
+ private void verifyMagentoProductName(String sku, String expectedName) throws IOException {
+ String url = commerceRestBase() + "/products/" + sku;
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpGet request = new HttpGet(url);
+ request.setHeader("Authorization", "Bearer " + INTEGRATION_TOKEN);
+ HttpResponse response = client.execute(request);
+ String body = EntityUtils.toString(response.getEntity());
+ Assert.assertEquals("Magento GET /products/" + sku + " should return 200",
+ 200, response.getStatusLine().getStatusCode());
+ JsonNode root = OBJECT_MAPPER.readTree(body);
+ String actualName = root.path("name").asText(null);
+ Assert.assertEquals("Magento product " + sku + " did not reflect the expected name",
+ expectedName, actualName);
+ }
+ }
+
+ /**
+ * Polls the product card name on the category listing until it matches {@code expected} or the poll
+ * timeout elapses.
+ */
+ private void waitForProductNameOnCategoryListing(TestData data, String expected) throws Exception {
+ long deadline = System.currentTimeMillis() + AEM_POLL_TIMEOUT_MS;
+ String last = null;
+ while (System.currentTimeMillis() < deadline) {
+ last = getProductNameFromCategoryPage(data);
+ if (expected.equals(last)) {
+ return;
+ }
+ Thread.sleep(AEM_POLL_INTERVAL_MS);
+ }
+ Assert.assertEquals(
+ "Category-page product name did not match expected value within " + AEM_POLL_TIMEOUT_MS
+ + "ms after invalidation",
+ expected, last);
+ }
+
+ /**
+ * Polls the PDP product name until it matches {@code expected} or the poll timeout elapses.
+ */
+ private void waitForProductNameOnPdp(TestData data, String expected) throws Exception {
+ long deadline = System.currentTimeMillis() + AEM_POLL_TIMEOUT_MS;
+ String last = null;
+ while (System.currentTimeMillis() < deadline) {
+ last = getProductNameFromPdp(data);
+ if (expected.equals(last)) {
+ return;
+ }
+ Thread.sleep(AEM_POLL_INTERVAL_MS);
+ }
+ Assert.assertEquals(
+ "PDP product name did not match expected value within " + AEM_POLL_TIMEOUT_MS + "ms after invalidation",
+ expected, last);
+ }
+
+ // ============================================================================================
+ // SERVLET AVAILABILITY TESTS — single set, no Magento writes
+ // ============================================================================================
+
+ /** Servlet is reachable — 200 (success) or 400 (bad request); 404/500 fail the test. */
+ @Test
+ public void testServletReachable() throws Exception {
+ postJson(CACHE_INVALIDATION_ENDPOINT, invalidateAllPayload(), 200, 400);
+ }
+
+ /** Servlet accepts the {@code productSkus} payload. */
+ @Test
+ public void testInvalidateByProductSkus() throws Exception {
+ SlingHttpResponse response = postJson(CACHE_INVALIDATION_ENDPOINT,
+ productSkusPayload(CLASSIC.productSku), 200);
+ Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+ }
+
+ /** Servlet accepts the {@code categoryUids} payload. */
+ @Test
+ public void testInvalidateByCategoryUids() throws Exception {
+ SlingHttpResponse response = postJson(CACHE_INVALIDATION_ENDPOINT,
+ categoryUidsPayload(CLASSIC.categoryUid), 200);
+ Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+ }
+
+ /** Servlet accepts the {@code cacheNames} payload. */
+ @Test
+ public void testInvalidateByCacheNames() throws Exception {
+ SlingHttpResponse response = postJson(CACHE_INVALIDATION_ENDPOINT,
+ cacheNamesPayload(
+ "cif-components-it-site/components/commerce/productlist",
+ "cif-components-it-site/components/commerce/navigation"),
+ 200);
+ Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+ }
+
+ /** Servlet accepts the {@code regexPatterns} payload. */
+ @Test
+ public void testInvalidateByRegexPatterns() throws Exception {
+ SlingHttpResponse response = postJson(CACHE_INVALIDATION_ENDPOINT,
+ regexPatternsPayload("\\\"sku\\\":\\\\s*\\\"" + CLASSIC.productSku + "\\\""), 200);
+ Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+ }
+
+ /** Servlet accepts the {@code invalidateAll} payload. */
+ @Test
+ public void testInvalidateAll() throws Exception {
+ SlingHttpResponse response = postJson(CACHE_INVALIDATION_ENDPOINT, invalidateAllPayload(), 200);
+ Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+ }
+
+ // ============================================================================================
+ // WORKFLOW HELPERS — five workflows, each parameterised by a TestData fixture
+ // ============================================================================================
+
+ private void runProductSkusWorkflow(TestData data) throws Exception {
+ requireCommerceCredentials();
+ assertPdpResolves(data);
+ String originalNameOnCategory = getProductNameFromCategoryPage(data);
+ Assert.assertNotNull("Category page should render product " + data.productSku, originalNameOnCategory);
+ String originalNameOnPdp = getProductNameFromPdp(data);
+ Assert.assertNotNull("PDP should render " + data.productSku + " with a name", originalNameOnPdp);
+
+ String testName = "CIF-IT-" + data.productSku + "-" + System.currentTimeMillis();
+ try (TemporaryProductName ignored = temporaryProductName(data, testName)) {
+ verifyMagentoProductName(data.productSku, testName);
+ Assert.assertEquals("Category listing should serve stale cached name before invalidation",
+ originalNameOnCategory, getProductNameFromCategoryPage(data));
+ Assert.assertEquals("PDP should serve stale cached name before invalidation",
+ originalNameOnPdp, getProductNameFromPdp(data));
+
+ postJson(CACHE_INVALIDATION_ENDPOINT, productSkusPayload(data.productSku), 200);
+
+ waitForProductNameOnCategoryListing(data, testName);
+ waitForProductNameOnPdp(data, testName);
+
+ } finally {
+ invalidateProductSkuQuietly(data);
+ }
+ }
+
+ private void runCategoryUidsWorkflow(TestData data) throws Exception {
+ requireCommerceCredentials();
+ assertPdpResolves(data);
+ String originalCategoryName = getCategoryNameFromPage(data);
+ Assert.assertNotNull("Category page should render a category name", originalCategoryName);
+ Assert.assertTrue("PDP breadcrumb should initially contain category '" + originalCategoryName + "'",
+ getPdpBreadcrumbText(data).contains(originalCategoryName));
+
+ String testName = "CIF-IT-Cat-" + data.categoryId + "-" + System.currentTimeMillis();
+ try (TemporaryCategoryName ignored = temporaryCategoryName(data, testName)) {
+ verifyMagentoCategoryName(data.categoryId, testName);
+ Assert.assertEquals("Category title should serve stale cached name before invalidation",
+ originalCategoryName, getCategoryNameFromPage(data));
+ Assert.assertTrue("PDP breadcrumb should still contain stale category name before invalidation",
+ getPdpBreadcrumbText(data).contains(originalCategoryName));
+
+ postJson(CACHE_INVALIDATION_ENDPOINT, categoryUidsPayload(data.categoryUid), 200);
+
+ waitForCategoryTitleOnAem(data, testName);
+ waitForCategoryNameInPdpBreadcrumb(data, testName);
+
+ } finally {
+ invalidateCategoryUidQuietly(data);
+ }
+ }
+
+ private void runCacheNamesWorkflow(TestData data) throws Exception {
+ requireCommerceCredentials();
+ assertPdpResolves(data);
+ String originalNameOnCategory = getProductNameFromCategoryPage(data);
+ Assert.assertNotNull("Category page should render product " + data.productSku, originalNameOnCategory);
+ String originalNameOnPdp = getProductNameFromPdp(data);
+ Assert.assertNotNull("PDP should render " + data.productSku + " with a name", originalNameOnPdp);
+
+ String testName = "CIF-IT-CN-" + data.productSku + "-" + System.currentTimeMillis();
+ try (TemporaryProductName ignored = temporaryProductName(data, testName)) {
+ verifyMagentoProductName(data.productSku, testName);
+ Assert.assertEquals("Category listing should serve stale cached name before cache-name invalidation",
+ originalNameOnCategory, getProductNameFromCategoryPage(data));
+ Assert.assertEquals("PDP should serve stale cached name before cache-name invalidation",
+ originalNameOnPdp, getProductNameFromPdp(data));
+
+ postJson(CACHE_INVALIDATION_ENDPOINT,
+ cacheNamesPayload(
+ "cif-components-it-site/components/commerce/productlist",
+ "cif-components-it-site/components/commerce/product"),
+ 200);
+
+ waitForProductNameOnCategoryListing(data, testName);
+ waitForProductNameOnPdp(data, testName);
+
+ } finally {
+ invalidateProductSkuQuietly(data);
+ }
+ }
+
+ private void runInvalidateAllWorkflow(TestData data) throws Exception {
+ requireCommerceCredentials();
+ assertPdpResolves(data);
+ String originalProductOnCategory = getProductNameFromCategoryPage(data);
+ Assert.assertNotNull("Category page should render product " + data.productSku, originalProductOnCategory);
+ String originalProductOnPdp = getProductNameFromPdp(data);
+ Assert.assertNotNull("PDP should render " + data.productSku + " with a name", originalProductOnPdp);
+ String originalCategoryName = getCategoryNameFromPage(data);
+ Assert.assertNotNull("Category page should render a category name", originalCategoryName);
+ Assert.assertTrue("PDP breadcrumb should initially contain category '" + originalCategoryName + "'",
+ getPdpBreadcrumbText(data).contains(originalCategoryName));
+
+ String testProductName = "CIF-IT-AllP-" + data.productSku + "-" + System.currentTimeMillis();
+ String testCategoryName = "CIF-IT-AllC-" + data.categoryId + "-" + System.currentTimeMillis();
+ try (TemporaryProductName ignoredProduct = temporaryProductName(data, testProductName);
+ TemporaryCategoryName ignoredCategory = temporaryCategoryName(data, testCategoryName)) {
+ verifyMagentoProductName(data.productSku, testProductName);
+ verifyMagentoCategoryName(data.categoryId, testCategoryName);
+ Assert.assertEquals("Category listing should serve stale product name before invalidateAll",
+ originalProductOnCategory, getProductNameFromCategoryPage(data));
+ Assert.assertEquals("PDP should serve stale product name before invalidateAll",
+ originalProductOnPdp, getProductNameFromPdp(data));
+ Assert.assertEquals("Category title should be stale before invalidateAll",
+ originalCategoryName, getCategoryNameFromPage(data));
+ Assert.assertTrue("PDP breadcrumb should still contain stale category name before invalidateAll",
+ getPdpBreadcrumbText(data).contains(originalCategoryName));
+
+ postJson(CACHE_INVALIDATION_ENDPOINT, invalidateAllPayload(), 200);
+
+ waitForProductNameOnCategoryListing(data, testProductName);
+ waitForProductNameOnPdp(data, testProductName);
+ waitForCategoryTitleOnAem(data, testCategoryName);
+ waitForCategoryNameInPdpBreadcrumb(data, testCategoryName);
+
+ } finally {
+ invalidateAllQuietly();
+ }
+ }
+
+ private void runRegexPatternsWorkflow(TestData data) throws Exception {
+ requireCommerceCredentials();
+ assertPdpResolves(data);
+ String originalNameOnCategory = getProductNameFromCategoryPage(data);
+ Assert.assertNotNull("Category page should render product " + data.productSku, originalNameOnCategory);
+ String originalNameOnPdp = getProductNameFromPdp(data);
+ Assert.assertNotNull("PDP should render " + data.productSku + " with a name", originalNameOnPdp);
+
+ String testName = "CIF-IT-RX-" + data.productSku + "-" + System.currentTimeMillis();
+ try (TemporaryProductName ignored = temporaryProductName(data, testName)) {
+ verifyMagentoProductName(data.productSku, testName);
+ Assert.assertEquals("Category listing should serve stale cached name before regex invalidation",
+ originalNameOnCategory, getProductNameFromCategoryPage(data));
+ Assert.assertEquals("PDP should serve stale cached name before regex invalidation",
+ originalNameOnPdp, getProductNameFromPdp(data));
+
+ postJson(CACHE_INVALIDATION_ENDPOINT,
+ regexPatternsPayload("\\\"sku\\\":\\\\s*\\\"" + data.productSku + "\\\""), 200);
+
+ waitForProductNameOnCategoryListing(data, testName);
+ waitForProductNameOnPdp(data, testName);
+
+ } finally {
+ invalidateProductSkuQuietly(data);
+ }
+ }
+
+ // ============================================================================================
+ // CLASSIC / AEM 6.5 — VT01 Penelope Peasant Blouse
+ // ============================================================================================
+
+ @Test
+ @Category({ IgnoreOnCloud.class, IgnoreOnLts.class })
+ public void test65_ProductSkusWorkflow() throws Exception {
+ runProductSkusWorkflow(CLASSIC);
+ }
+
+ @Test
+ @Category({ IgnoreOnCloud.class, IgnoreOnLts.class })
+ public void test65_CategoryUidsWorkflow() throws Exception {
+ runCategoryUidsWorkflow(CLASSIC);
+ }
+
+ @Test
+ @Category({ IgnoreOnCloud.class, IgnoreOnLts.class })
+ public void test65_CacheNamesWorkflow() throws Exception {
+ runCacheNamesWorkflow(CLASSIC);
+ }
+
+ @Test
+ @Category({ IgnoreOnCloud.class, IgnoreOnLts.class })
+ public void test65_InvalidateAllWorkflow() throws Exception {
+ runInvalidateAllWorkflow(CLASSIC);
+ }
+
+ @Test
+ @Category({ IgnoreOnCloud.class, IgnoreOnLts.class })
+ public void test65_RegexPatternsWorkflow() throws Exception {
+ runRegexPatternsWorkflow(CLASSIC);
+ }
+
+ // ============================================================================================
+ // LTS — VP01 Selena Pants
+ // ============================================================================================
+
+ @Test
+ @Category({ IgnoreOn65.class, IgnoreOnCloud.class })
+ public void testLts_ProductSkusWorkflow() throws Exception {
+ runProductSkusWorkflow(LTS);
+ }
+
+ @Test
+ @Category({ IgnoreOn65.class, IgnoreOnCloud.class })
+ public void testLts_CategoryUidsWorkflow() throws Exception {
+ runCategoryUidsWorkflow(LTS);
+ }
+
+ @Test
+ @Category({ IgnoreOn65.class, IgnoreOnCloud.class })
+ public void testLts_CacheNamesWorkflow() throws Exception {
+ runCacheNamesWorkflow(LTS);
+ }
+
+ @Test
+ @Category({ IgnoreOn65.class, IgnoreOnCloud.class })
+ public void testLts_InvalidateAllWorkflow() throws Exception {
+ runInvalidateAllWorkflow(LTS);
+ }
+
+ @Test
+ @Category({ IgnoreOn65.class, IgnoreOnCloud.class })
+ public void testLts_RegexPatternsWorkflow() throws Exception {
+ runRegexPatternsWorkflow(LTS);
+ }
+
+ // ============================================================================================
+ // CLOUD — VA01 Dulcea Infinity Scarf
+ // ============================================================================================
+
+ @Test
+ @Category({ IgnoreOn65.class, IgnoreOnLts.class })
+ public void testCloud_ProductSkusWorkflow() throws Exception {
+ runProductSkusWorkflow(CLOUD);
+ }
+
+ @Test
+ @Category({ IgnoreOn65.class, IgnoreOnLts.class })
+ public void testCloud_CategoryUidsWorkflow() throws Exception {
+ runCategoryUidsWorkflow(CLOUD);
+ }
+
+ @Test
+ @Category({ IgnoreOn65.class, IgnoreOnLts.class })
+ public void testCloud_CacheNamesWorkflow() throws Exception {
+ runCacheNamesWorkflow(CLOUD);
+ }
+
+ @Test
+ @Category({ IgnoreOn65.class, IgnoreOnLts.class })
+ public void testCloud_InvalidateAllWorkflow() throws Exception {
+ runInvalidateAllWorkflow(CLOUD);
+ }
+
+ @Test
+ @Category({ IgnoreOn65.class, IgnoreOnLts.class })
+ public void testCloud_RegexPatternsWorkflow() throws Exception {
+ runRegexPatternsWorkflow(CLOUD);
+ }
+}
diff --git a/it/http/src/test/java/com/adobe/cq/commerce/it/http/CommerceTestBase.java b/it/http/src/test/java/com/adobe/cq/commerce/it/http/CommerceTestBase.java
index 78a87d0bd7..757b04bbd0 100644
--- a/it/http/src/test/java/com/adobe/cq/commerce/it/http/CommerceTestBase.java
+++ b/it/http/src/test/java/com/adobe/cq/commerce/it/http/CommerceTestBase.java
@@ -16,20 +16,17 @@
package com.adobe.cq.commerce.it.http;
import java.io.IOException;
+import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeoutException;
import org.apache.commons.io.IOUtils;
import org.apache.sling.testing.clients.ClientException;
-import org.apache.sling.testing.clients.SlingHttpResponse;
import org.apache.sling.testing.clients.osgi.OsgiConsoleClient;
-import org.apache.sling.testing.clients.util.JsonUtils;
import org.apache.sling.testing.clients.util.poller.Polling;
-import org.codehaus.jackson.JsonNode;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
@@ -71,39 +68,24 @@ public class CommerceTestBase {
public static void init() throws ClientException, InterruptedException, TimeoutException {
adminAuthor = cqBaseClassRule.authorRule.getAdminClient(CommerceClient.class);
- // This configures the GraphQL client for the CIF components library
+ // This configures the GraphQL client for the CIF components library.
+ // Use the server's own URL so the mock endpoint is reachable regardless of which port AEM is on.
+ URI serverUri = adminAuthor.getUrl();
+ String graphqlMockUrl = serverUri.getScheme() + "://" + serverUri.getHost() + ":" + serverUri.getPort()
+ + "/apps/cif-components-examples/graphql";
+
GraphqlOSGiConfig graphqlOsgiConfig = new GraphqlOSGiConfig()
.withIdentifier("examples")
- .withUrl("http://localhost:4502/apps/cif-components-examples/graphql")
+ .withUrl(graphqlMockUrl)
.withHttpMethod("GET")
.withAcceptSelfSignedCertificates(true)
.withAllowHttpProtocol(true);
- updateOSGiConfiguration(adminAuthor, graphqlOsgiConfig.build(), GRAPHQL_CLIENT_BUNDLE, GRAPHQL_CLIENT_FACTORY_PID);
+ updateOSGiConfiguration(adminAuthor, graphqlOsgiConfig.build(), GRAPHQL_CLIENT_BUNDLE,
+ GRAPHQL_CLIENT_FACTORY_PID + "~examples");
updateSlingAuthenticatorOSGiConfig(adminAuthor);
}
- /**
- * Fetches the PID of a service based on the factory PID.
- *
- * @param osgiClient
- * @return The PID of the first configuration found for factory PID.
- * @throws ClientException
- */
- private static String getConfigurationPid(OsgiConsoleClient osgiClient, String factoryPID) throws ClientException {
- SlingHttpResponse resp = osgiClient.doGet(CONFIGURATION_CONSOLE_URL + "/*.json");
- JsonNode json = JsonUtils.getJsonNodeFromString(resp.getContent());
- Iterator it = json.getElements();
- while (it.hasNext()) {
- JsonNode config = it.next();
- JsonNode factoryId = config.get("factoryPid");
- if (factoryId != null && factoryPID.equals(factoryId.getTextValue())) {
- return config.get("pid").getTextValue();
- }
- }
- return null;
- }
-
protected static void updateOSGiConfiguration(CQClient client, Map config, String bundle, String factoryPID)
throws ClientException,
TimeoutException, InterruptedException {
@@ -129,8 +111,7 @@ public Boolean call() throws Exception {
polling.poll(30000, 1000);
LOG.info("Creating configuration. {}", config);
- String configurationPid = getConfigurationPid(osgiClient, factoryPID);
- osgiClient.waitEditConfiguration(30, configurationPid, null, config, SC_MOVED_TEMPORARILY);
+ osgiClient.waitEditConfiguration(30, factoryPID, null, config, SC_MOVED_TEMPORARILY);
// Wait for bundle to restart
polling.poll(30000, 1000);
diff --git a/it/http/src/test/java/com/adobe/cq/commerce/it/http/ItSiteSmokeIT.java b/it/http/src/test/java/com/adobe/cq/commerce/it/http/ItSiteSmokeIT.java
new file mode 100644
index 0000000000..842eb56dd8
--- /dev/null
+++ b/it/http/src/test/java/com/adobe/cq/commerce/it/http/ItSiteSmokeIT.java
@@ -0,0 +1,58 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Copyright 2025 Adobe
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package com.adobe.cq.commerce.it.http;
+
+import org.apache.sling.testing.clients.ClientException;
+import org.jsoup.nodes.Document;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+public class ItSiteSmokeIT extends ItSiteTestBase {
+
+ private static final String HEADER_XF_MODEL = IT_SITE_XF_ROOT + "/header/master.model.json";
+
+ @Test
+ public void testHomePageLoads() throws ClientException {
+ Document doc = getPage(IT_SITE_ROOT);
+ Assert.assertTrue("Page h1 should contain 'CIF IT Site'",
+ doc.select("h1.cmp-title__text").first().text().contains("CIF IT Site"));
+ }
+
+ @Test
+ public void testNavigationRendered() throws ClientException {
+ Document doc = getPage(IT_SITE_ROOT);
+ Assert.assertEquals("Navigation should have 6 first-level items",
+ 6, doc.select("li.cmp-navigation__item--level-0").size());
+ }
+
+ @Test
+ public void testCommerceNavigationConfigured() throws Exception {
+ JsonNode navigation = getJson(HEADER_XF_MODEL).at("/:items/root/:items/navigation");
+ Assert.assertFalse("Navigation component should exist at the expected model path",
+ navigation.isMissingNode());
+ Assert.assertEquals("Navigation should be the CIF commerce navigation component",
+ "cif-components-it-site/components/commerce/navigation", navigation.get(":type").asText());
+ }
+
+ @Test
+ public void testCommerceGraphqlEndpointReachable() throws Exception {
+ JsonNode json = executeGraphql("{storeConfig{store_code}}");
+ Assert.assertEquals("GraphQL endpoint should return store_code 'default'",
+ "default", json.at("/data/storeConfig/store_code").asText());
+ }
+}
diff --git a/it/http/src/test/java/com/adobe/cq/commerce/it/http/ItSiteTestBase.java b/it/http/src/test/java/com/adobe/cq/commerce/it/http/ItSiteTestBase.java
new file mode 100644
index 0000000000..f6c8f1f9a0
--- /dev/null
+++ b/it/http/src/test/java/com/adobe/cq/commerce/it/http/ItSiteTestBase.java
@@ -0,0 +1,105 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Copyright 2026 Adobe
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package com.adobe.cq.commerce.it.http;
+
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.sling.testing.clients.ClientException;
+import org.apache.sling.testing.clients.SlingHttpResponse;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+
+import com.adobe.cq.testing.client.CQClient;
+import com.adobe.cq.testing.junit.rules.CQAuthorClassRule;
+import com.adobe.cq.testing.junit.rules.CQRule;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Base class for integration tests targeting the CIF IT Site
+ * at {@code /content/cif-components-it-site}.
+ *
+ * Provides shared client setup and request utilities (page HTML, JSON model,
+ * GraphQL) so individual test classes stay focused on assertions.
+ */
+public class ItSiteTestBase {
+
+ protected static final String IT_SITE_ROOT = "/content/cif-components-it-site/us/en";
+ protected static final String IT_SITE_XF_ROOT = "/content/experience-fragments/cif-components-it-site/us/en/site";
+ protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ @ClassRule
+ public static final CQAuthorClassRule cqBaseClassRule = new CQAuthorClassRule();
+
+ @Rule
+ public CQRule cqBaseRule = new CQRule(cqBaseClassRule.authorRule);
+
+ protected static CQClient adminAuthor;
+
+ @BeforeClass
+ public static void initBase() throws ClientException {
+ adminAuthor = cqBaseClassRule.authorRule.getAdminClient(CQClient.class);
+ }
+
+ /**
+ * Fetches a page and returns its HTML parsed as a Jsoup Document.
+ *
+ * @param pagePath JCR path without extension, e.g. {@code /content/cif-components-it-site/us/en}
+ */
+ protected Document getPage(String pagePath) throws ClientException {
+ SlingHttpResponse response = adminAuthor.doGet(pagePath + ".html", 200);
+ return Jsoup.parse(response.getContent());
+ }
+
+ /**
+ * Fetches a Sling model JSON export and returns the parsed JsonNode.
+ *
+ * @param modelPath full path including extension, e.g. {@code /content/.../master.model.json}
+ */
+ protected JsonNode getJson(String modelPath) throws Exception {
+ SlingHttpResponse response = adminAuthor.doGet(modelPath, 200);
+ return OBJECT_MAPPER.readTree(response.getContent());
+ }
+
+ /**
+ * Executes a GraphQL query against the IT site endpoint and returns the parsed response.
+ *
+ * @param query raw GraphQL query string, e.g. {@code {storeConfig{store_code}}}
+ */
+ protected JsonNode executeGraphql(String query) throws Exception {
+ String encoded = URLEncoder.encode(query, StandardCharsets.UTF_8.name());
+ SlingHttpResponse response = adminAuthor.doGet("/api/graphql?query=" + encoded, 200);
+ return OBJECT_MAPPER.readTree(response.getContent());
+ }
+
+ /**
+ * Posts a JSON body to the given path and returns the parsed response.
+ *
+ * @param path JCR/servlet path, e.g. {@code /bin/cif/invalidate-cache}
+ * @param jsonBody raw JSON string to send as request body
+ * @param expectedStatus HTTP status codes to accept (vararg — pass none to skip status check)
+ */
+ protected SlingHttpResponse postJson(String path, String jsonBody, int... expectedStatus) throws ClientException {
+ StringEntity entity = new StringEntity(jsonBody, ContentType.APPLICATION_JSON);
+ return adminAuthor.doPost(path, entity, null, expectedStatus);
+ }
+}
diff --git a/it/http/src/test/java/junit/category/IgnoreOnLts.java b/it/http/src/test/java/junit/category/IgnoreOnLts.java
new file mode 100644
index 0000000000..0beb771193
--- /dev/null
+++ b/it/http/src/test/java/junit/category/IgnoreOnLts.java
@@ -0,0 +1,22 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Copyright 2026 Adobe
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package junit.category;
+
+/**
+ * JUnit category for tests that must not run on AEM LTS. Annotate with
+ * {@code @Category(IgnoreOnLts.class)}; CI excludes this category when {@code AEM=lts}.
+ */
+public interface IgnoreOnLts {}
diff --git a/it/site/README.md b/it/site/README.md
new file mode 100644
index 0000000000..b8178aacb4
--- /dev/null
+++ b/it/site/README.md
@@ -0,0 +1,128 @@
+# CIF IT Site (`it/site`)
+
+Integration-test site for **AEM CIF Core Components**, living inside the `aem-core-cif-components` monorepo under `it/site/`. It is a **FileVault-only** layout (no local OSGi `core` bundle, no `ui.frontend` build, no `it.tests` / `ui.tests`). See **`generate.md`** for how this site was originally created and how to recreate it for a different project.
+
+## Modules
+
+* **ui.apps** — `/apps` code: components, clientlibs, HTL, etc.
+* **ui.apps.structure** — repository root filters for package validation (declared in `pom.xml`, no `src/main/content`).
+* **ui.content** — mutable content, templates under `/conf`, sample pages.
+* **ui.config** — OSGi configurations (cloud). GraphQL URL is set at runtime (see [GraphQL endpoint](#graphql-endpoint)).
+* **all** — container package embedding the site's `ui.apps`, `ui.content`, and `ui.config`.
+* **classic/all** (reactor profile **`classic`**) — 6.5 / AMS mixed package embedding site + classic overlays + CIF Core + WCM Core.
+
+## Local environment setup
+
+This module does **not** embed CIF Core artifacts. The following three things must be installed on your local AEM SDK in order, before the site works correctly.
+
+### Step 1 — Install CIF Core Components
+
+Build and install the main repo `all` package from the **monorepo root**. This installs the `core-cif-components-core` OSGi bundle, `core-cif-components-apps`, and `core-cif-components-config`:
+
+```bash
+# from the monorepo root
+mvn clean install -PautoInstallSinglePackage -Dskip-it
+```
+
+### Step 2 — Install `magento-graphql` bundle
+
+`core-cif-components-core` imports `com.adobe.cq.commerce.magento.graphql` from a standalone JAR that is not embedded in any package. Without it the core bundle stays in `Installed` state and no CIF React components (navigation, cart, etc.) will render.
+
+After step 1, the JAR is in your local Maven repository. Install it via the Felix console (`/system/console/bundles` → **Install/Update**):
+
+```
+~/.m2/repository/com/adobe/commerce/cif/magento-graphql/9.1.0-magento242ee/magento-graphql-9.1.0-magento242ee.jar
+```
+
+Or with curl:
+
+```bash
+curl -u admin:admin \
+ -F action=install \
+ -F bundlestartlevel=20 \
+ -F bundlefile=@~/.m2/repository/com/adobe/commerce/cif/magento-graphql/9.1.0-magento242ee/magento-graphql-9.1.0-magento242ee.jar \
+ http://localhost:4502/system/console/bundles
+```
+
+Confirm `com.adobe.commerce.cif.core-cif-components-core` shows **Active** at `/system/console/bundles` before continuing.
+
+### Step 3 — Install the IT Site
+
+```bash
+# from it/site/
+mvn clean install -PautoInstallSinglePackage
+```
+
+This installs `ui.apps`, `ui.content`, and `ui.config` for the test site via the `all` container package.
+
+After all three steps, open `http://localhost:4502/content/cif-components-it-site/us/en.html` to verify the site loads with navigation and header.
+
+## Build
+
+### From `it/site/` directly
+
+```bash
+# Cloud only
+mvn clean install
+
+# Cloud + classic (AEM 6.5 / AMS)
+mvn clean install -Pclassic
+```
+
+### From the monorepo root
+
+```bash
+# Build only it/site and its upstream dependencies
+mvn clean install -pl it/site -am
+
+# Skip it/site and all integration-test modules
+mvn clean install -Dskip-it
+```
+
+## Testing
+
+HTTP integration tests for commerce components live in `../http` (`it/http`). Run them against a running AEM author:
+
+```bash
+# from the monorepo root
+mvn clean verify -pl it/http -am -Dit
+```
+
+See `it/http/README.md` for override properties (`aem.host`, `aem.port`, `it.commerce.library.path`).
+
+## GraphQL endpoint
+
+`GraphqlClientImpl~default` drives IT site commerce pages (`ui.config` / `classic/ui.config`). The package does **not** ship a Catalog Service URL — only OSGi interpolation and **`httpMethod: POST`**:
+
+```json
+"httpMethod": "POST",
+"url": "$[env:COMMERCE_ENDPOINT;default=]"
+```
+
+Use the full GraphQL URL (`https://…/graphql`). Do not use `${COMMERCE_ENDPOINT}` in `.cfg.json`; that syntax is for Cloud Manager deploy-time substitution, not AEM OSGi.
+
+### CircleCI / local Quickstart
+
+After AEM is ready, `.circleci/ci/it-tests.js` applies IT site OSGi via Felix `configMgr` (same pattern as `GraphqlClientImpl~examples`): full `GraphqlClientImpl~default` settings from `ui.config` with `url` taken from CircleCI `COMMERCE_ENDPOINT`, plus `UrlProviderImpl` (`url_path`, context-aware). This is needed because the IT site `ui.config` content package is not always picked up on the pipeline AEM instance.
+
+Changing backends does not require a code change — only that pipeline variable.
+
+### Local development
+
+* **Cloud SDK** — `export COMMERCE_ENDPOINT=https://…/graphql` before starting AEM, or set `url` in `/system/console/configMgr` for `GraphqlClientImpl~default`.
+* **AEM 6.5 / AMS** (`-Pclassic`) — `$[env:…]` is not applied on 6.5; use the CI curl step above or override the factory config in OSGi.
+* **AEM as a Cloud Service** — define `COMMERCE_ENDPOINT` in Cloud Manager; the `$[env:COMMERCE_ENDPOINT;default=]` placeholder is resolved on the AEM JVM.
+
+### HTTP integration tests
+
+The same `COMMERCE_ENDPOINT` is passed to Maven for `CacheInvalidationIT` (Magento REST). See `it/http/README.md` for overrides.
+
+## ClientLibs
+
+There is no `ui.frontend` webpack/npm build step. CSS is a single static file committed directly to `ui.apps`:
+
+```
+ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-site/css/site.css
+```
+
+To update styles, edit that file directly and redeploy `ui.apps`.
diff --git a/it/site/all/pom.xml b/it/site/all/pom.xml
new file mode 100644
index 0000000000..8a391c6b6d
--- /dev/null
+++ b/it/site/all/pom.xml
@@ -0,0 +1,183 @@
+
+
+ 4.0.0
+
+
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site
+ 1.0.0-SNAPSHOT
+ ../pom.xml
+
+
+
+
+
+ cif-components-it-site.all
+ content-package
+ CIF IT Site - All
+ All content package for CIF IT Site
+
+
+
+
+
+
+
+
+
+
+ org.apache.jackrabbit
+ filevault-package-maven-plugin
+ true
+
+ com.adobe.commerce.cif
+ container
+
+ true
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.apps
+ zip
+ /apps/cif-components-it-site-packages/application/install
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.content
+ zip
+ /apps/cif-components-it-site-packages/content/install
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.config
+ zip
+ /apps/cif-components-it-site-packages/application/install
+
+
+
+
+
+ com.day.jcr.vault
+ content-package-maven-plugin
+ true
+
+ true
+ true
+
+
+
+ maven-clean-plugin
+
+
+ auto-clean
+ initialize
+
+ clean
+
+
+
+
+
+
+
+
+
+
+
+
+ autoInstallSinglePackage
+
+ false
+
+
+
+
+ com.day.jcr.vault
+ content-package-maven-plugin
+
+
+ install-package
+
+ install
+
+
+ http://${aem.host}:${aem.port}/crx/packmgr/service.jsp
+ true
+
+
+
+
+
+
+
+
+ autoInstallSinglePackagePublish
+
+ false
+
+
+
+
+ com.day.jcr.vault
+ content-package-maven-plugin
+
+
+ install-package-publish
+
+ install
+
+
+ http://${aem.publish.host}:${aem.publish.port}/crx/packmgr/service.jsp
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.apps
+ ${project.version}
+ zip
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.content
+ ${project.version}
+ zip
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.config
+ ${project.version}
+ zip
+
+
+
diff --git a/it/site/all/src/main/content/META-INF/vault/definition/.content.xml b/it/site/all/src/main/content/META-INF/vault/definition/.content.xml
new file mode 100644
index 0000000000..1fbbe71167
--- /dev/null
+++ b/it/site/all/src/main/content/META-INF/vault/definition/.content.xml
@@ -0,0 +1,23 @@
+
+
+
+
diff --git a/it/site/all/src/main/content/META-INF/vault/filter.xml b/it/site/all/src/main/content/META-INF/vault/filter.xml
new file mode 100644
index 0000000000..9e57b59373
--- /dev/null
+++ b/it/site/all/src/main/content/META-INF/vault/filter.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/it/site/classic/all/pom.xml b/it/site/classic/all/pom.xml
new file mode 100644
index 0000000000..cca060c454
--- /dev/null
+++ b/it/site/classic/all/pom.xml
@@ -0,0 +1,240 @@
+
+
+
+ 4.0.0
+
+
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+
+
+
+
+ cif-components-it-site.all-classic
+ content-package
+ CIF IT Site - All (classic)
+ All content package for CIF IT Site (classic)
+
+
+
+
+
+
+
+
+
+
+ org.apache.jackrabbit
+ filevault-package-maven-plugin
+ true
+
+ com.adobe.commerce.cif
+ cif-components-it-site.all
+ mixed
+ true
+
+ true
+
+ all
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.apps
+ zip
+ /apps/cif-components-it-site-packages/application/install
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.content
+ zip
+ /apps/cif-components-it-site-packages/content/install
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.config
+ zip
+ /apps/cif-components-it-site-packages/application/install
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.content-classic
+ zip
+ /apps/cif-components-it-site-packages/content/install
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.config-classic
+ zip
+ /apps/cif-components-it-site-packages/application/install
+
+
+ com.adobe.cq
+ core.wcm.components.content
+ zip
+ /apps/cif-components-it-site-vendor-packages/application/install
+
+
+ com.adobe.cq
+ core.wcm.components.config
+ zip
+ /apps/cif-components-it-site-vendor-packages/application/install
+
+
+ com.adobe.cq
+ core.wcm.components.core
+ /apps/cif-components-it-site-vendor-packages/application/install
+
+
+
+
+
+ com.day.jcr.vault
+ content-package-maven-plugin
+ true
+
+
+ maven-clean-plugin
+
+
+ auto-clean
+ initialize
+
+ clean
+
+
+
+
+
+
+
+
+
+
+
+
+ autoInstallSinglePackage
+
+ false
+
+
+
+
+ com.day.jcr.vault
+ content-package-maven-plugin
+
+
+ install-package
+
+ install
+
+
+ http://${aem.host}:${aem.port}/crx/packmgr/service.jsp
+ true
+
+
+
+
+
+
+
+
+ autoInstallSinglePackagePublish
+
+ false
+
+
+
+
+ com.day.jcr.vault
+ content-package-maven-plugin
+
+
+ install-package-publish
+
+ install
+
+
+ http://${aem.publish.host}:${aem.publish.port}/crx/packmgr/service.jsp
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.apps
+ ${project.version}
+ zip
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.content
+ ${project.version}
+ zip
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.config
+ ${project.version}
+ zip
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.content-classic
+ ${project.version}
+ zip
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.config-classic
+ ${project.version}
+ zip
+
+
+
+
+ com.adobe.cq
+ core.wcm.components.content
+ zip
+
+
+ com.adobe.cq
+ core.wcm.components.config
+ zip
+
+
+ com.adobe.cq
+ core.wcm.components.core
+
+
+
diff --git a/it/site/classic/all/src/main/content/META-INF/vault/definition/.content.xml b/it/site/classic/all/src/main/content/META-INF/vault/definition/.content.xml
new file mode 100644
index 0000000000..c2fc9bc478
--- /dev/null
+++ b/it/site/classic/all/src/main/content/META-INF/vault/definition/.content.xml
@@ -0,0 +1,23 @@
+
+
+
+
diff --git a/it/site/classic/all/src/main/content/META-INF/vault/filter.xml b/it/site/classic/all/src/main/content/META-INF/vault/filter.xml
new file mode 100644
index 0000000000..7c8abd04e5
--- /dev/null
+++ b/it/site/classic/all/src/main/content/META-INF/vault/filter.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/it/site/classic/ui.config/pom.xml b/it/site/classic/ui.config/pom.xml
new file mode 100644
index 0000000000..ad896c9a3c
--- /dev/null
+++ b/it/site/classic/ui.config/pom.xml
@@ -0,0 +1,76 @@
+
+
+
+ 4.0.0
+
+
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+
+
+
+
+ cif-components-it-site.ui.config-classic
+ content-package
+ CIF IT Site - UI config (classic)
+ Overlay of UI config package for CIF IT Site for classic
+
+
+
+
+
+ src/main/content/jcr_root
+
+
+
+ org.apache.jackrabbit
+ filevault-package-maven-plugin
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.config-classic
+ container
+ false
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.apps.structure
+
+
+
+
+
+ com.day.jcr.vault
+ content-package-maven-plugin
+
+
+
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.apps.structure
+ ${project.version}
+ zip
+
+
+
diff --git a/it/site/classic/ui.config/src/main/content/META-INF/vault/filter.xml b/it/site/classic/ui.config/src/main/content/META-INF/vault/filter.xml
new file mode 100644
index 0000000000..0a834e6d2e
--- /dev/null
+++ b/it/site/classic/ui.config/src/main/content/META-INF/vault/filter.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.cif.cacheinvalidation.internal.InvalidateCacheNotificationImpl~default.cfg.json b/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.cif.cacheinvalidation.internal.InvalidateCacheNotificationImpl~default.cfg.json
new file mode 100644
index 0000000000..0db3279e44
--- /dev/null
+++ b/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.cif.cacheinvalidation.internal.InvalidateCacheNotificationImpl~default.cfg.json
@@ -0,0 +1,3 @@
+{
+
+}
diff --git a/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.commerce.core.cacheinvalidation.internal.InvalidateCacheSupport.cfg.json b/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.commerce.core.cacheinvalidation.internal.InvalidateCacheSupport.cfg.json
new file mode 100644
index 0000000000..a35457bfcc
--- /dev/null
+++ b/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.commerce.core.cacheinvalidation.internal.InvalidateCacheSupport.cfg.json
@@ -0,0 +1,9 @@
+{
+ "enableDispatcherCacheInvalidation": false,
+ "dispatcherBaseUrl": "http://localhost:80",
+ "dispatcherBasePathConfiguration": "/content/cif-components-it-site/([a-z]{2})/([a-z]{2}):/content/cif-components-it-site/$1/$2",
+ "dispatcherUrlPathConfiguration": [
+ "productUrlPath:/products/product-page.html/(.+):/p/$1",
+ "categoryUrlPath:/products/category-page.html/(.+):/c/$1"
+ ]
+}
diff --git a/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.commerce.core.components.internal.services.UrlProviderImpl.cfg.json b/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.commerce.core.components.internal.services.UrlProviderImpl.cfg.json
new file mode 100644
index 0000000000..2dbc0b429f
--- /dev/null
+++ b/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.commerce.core.components.internal.services.UrlProviderImpl.cfg.json
@@ -0,0 +1,5 @@
+{
+ "enableContextAwareProductUrls": true,
+ "productPageUrlFormat":"{{page}}.html/{{url_path}}.html#{{variant_sku}}",
+ "categoryPageUrlFormat":"{{page}}.html/{{url_path}}.html"
+}
diff --git a/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.commerce.graphql.client.impl.GraphqlClientImpl~default.cfg.json b/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.commerce.graphql.client.impl.GraphqlClientImpl~default.cfg.json
new file mode 100644
index 0000000000..6d96cc97f9
--- /dev/null
+++ b/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.commerce.graphql.client.impl.GraphqlClientImpl~default.cfg.json
@@ -0,0 +1,18 @@
+{
+ "identifier": "default",
+ "httpMethod": "POST",
+ "url" : "$[env:COMMERCE_ENDPOINT;default=]",
+ "connectionTimeout": 5000,
+ "socketTimeout": 5000,
+ "maxHttpConnections": 20,
+ "requestPoolTimeout": 2000,
+ "acceptSelfSignedCertificates": true,
+ "cacheConfigurations": [
+ "cif-components-it-site/components/commerce/navigation:true:5:300",
+ "com.adobe.cq.commerce.core.search.services.SearchFilterService:true:10:300",
+ "cif-components-it-site/components/commerce/breadcrumb:true:1000:300",
+ "cif-components-it-site/components/commerce/product:true:50:1000",
+ "cif-components-it-site/components/commerce/productcollection:true:50:1000",
+ "cif-components-it-site/components/commerce/productlist:true:50:300"
+ ]
+}
diff --git a/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.commerce.graphql.magento.GraphqlDataServiceImpl~default.cfg.json b/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.commerce.graphql.magento.GraphqlDataServiceImpl~default.cfg.json
new file mode 100644
index 0000000000..a8a524ebe1
--- /dev/null
+++ b/it/site/classic/ui.config/src/main/content/jcr_root/apps/cif-components-it-site/osgiconfig-classic/config/com.adobe.cq.commerce.graphql.magento.GraphqlDataServiceImpl~default.cfg.json
@@ -0,0 +1,9 @@
+{
+ "identifier": "default",
+ "productCachingTimeMinutes": 5,
+ "categoryCachingEnabled": true,
+ "categoryCachingSize": 100,
+ "categoryCachingTimeMinutes": 60,
+ "productCachingEnabled": true,
+ "productCachingSize": 1000
+}
diff --git a/it/site/classic/ui.content/pom.xml b/it/site/classic/ui.content/pom.xml
new file mode 100644
index 0000000000..a92281f23a
--- /dev/null
+++ b/it/site/classic/ui.content/pom.xml
@@ -0,0 +1,91 @@
+
+
+
+ 4.0.0
+
+
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+
+
+
+
+ cif-components-it-site.ui.content-classic
+ content-package
+ CIF IT Site - UI content (classic)
+ Overlay of UI content package for CIF IT Site for classic
+
+
+
+
+
+ src/main/content/jcr_root
+
+
+
+
+
+
+ org.apache.jackrabbit
+ filevault-package-maven-plugin
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.content-classic
+ content
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.apps.structure
+
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.content
+ ${project.version}
+
+
+
+
+
+ com.day.jcr.vault
+ content-package-maven-plugin
+
+
+
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.apps.structure
+ ${project.version}
+ zip
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.content
+ ${project.version}
+ zip
+
+
+
diff --git a/it/site/classic/ui.content/src/main/content/META-INF/vault/filter.xml b/it/site/classic/ui.content/src/main/content/META-INF/vault/filter.xml
new file mode 100644
index 0000000000..27f6c561e8
--- /dev/null
+++ b/it/site/classic/ui.content/src/main/content/META-INF/vault/filter.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/it/site/classic/ui.content/src/main/content/jcr_root/conf/cif-components-it-site/settings/cloudconfigs/commerce/.content.xml b/it/site/classic/ui.content/src/main/content/jcr_root/conf/cif-components-it-site/settings/cloudconfigs/commerce/.content.xml
new file mode 100644
index 0000000000..5dae8bf34c
--- /dev/null
+++ b/it/site/classic/ui.content/src/main/content/jcr_root/conf/cif-components-it-site/settings/cloudconfigs/commerce/.content.xml
@@ -0,0 +1,17 @@
+
+
+
+
diff --git a/it/site/classic/ui.content/src/main/content/jcr_root/var/commerce/products/cif-components-it-site-default/.content.xml b/it/site/classic/ui.content/src/main/content/jcr_root/var/commerce/products/cif-components-it-site-default/.content.xml
new file mode 100644
index 0000000000..69146b53ce
--- /dev/null
+++ b/it/site/classic/ui.content/src/main/content/jcr_root/var/commerce/products/cif-components-it-site-default/.content.xml
@@ -0,0 +1,5 @@
+
diff --git a/it/site/generate.md b/it/site/generate.md
new file mode 100644
index 0000000000..ec45b6beac
--- /dev/null
+++ b/it/site/generate.md
@@ -0,0 +1,383 @@
+# Regenerating `it/site` (AEM Archetype → CIF IT Site layout)
+
+Use this when you want a **fresh** AEM Commerce project with the same IDs as this module, then apply the same trims and additions so the next iteration matches **`it/site`** without rediscovering steps.
+
+**Context:** This module lives inside the `aem-core-cif-components` monorepo at `it/site/`. Its reactor pom inherits from **`core-cif-components-parent`** (version `2.18.3-SNAPSHOT`, relativePath `../../parent/pom.xml`). If you need to recreate this as a **standalone** project outside the monorepo, the archetype command in step 1 is your starting point; for embedding inside the monorepo again, follow the same steps and then re-parent as described in step 6.
+
+---
+
+## Target layout (what you should have when done)
+
+```text
+it/site/
+├── pom.xml # reactor + dependencyManagement + profile classic
+├── all/ # cloud "all" container (no local core bundle embed)
+├── ui.apps/
+├── ui.apps.structure/ # structure package; filters in pom.xml only
+├── ui.config/
+├── ui.content/
+├── classic/ # built only with -Pclassic
+│ ├── ui.config/ # osgiconfig-classic OSGi for 6.x CIF GraphQL
+│ ├── ui.content/ # commerce cloud config + /var/commerce/products/…
+│ └── all/ # mixed classic "all": site + classic overlays (+ optional WCM Core)
+├── generate.md
+└── README.md
+```
+
+**Default reactor modules (root `pom.xml`):** `all`, `ui.apps`, `ui.apps.structure`, `ui.config`, `ui.content` — **no** `ui.frontend`, `core`, `it.tests`, `ui.tests`.
+
+**Classic reactor modules (profile `classic`):** `classic/ui.config`, `classic/ui.content`, `classic/all`.
+
+---
+
+## 1. Maven archetype (baseline)
+
+Run from the **parent folder** where the new directory should appear. Adjust `-DarchetypeVersion` to a version you can resolve from Maven (release or snapshot).
+
+```bash
+mvn -B org.apache.maven.plugins:maven-archetype-plugin:3.4.1:generate \
+ -DarchetypeGroupId=com.adobe.aem \
+ -DarchetypeArtifactId=aem-project-archetype \
+ -DarchetypeVersion=57-SNAPSHOT \
+ -DgroupId=com.adobe.commerce.cif \
+ -DartifactId=cif-components-it-site \
+ -Dversion=1.0.0-SNAPSHOT \
+ -Dpackage=com.adobe.commerce.cif \
+ -DappId=cif-components-it-site \
+ -DappTitle="CIF IT Site" \
+ -DaemVersion=cloud \
+ -DsdkVersion=latest \
+ -Dlanguage=en \
+ -Dcountry=us \
+ -DsingleCountry=n \
+ -DincludeCif=y \
+ -DcommerceEndpoint=https://YOUR-HOST/graphql \
+ -DfrontendModule=general \
+ -DincludeDispatcherConfig=n \
+ -DincludeExamples=n
+```
+
+- Replace **`https://YOUR-HOST/graphql`** with your Commerce GraphQL endpoint (must be `https` and end with **`/graphql`** per archetype validation).
+- **`sdkVersion=latest`**: AEM Cloud SDK API version label for the generated parent POM — not the frontend. Pin a concrete SDK string in **`pom.xml`** if you want reproducible builds.
+- **`includeDispatcherConfig=n`**: matches this module (no dispatcher module).
+- **`includeExamples=n`**: reduces sample surface; you still fix **Hello World** if the archetype leaves a model-bound HTL (see below).
+
+---
+
+## 2. Remove the `core` bundle and test modules (no in-repo Java)
+
+The archetype may create **`core`**, **`it.tests`**, and **`ui.tests`**. This module is **FileVault + frontend only** (CIF Core comes from Maven coordinates).
+
+1. **Delete directories:** `core/`, `it.tests/`, `ui.tests/` (if present).
+2. **Root `pom.xml`:** remove `core`, `it.tests`, `ui.tests` from ``.
+3. **`all/pom.xml`:** remove the `` (and any related dependency) for **`cif-components-it-site.core`**.
+4. **`ui.apps/pom.xml`:** remove the **`cif-components-it-site.core`** dependency.
+5. **`ui.content/pom.xml`:** remove the **`cif-components-it-site.core`** dependency.
+
+---
+
+## 3. Hello World HTL (compile without local Sling models)
+
+If **`ui.apps`** contains a **Hello World** component whose HTL uses **`data-sly-use`** / **`com.adobe.commerce.cif.core.models.HelloWorldModel`** from the removed **`core`** bundle, **`htl-maven-plugin`** will fail.
+
+**Choose one:**
+
+- Remove the `helloworld` component, **or**
+- Strip the model usage so the component uses only dialog / static markup (this module's approach).
+
+---
+
+## 4. Cloud CIF configuration under `/conf` (required for AEM CS / cloud `all`)
+
+The default **`ui.content`** package must include **Commerce** under the site's context-aware configuration, otherwise **Tools → Cloud Services → Commerce** (and bindings that use `cq:graphqlClient="default"`) will not exist for **`/conf/cif-components-it-site`**.
+
+Add:
+
+- `conf//settings/cloudconfigs/.content.xml` — `sling:Folder`
+- `conf//settings/cloudconfigs/commerce/.content.xml` — **Cloud only** in **`ui.content`**: **`cif/shell/components/configuration/page`**. **Do not** put **`commerce/gui`** or **`/var/commerce/...`** here; those stay **`classic/ui.content`** for 6.5.
+- `conf//settings/cloudconfigs/commerce/_rep_policy.xml` — ACLs for authors (copy from Venia / this module).
+
+**Classic-only** commerce pages (`commerce/gui`, `cq:catalogPath`, `/var/commerce/products/…`) stay in **`classic/ui.content`** and ship with **`classic/all`** for 6.5.
+
+---
+
+## 5. `ui.apps.structure`
+
+- If the archetype left an **empty** `ui.apps.structure/src/main/content` tree, **delete** it so the structure package is driven by **``** in **`ui.apps.structure/pom.xml`**.
+- Align **``** with paths your code packages install under (e.g. `/apps/cif-components-it-site`, `/conf/cif-components-it-site`, `/content/cif-components-it-site`, `/var`, etc.).
+- On **`filevault-package-maven-plugin`**, set **`true`** when the plugin is declared at module level.
+
+---
+
+## 6. Root `pom.xml`: parent and versions
+
+### 6a. Standalone project (outside the monorepo)
+
+Keep / tune these **properties** (examples as in this module; bump to match your Cloud Manager / CIF release):
+
+- **`aem.sdk.api`** — AEM as a Cloud Service SDK API for cloud modules.
+- **`aem.cif.sdk.api`** — CIF add-on API for `ui.apps` / compile scope.
+- **`core.cif.components.version`** — version of the CIF Core bundle used as **compile-time provided** dependency for HTL validation (runtime provides the bundle; `it/site` packages do not embed it).
+- **`core.wcm.components.version`** — WCM Core (needed for **`classic/all`** embeds).
+- **`magento.graphql.version`** — GraphQL client artifact line.
+
+### 6b. Inside `aem-core-cif-components` monorepo
+
+The reactor pom must inherit from **`core-cif-components-parent`**:
+
+```xml
+
+ com.adobe.commerce.cif
+ core-cif-components-parent
+ 2.18.3-SNAPSHOT
+ ../../parent/pom.xml
+
+```
+
+Drop `` and `` from the reactor pom (both inherited). Properties already in the monorepo parent (`aem.host`, `aem.port`, `core.wcm.components.version`, `magento.graphql.version`, etc.) should be omitted from `it/site/pom.xml` unless you need to override them. Only site-specific properties need to be declared (`aem.sdk.api`, `aem.cif.sdk.api`, etc.).
+
+**Compiler and enforcer — set to Java 8.** The archetype generates `11` and an enforcer requiring Java 11. Replace both so the module builds on Java 8 (AEM 6.5 / classic) and Java 11 (AEM Cloud SDK):
+
+```xml
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+
+
+
+ Project must be compiled with Java 8 or higher
+ 1.8.0
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.8
+ 1.8
+
+
+```
+
+> **⚠️ Remove `aemanalyser-maven-plugin` from `all/pom.xml`.**
+> The archetype adds `aemanalyser-maven-plugin` to the `all` module by default. This plugin is compiled with Java 11 (class file version 55.0) and will fail at build time on Java 8 (`class file versions up to 52.0`). Since this module must build on both Java 8 (AEM 6.5 / classic) and Java 11 (AEM Cloud SDK), remove the plugin entirely from `all/pom.xml` and its `` entry from `pluginManagement`. The `examples` module follows the same approach — it does not use this plugin at all.
+
+**RAT exclude for generated `target/` files.** When the `classic` profile has been run, the root RAT check scans `classic/*/target/vault-work/` and flags auto-generated `MANIFEST.MF` and `definition/.content.xml` files. Add this to the root `pom.xml` ``:
+
+```xml
+
+ org.apache.rat
+ apache-rat-plugin
+
+
+ **/target/**
+
+
+
+```
+
+All child poms' `` must match the monorepo version (`2.18.3-SNAPSHOT`), not the archetype-generated `1.0.0-SNAPSHOT`.
+
+**Version compatibility — WCM Core vs CIF Core (critical):** `core-cif-components-apps` declares a vault dependency on a minimum `core.wcm.components.content` version. If `core.wcm.components.version` is lower than what CIF requires, Package Manager will refuse to install CIF apps on AEM 6.5. Always check the CIF release notes or inspect the vault `properties.xml` of the `core-cif-components-apps-*.zip` artifact for its `dependencies` entry. Example: CIF Core **2.18.2** requires WCM Core **≥ 2.29.0**.
+
+**`dependencyManagement`:** ensure at least:
+
+- **`com.adobe.cq`**: `core.wcm.components.core` (without `provided` scope to allow embedding), **`core.wcm.components.content`** (zip), **`core.wcm.components.config`** (zip) — the last two are required so **`classic/all`** can resolve WCM Core content/config packages.
+
+---
+
+## 7. Adding AEM 6.5 / AMS support — the `classic/` folder
+
+> **The Maven archetype (step 1) generates a cloud-only project.** It does not create `classic/` at all. To make the project deployable on AEM 6.5 / AMS you need to add the three classic modules manually.
+>
+> **Fastest approach:** copy the `classic/` folder from this module into your new project, then do the targeted substitutions listed below. All structure, packaging rules, and pitfalls are already correct in this folder — you are just renaming artifact IDs and paths.
+
+### 7.1 Copy `classic/` into the new project
+
+```bash
+# from the root of your new archetype-generated project
+cp -r /path/to/aem-core-cif-components/it/site/classic ./classic
+# remove build artifacts if present
+rm -rf classic/*/target
+```
+
+### 7.2 String substitutions — find & replace throughout `classic/`
+
+Every occurrence of the strings below must be replaced with your project's values. A single `sed` or IDE find-and-replace across the whole `classic/` folder is sufficient.
+
+| Find (this module) | Replace with | Where it appears |
+|---|---|---|
+| `cif-components-it-site` | `` | pom.xml ``, ``, filter paths, JCR paths, `.content.xml` properties |
+| `cif-components-it-site-default` | `-default` | filter roots, `cq:catalogPath`, directory name under `var/commerce/products/` |
+| `com.adobe.commerce.cif` | `` | pom.xml ``, ``, `` |
+
+After substitution, also **rename the two JCR content directories** to match your appId:
+
+```bash
+# classic/ui.content — rename the conf path directory
+mv classic/ui.content/src/main/content/jcr_root/conf/cif-components-it-site \
+ classic/ui.content/src/main/content/jcr_root/conf/
+
+# classic/ui.content — rename the var/commerce/products directory
+mv "classic/ui.content/src/main/content/jcr_root/var/commerce/products/cif-components-it-site-default" \
+ "classic/ui.content/src/main/content/jcr_root/var/commerce/products/-default"
+```
+
+### 7.3 Changes in the root `pom.xml`
+
+Three things to add / adjust in the **root `pom.xml`** of the new project:
+
+**a) Add the `classic` profile** (inside ``):
+
+```xml
+
+ classic
+
+ classic/ui.config
+ classic/ui.content
+ classic/all
+
+
+```
+
+**b) Fix WCM Core version** to match what CIF Core requires.
+Check the CIF Core artifact's vault dependency (inspect `core-cif-components-apps-.zip` → `META-INF/vault/properties.xml` → `dependencies` entry). Set `core.wcm.components.version` to that version or higher.
+Example: CIF Core **2.18.2** requires WCM Core **≥ 2.29.0**.
+
+```xml
+2.29.0
+```
+
+**c) Add WCM Core to `dependencyManagement`** if not already present:
+
+```xml
+
+ com.adobe.cq
+ core.wcm.components.content
+ ${core.wcm.components.version}
+ zip
+
+
+ com.adobe.cq
+ core.wcm.components.config
+ ${core.wcm.components.version}
+ zip
+
+
+ com.adobe.cq
+ core.wcm.components.core
+ ${core.wcm.components.version}
+
+```
+
+### 7.4 What the classic modules contain (reference)
+
+| Module | packageType | Key content |
+|---|---|---|
+| `classic/ui.config` | `container` | OSGi configs under `osgiconfig-classic/config/` (GraphQL client + data service) and `osgiconfig-classic/config.author/` (editor status type) |
+| `classic/ui.content` | `content` | Commerce cloudconfig page with AEM 6.5 resource type; `/var/commerce/products/` catalog root folder |
+| `classic/all` | `mixed` | Embeds all site packages + classic overlays (+ optional WCM Core). CIF Core is expected to be installed separately on the target AEM |
+
+### 7.5 Critical pitfalls
+
+**⚠️ Never embed `ui.apps.structure` in `classic/all`.**
+`ui.apps.structure` is an intentionally empty package with broad REPLACE-mode filters covering `/apps`, `/conf`, `/content`, etc. Embedding it causes AEM's JCR Package Installer to wipe out everything under those roots on install — corrupting the entire repository. It must only ever appear in `` (build-time validation, never deployed). The `classic/` folder in this module already has this correct.
+
+**WCM Core version must satisfy CIF Core's vault dependency.**
+If the installed WCM Core is lower than what the installed CIF apps require, Package Manager will refuse to install CIF apps on AEM 6.5 with a `dependencies!` error. See 7.3b above.
+
+**Install order is enforced by vault dependency.**
+`classic/ui.content-classic` declares a vault dependency on `ui.content` in its `properties.xml`. This guarantees AEM Package Manager installs the cloud content package first (which creates `/conf/.../cloudconfigs/commerce` with the cloud resource type), then the classic overlay runs (replacing it with the 6.5 resource type `commerce/gui/components/configuration/page`).
+
+### 7.6 Deploy and verify on AEM 6.5
+
+```bash
+# 1. Build everything including classic modules (from it/site/ or monorepo root)
+mvn clean install -Pclassic
+
+# 2. Upload to AEM 6.5 Package Manager UI:
+# classic/all/target/cif-components-it-site.all-classic-*.zip
+```
+
+The JCR Package Installer runs **asynchronously** — wait ~30 seconds after Package Manager reports success before checking CRXDE.
+
+**Expected state in CRXDE after install:**
+
+| Path | Expected value |
+|---|---|
+| `/conf//settings/cloudconfigs/commerce/jcr:content/@sling:resourceType` | `commerce/gui/components/configuration/page` |
+| `/var/commerce/products/-default/@jcr:primaryType` | `sling:Folder` |
+| `/var/commerce/products/-default/@cq:conf` | `/conf/` |
+
+In Package Manager, confirm **`.ui.content-classic`** shows **Installed** (not just the `all-classic` container).
+
+---
+
+## 8. ClientLibs (static CSS — no `ui.frontend` build step)
+
+This module does **not** use a `ui.frontend` webpack/npm pipeline. The archetype generates one, but it was removed because the SCSS files were almost entirely empty stubs and the only meaningful output was a small amount of CSS.
+
+CSS is shipped as a single static file committed directly to `ui.apps`:
+
+```
+ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-site/css/site.css
+```
+
+The `clientlib-site` folder is a standard AEM `cq:ClientLibraryFolder` with `categories="[cif-components-it-site.site]"`. There is no JS (the only JS in the archetype was for the HelloWorld demo component, which is also removed).
+
+**When regenerating from the archetype:** delete `ui.frontend/` and remove `ui.frontend` and the `frontend-maven-plugin.version` property from the root `pom.xml`. Then write or copy a `site.css` directly into `clientlib-site/css/`.
+
+**`customheaderlibs.html`** — ensure all three clientlib categories are loaded:
+
+```html
+
+
+
+
+
+```
+
+The archetype omits the `cif-components-it-site.site` line — without it the `clientlib-site` CSS is never served to the browser.
+
+---
+
+## 9. Verify
+
+**Cloud reactor (from `it/site/`):**
+
+```bash
+mvn clean install
+```
+
+**Including classic:**
+
+```bash
+mvn clean install -Pclassic
+```
+
+**From the monorepo root (integration-tests profile):**
+
+```bash
+mvn clean install -pl it/site -am
+```
+
+**Local install (AEM Cloud SDK):** `mvn clean install -PautoInstallSinglePackage` — do **not** add `-Pclassic` for Cloud SDK.
+
+**Local install (AEM 6.5):** build first, then upload directly:
+```bash
+mvn clean install -Pclassic
+# Upload classic/all/target/cif-components-it-site.all-classic-*.zip via AEM Package Manager UI
+```
+
+Avoid `mvn clean install -PautoInstallSinglePackage,classic` on AEM 6.5 — it deploys the cloud `all` container first, which may install Cloud Service-specific content before the classic overlay can correct it.
+
+---
+
+## 10. CIF Core installation model (this module's assumption)
+
+This module **does not embed** CIF Core artifacts (`core-cif-components-*`) into its `all` / `classic/all` packages. The intended model is:
+
+- Install **CIF Core** separately on the target AEM (or have it pre-installed in the test environment).
+- Install **`it/site`** packages to add the test site content/config/apps that the integration tests rely on.
diff --git a/it/site/pom.xml b/it/site/pom.xml
new file mode 100644
index 0000000000..51e366b2e3
--- /dev/null
+++ b/it/site/pom.xml
@@ -0,0 +1,541 @@
+
+
+
+ 4.0.0
+
+
+ com.adobe.commerce.cif
+ core-cif-components-parent
+ 2.18.3-SNAPSHOT
+ ../../parent/pom.xml
+
+
+ cif-components-it-site
+ 1.0.0-SNAPSHOT
+ pom
+ CIF IT Site
+ CIF IT Site — integration-test site for AEM CIF Core Components
+
+
+ all
+ ui.apps
+ ui.apps.structure
+ ui.config
+ ui.content
+
+
+
+
+
+
+
+
+ 2.18.3-SNAPSHOT
+ 2025.09.02.00
+ 2026.3.25194.20260330T181734Z-260300
+ 6.4.0
+ CIF IT Site
+ UTF-8
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+
+
+ enforce-maven
+
+ enforce
+
+
+
+
+ [3.3.9,)
+
+
+ Project must be compiled with Java 8 or higher
+ 1.8.0
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.8
+ 1.8
+
+
+
+ org.apache.rat
+ apache-rat-plugin
+
+
+ **/target/**
+
+
+
+
+
+
+
+
+ com.github.eirslett
+ frontend-maven-plugin
+ ${frontend-maven-plugin.version}
+
+ v16.17.0
+ 8.15.0
+
+
+
+ install node and npm
+
+ install-node-and-npm
+
+
+
+ npm ci
+
+ npm
+
+
+ ci
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.1.2
+
+
+
+ org.apache.maven.plugins
+ maven-clean-plugin
+ 3.0.0
+
+
+
+ biz.aQute.bnd
+ bnd-maven-plugin
+ ${bnd.version}
+
+
+ bnd-process
+
+ bnd-process
+
+
+
+Bundle-Category: ${componentGroupName}
+-exportcontents: ${removeall;${packages;VERSIONED};${packages;CONDITIONAL}}
+-noextraheaders: true
+-snapshot: SNAPSHOT
+Bundle-DocURL:
+-plugin org.apache.sling.caconfig.bndplugin.ConfigurationClassScannerPlugin
+-plugin org.apache.sling.bnd.models.ModelsScannerPlugin
+
+
+
+
+
+
+ org.apache.sling
+ org.apache.sling.caconfig.bnd-plugin
+ 1.0.2
+
+
+ org.apache.sling
+ org.apache.sling.bnd.models
+ 1.0.0
+
+
+ org.apache.sling
+ scriptingbundle-maven-plugin
+ 0.5.0
+
+
+
+
+ biz.aQute.bnd
+ bnd-baseline-maven-plugin
+ ${bnd.version}
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 3.0.2
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+ 2.5.2
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.1
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ 2.22.1
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+
+
+ org.apache.sling
+ sling-maven-plugin
+ 2.4.0
+
+ http://${aem.host}:${aem.port}/system/console
+ WebConsole
+
+
+
+
+ org.apache.sling
+ htl-maven-plugin
+ 2.0.2-1.4.0
+
+ true
+
+
+
+
+ org.apache.jackrabbit
+ filevault-package-maven-plugin
+ true
+ 1.3.6
+
+ src/main/content/META-INF/vault/filter.xml
+
+
+
+ tccl:aem.cnd
+
+
+
+
+
+
+ biz.netcentric.aem
+ aem-nodetypes
+ 6.5.7.0
+
+
+
+
+
+ com.day.jcr.vault
+ content-package-maven-plugin
+ 1.0.6
+
+ http://${aem.host}:${aem.port}/crx/packmgr/service.jsp
+ true
+ ${vault.user}
+ ${vault.password}
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 3.0.0
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.0.0
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.0.0
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ [1.0.0,)
+ enforce
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ [2.2,)
+
+ copy-dependencies
+ unpack
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ [1.5,)
+ reserve-network-port
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ autoInstallPackage
+
+ false
+
+
+
+
+
+ org.apache.jackrabbit
+ filevault-package-maven-plugin
+
+
+ create-package
+ package
+
+
+
+
+ com.day.jcr.vault
+ content-package-maven-plugin
+
+
+ install-package
+ install
+
+ http://${aem.host}:${aem.port}/crx/packmgr/service.jsp
+
+
+
+
+
+
+
+
+
+ autoInstallPackagePublish
+
+ false
+
+
+
+
+
+ org.apache.jackrabbit
+ filevault-package-maven-plugin
+
+
+ create-package
+ package
+
+
+
+
+ com.day.jcr.vault
+ content-package-maven-plugin
+
+
+ install-package-publish
+ install
+
+ http://${aem.publish.host}:${aem.publish.port}/crx/packmgr/service.jsp
+
+
+
+
+
+
+
+
+
+
+ classic
+
+ classic/ui.config
+ classic/ui.content
+ classic/all
+
+
+
+
+
+
+
+
+
+
+
+ com.adobe.aem
+ aem-sdk-api
+ ${aem.sdk.api}
+ provided
+
+
+
+ com.adobe.cq
+ core.wcm.components.content
+ ${core.wcm.components.version}
+ zip
+
+
+ com.adobe.cq
+ core.wcm.components.config
+ ${core.wcm.components.version}
+ zip
+
+
+
+ com.adobe.cq
+ core.wcm.components.core
+ ${core.wcm.components.version}
+
+
+
+
+ com.adobe.aem
+ aem-cif-sdk-api
+ ${aem.cif.sdk.api}
+ provided
+
+
+
+ org.junit
+ junit-bom
+ 5.8.2
+ pom
+ import
+
+
+ org.mockito
+ mockito-core
+ 4.1.0
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 4.1.0
+ test
+
+
+ junit-addons
+ junit-addons
+ 1.4
+ test
+
+
+ io.wcm
+ io.wcm.testing.aem-mock.junit5
+ 5.5.4
+ test
+
+
+ org.apache.sling
+ org.apache.sling.scripting.api
+
+
+
+
+ org.apache.sling
+ org.apache.sling.testing.caconfig-mock-plugin
+ 1.4.0
+
+
+ commons-collections
+ commons-collections
+
+
+
+
+ com.adobe.cq
+ core.wcm.components.testing.aem-mock-plugin
+ ${core.wcm.components.version}
+ test
+
+
+ org.apache.sling
+ org.apache.sling.scripting.api
+
+
+
+
+ uk.org.lidalia
+ slf4j-test
+ 1.0.1
+ test
+
+
+
+
diff --git a/it/site/ui.apps.structure/pom.xml b/it/site/ui.apps.structure/pom.xml
new file mode 100644
index 0000000000..3c29793365
--- /dev/null
+++ b/it/site/ui.apps.structure/pom.xml
@@ -0,0 +1,79 @@
+
+
+
+ 4.0.0
+
+
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site
+ 1.0.0-SNAPSHOT
+ ../pom.xml
+
+
+
+
+
+ cif-components-it-site.ui.apps.structure
+ content-package
+ CIF IT Site - Repository Structure Package
+
+ Empty package that defines the structure of the Adobe Experience Manager repository the Code packages in this project deploy into.
+ Any roots in the Code packages of this project should have their parent enumerated in the Filters list below.
+
+
+
+
+
+ org.apache.jackrabbit
+ filevault-package-maven-plugin
+ true
+
+
+ none
+
+
+
+ /apps
+ /apps/cif-components-it-site
+
+ /apps/sling
+ /apps/cq
+ /apps/dam
+ /apps/wcm
+ /apps/msm
+
+
+ /apps/settings
+
+ /conf
+ /conf/cif-components-it-site
+ /content
+ /content/cif-components-it-site
+ /content/experience-fragments/cif-components-it-site
+
+
+ /content/dam/cif-components-it-site
+
+
+
+
+
+
+
diff --git a/it/site/ui.apps/pom.xml b/it/site/ui.apps/pom.xml
new file mode 100644
index 0000000000..2b5d7583fb
--- /dev/null
+++ b/it/site/ui.apps/pom.xml
@@ -0,0 +1,152 @@
+
+
+
+ 4.0.0
+
+
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site
+ 1.0.0-SNAPSHOT
+ ../pom.xml
+
+
+
+
+
+ cif-components-it-site.ui.apps
+ content-package
+ CIF IT Site - UI apps
+ UI apps package for CIF IT Site
+
+
+
+
+
+ src/main/content/jcr_root
+
+
+
+
+
+ org.apache.jackrabbit
+ filevault-package-maven-plugin
+
+
+ none
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.apps
+ application
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.apps.structure
+
+
+
+
+
+
+
+ com.day.jcr.vault
+ content-package-maven-plugin
+ true
+
+ true
+ true
+
+
+
+
+ org.apache.rat
+ apache-rat-plugin
+
+ true
+
+
+
+ org.apache.sling
+ htl-maven-plugin
+
+
+ validate-htl-scripts
+
+ validate
+
+ generate-sources
+
+ true
+ org.apache.sling.scripting.sightly
+ ${project.build.sourceDirectory}
+
+ cssClassName
+ decoration
+ decorationTagName
+ wcmmode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ com.adobe.commerce.cif
+ cif-components-it-site.ui.apps.structure
+ ${project.version}
+ zip
+
+
+
+ com.adobe.aem
+ aem-sdk-api
+
+
+
+
+
+ com.adobe.commerce.cif
+ core-cif-components-core
+ ${core.cif.components.version}
+ provided
+
+
+ com.adobe.aem
+ aem-cif-sdk-api
+ provided
+
+
+
+ org.apache.sling
+ org.apache.sling.scripting.sightly.runtime
+ 1.2.4-1.4.0
+ provided
+
+
+
diff --git a/it/site/ui.apps/src/main/content/META-INF/vault/filter.xml b/it/site/ui.apps/src/main/content/META-INF/vault/filter.xml
new file mode 100644
index 0000000000..0bd4849947
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/META-INF/vault/filter.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-base/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-base/.content.xml
new file mode 100644
index 0000000000..ab12acfacd
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-base/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-base/css.txt b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-base/css.txt
new file mode 100644
index 0000000000..51f69f7472
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-base/css.txt
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright 2017 Adobe Systems Incorporated
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###############################################################################
\ No newline at end of file
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-base/js.txt b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-base/js.txt
new file mode 100644
index 0000000000..51f69f7472
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-base/js.txt
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright 2017 Adobe Systems Incorporated
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###############################################################################
\ No newline at end of file
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-cif/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-cif/.content.xml
new file mode 100644
index 0000000000..7b4fc5f12a
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-cif/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-cif/css.txt b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-cif/css.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-cif/js.txt b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-cif/js.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-dependencies/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-dependencies/.content.xml
new file mode 100644
index 0000000000..763832d3cf
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-dependencies/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-dependencies/css.txt b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-dependencies/css.txt
new file mode 100644
index 0000000000..8e192389f2
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-dependencies/css.txt
@@ -0,0 +1,2 @@
+#base=css
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-dependencies/js.txt b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-dependencies/js.txt
new file mode 100644
index 0000000000..44758590c7
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-dependencies/js.txt
@@ -0,0 +1,2 @@
+#base=js
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-grid/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-grid/.content.xml
new file mode 100644
index 0000000000..fad2f7bf36
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-grid/.content.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-grid/css.txt b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-grid/css.txt
new file mode 100644
index 0000000000..1f9ab56740
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-grid/css.txt
@@ -0,0 +1,17 @@
+###############################################################################
+# Copyright 2018 Adobe Systems Incorporated
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###############################################################################
+
+less/grid.less
\ No newline at end of file
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-grid/less/grid.less b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-grid/less/grid.less
new file mode 100644
index 0000000000..5732374bf4
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-grid/less/grid.less
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 Adobe Systems Incorporated
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@import (once) "/libs/wcm/foundation/clientlibs/grid/grid_base.less";
+
+/* maximum amount of grid cells to be provided */
+@max_col: 12;
+
+/* default breakpoint */
+.aem-Grid {
+ .generate-grid(default, @max_col);
+}
+
+/* phone breakpoint */
+@media (max-width: 768px) {
+ .aem-Grid {
+ .generate-grid(phone, @max_col);
+ }
+}
+
+/* tablet breakpoint */
+@media (min-width: 769px) and (max-width: 1200px) {
+ .aem-Grid {
+ .generate-grid(tablet, @max_col);
+ }
+}
+
+/* force showing hidden components in unhide mode */
+.aem-GridShowHidden > .cmp-container > .aem-Grid > .aem-GridColumn {
+ display: block !important;
+}
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-site/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-site/.content.xml
new file mode 100644
index 0000000000..2b27dbe4dd
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-site/.content.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-site/css.txt b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-site/css.txt
new file mode 100644
index 0000000000..f852363a59
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-site/css.txt
@@ -0,0 +1,3 @@
+#base=css
+
+site.css
\ No newline at end of file
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-site/css/site.css b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-site/css/site.css
new file mode 100644
index 0000000000..d3ba183f82
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/clientlibs/clientlib-site/css/site.css
@@ -0,0 +1,229 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Copyright 2026 Adobe
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+/* == Base ================================================================== */
+
+html,
+body {
+ margin: 0;
+ font-size: 16px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ color: #202020;
+ background: #ececec;
+}
+
+@media (prefers-color-scheme: dark) {
+ html,
+ body {
+ color: #dfdfdf;
+ background: #131313;
+ }
+}
+
+a {
+ color: #2020e0;
+}
+
+@media (prefers-color-scheme: dark) {
+ a {
+ color: #dfdf1f;
+ }
+}
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ font: inherit;
+}
+
+/* == Navigation ============================================================ */
+
+.cmp-navigation__group {
+ display: flex;
+ flex-flow: row wrap;
+}
+
+.cmp-navigation__item {
+ list-style: none;
+ padding: .5em;
+}
+
+.cmp-navigation__item--level-0 .cmp-navigation__group {
+ display: none;
+}
+
+.cmp-navigation__item--level-0:hover > .cmp-navigation__group {
+ background: #ececec;
+ display: flex;
+ flex-direction: column;
+ flex-wrap: nowrap;
+ position: absolute;
+}
+
+/* == Product collection ==================================================== */
+
+.category__style_list .productcollection__items {
+ display: block;
+ margin: auto;
+ width: 30%;
+}
+
+/* == Page layout =========================================================== */
+
+main.container {
+ padding: .5em 1em;
+}
+
+/* == XF Footer ============================================================= */
+
+footer.experiencefragment {
+ padding: .5em 1em;
+ border-top: 1px solid #202020;
+}
+
+@media (prefers-color-scheme: dark) {
+ footer.experiencefragment {
+ border-color: #dfdfdf;
+ }
+}
+
+footer.experiencefragment .cmp-separator__horizontal-rule {
+ margin: 0;
+ border: 0;
+}
+
+/* == XF Header ============================================================= */
+
+header.experiencefragment {
+ padding: .5em 1em;
+ border-bottom: 1px solid #202020;
+}
+
+@media (prefers-color-scheme: dark) {
+ header.experiencefragment {
+ border-bottom-color: #dfdfdf;
+ }
+}
+
+header.experiencefragment a {
+ color: #202020;
+ text-decoration: none;
+}
+
+@media (prefers-color-scheme: dark) {
+ header.experiencefragment a {
+ color: #dfdfdf;
+ }
+}
+
+header.experiencefragment a:hover,
+header.experiencefragment a:focus {
+ color: #2020e0;
+ text-decoration: underline;
+}
+
+@media (prefers-color-scheme: dark) {
+ header.experiencefragment a:hover,
+ header.experiencefragment a:focus {
+ color: #dfdf1f;
+ }
+}
+
+header.experiencefragment .cmp-container {
+ display: grid;
+ grid-template-columns: 4fr 1fr 1fr;
+}
+
+header.experiencefragment .cmp-languagenavigation {
+ position: relative;
+}
+
+header.experiencefragment .cmp-languagenavigation::before {
+ content: " ";
+ display: block;
+ height: 1.5em;
+ background-image: url('data:image/svg+xml;utf8,');
+ background-repeat: no-repeat;
+}
+
+@media (prefers-color-scheme: dark) {
+ header.experiencefragment .cmp-languagenavigation::before {
+ background-image: url('data:image/svg+xml;utf8,');
+ }
+}
+
+header.experiencefragment .cmp-languagenavigation > .cmp-languagenavigation__group {
+ visibility: hidden;
+ transition-delay: .5s;
+ position: absolute;
+ top: 34px;
+ width: 20em;
+ padding: 0 8px;
+ border: 1px solid #202020;
+ border-top: 0;
+ background: #ececec;
+}
+
+@media (prefers-color-scheme: dark) {
+ header.experiencefragment .cmp-languagenavigation > .cmp-languagenavigation__group {
+ border-color: #dfdfdf;
+ background: #131313;
+ }
+}
+
+header.experiencefragment .cmp-languagenavigation:hover > .cmp-languagenavigation__group {
+ visibility: visible;
+ transition-delay: 0s;
+}
+
+header.experiencefragment .cmp-languagenavigation__group {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+header.experiencefragment .cmp-languagenavigation__item-title {
+ font-size: x-small;
+ text-transform: uppercase;
+}
+
+header.experiencefragment .cmp-languagenavigation__item--level-0 {
+ margin-bottom: .5em;
+}
+
+header.experiencefragment .cmp-languagenavigation__item--level-1 {
+ display: inline;
+}
+
+header.experiencefragment .cmp-languagenavigation__item--level-1:not(:first-child)::before {
+ content: " | ";
+}
+
+header.experiencefragment .cmp-languagenavigation__item--active > .cmp-languagenavigation__item-link {
+ font-weight: bold;
+}
+
+header.experiencefragment .cmp-search__field {
+ display: flex;
+ margin: -3px 0;
+}
+
+header.experiencefragment .cmp-search__input {
+ height: 26px;
+}
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/accordion/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/accordion/.content.xml
new file mode 100644
index 0000000000..7dcb916992
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/accordion/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/accordion/_cq_editConfig.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/accordion/_cq_editConfig.xml
new file mode 100644
index 0000000000..e0c0bc5603
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/accordion/_cq_editConfig.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/accordion/_cq_template/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/accordion/_cq_template/.content.xml
new file mode 100644
index 0000000000..21e5fa40a8
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/accordion/_cq_template/.content.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/accordion/new/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/accordion/new/.content.xml
new file mode 100644
index 0000000000..3b5e5da8b8
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/accordion/new/.content.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/breadcrumb/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/breadcrumb/.content.xml
new file mode 100644
index 0000000000..7c1eb422bc
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/breadcrumb/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/button/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/button/.content.xml
new file mode 100644
index 0000000000..0a13755df8
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/button/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/carousel/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/carousel/.content.xml
new file mode 100644
index 0000000000..7c6a77c990
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/carousel/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/carousel/_cq_editConfig.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/carousel/_cq_editConfig.xml
new file mode 100644
index 0000000000..e0c0bc5603
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/carousel/_cq_editConfig.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/carousel/new/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/carousel/new/.content.xml
new file mode 100644
index 0000000000..b4244c257f
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/carousel/new/.content.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/accountdetails/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/accountdetails/.content.xml
new file mode 100644
index 0000000000..30a2c1edee
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/accountdetails/.content.xml
@@ -0,0 +1,4 @@
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/addressbook/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/addressbook/.content.xml
new file mode 100644
index 0000000000..d3bccac3fe
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/addressbook/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/breadcrumb/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/breadcrumb/.content.xml
new file mode 100644
index 0000000000..e2cfe43522
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/breadcrumb/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/catalogpage/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/catalogpage/.content.xml
new file mode 100644
index 0000000000..4741ced571
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/catalogpage/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/catalogpage/customfooterlibs.html b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/catalogpage/customfooterlibs.html
new file mode 100644
index 0000000000..cd9e30d1ad
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/catalogpage/customfooterlibs.html
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/catalogpage/customheaderlibs.html b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/catalogpage/customheaderlibs.html
new file mode 100644
index 0000000000..2504c6c7b4
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/catalogpage/customheaderlibs.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/categorycarousel/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/categorycarousel/.content.xml
new file mode 100644
index 0000000000..f3e84800a5
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/categorycarousel/.content.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/contentfragment/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/contentfragment/.content.xml
new file mode 100644
index 0000000000..fd4dbc1dad
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/contentfragment/.content.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/experiencefragment/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/experiencefragment/.content.xml
new file mode 100644
index 0000000000..da53ed28a9
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/experiencefragment/.content.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/featuredcategorylist/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/featuredcategorylist/.content.xml
new file mode 100644
index 0000000000..88f55165a2
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/featuredcategorylist/.content.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/logo/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/logo/.content.xml
new file mode 100644
index 0000000000..ab7bd2894a
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/logo/.content.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/logo/_cq_dialog/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/logo/_cq_dialog/.content.xml
new file mode 100644
index 0000000000..fa62f19de2
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/logo/_cq_dialog/.content.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/logo/logo.html b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/logo/logo.html
new file mode 100644
index 0000000000..bd678540f5
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/logo/logo.html
@@ -0,0 +1,30 @@
+
+
+
+
+ ${header.navigationRootPageTitle}
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/miniaccount/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/miniaccount/.content.xml
new file mode 100644
index 0000000000..cfcd1e88a5
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/miniaccount/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/minicart/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/minicart/.content.xml
new file mode 100644
index 0000000000..cf88b2c371
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/minicart/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/navigation/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/navigation/.content.xml
new file mode 100644
index 0000000000..45eb8739bf
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/navigation/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/product/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/product/.content.xml
new file mode 100644
index 0000000000..9660e3d46a
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/product/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/productcarousel/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/productcarousel/.content.xml
new file mode 100644
index 0000000000..9ef74272ad
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/productcarousel/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/productlist/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/productlist/.content.xml
new file mode 100644
index 0000000000..d4818920f2
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/productlist/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/productteaser/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/productteaser/.content.xml
new file mode 100644
index 0000000000..67bafbd2aa
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/productteaser/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/productteaser/_cq_template/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/productteaser/_cq_template/.content.xml
new file mode 100644
index 0000000000..b91f403e09
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/productteaser/_cq_template/.content.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/relatedproducts/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/relatedproducts/.content.xml
new file mode 100644
index 0000000000..2cd158922e
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/relatedproducts/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/resetpassword/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/resetpassword/.content.xml
new file mode 100644
index 0000000000..becf9a17ff
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/resetpassword/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/searchbar/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/searchbar/.content.xml
new file mode 100644
index 0000000000..53ea7f8b05
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/searchbar/.content.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/searchresults/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/searchresults/.content.xml
new file mode 100644
index 0000000000..7aab10eee3
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/searchresults/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/teaser/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/teaser/.content.xml
new file mode 100644
index 0000000000..f832437439
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/commerce/teaser/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/container/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/container/.content.xml
new file mode 100644
index 0000000000..cb6abd4331
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/container/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/container/new/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/container/new/.content.xml
new file mode 100644
index 0000000000..d04de3c5f5
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/container/new/.content.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/contentfragment/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/contentfragment/.content.xml
new file mode 100644
index 0000000000..bf89ebcd35
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/contentfragment/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/contentfragment/_cq_editConfig.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/contentfragment/_cq_editConfig.xml
new file mode 100644
index 0000000000..e0c0bc5603
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/contentfragment/_cq_editConfig.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/contentfragmentlist/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/contentfragmentlist/.content.xml
new file mode 100644
index 0000000000..acf9b4b489
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/contentfragmentlist/.content.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/experiencefragment/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/experiencefragment/.content.xml
new file mode 100644
index 0000000000..b5654d00eb
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/experiencefragment/.content.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/experiencefragment/_cq_editConfig.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/experiencefragment/_cq_editConfig.xml
new file mode 100644
index 0000000000..e0c0bc5603
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/experiencefragment/_cq_editConfig.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/header/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/header/.content.xml
new file mode 100644
index 0000000000..44ec4a7f2e
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/header/.content.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/image/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/image/.content.xml
new file mode 100644
index 0000000000..36b39248e8
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/image/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/image/_cq_editConfig.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/image/_cq_editConfig.xml
new file mode 100644
index 0000000000..06ca8c28b5
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/image/_cq_editConfig.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/languagenavigation/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/languagenavigation/.content.xml
new file mode 100644
index 0000000000..481e656842
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/languagenavigation/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/list/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/list/.content.xml
new file mode 100644
index 0000000000..4e9ee14c5c
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/list/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/list/_cq_editConfig.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/list/_cq_editConfig.xml
new file mode 100644
index 0000000000..e0c0bc5603
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/list/_cq_editConfig.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/navigation/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/navigation/.content.xml
new file mode 100644
index 0000000000..8e230b3637
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/navigation/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/page/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/page/.content.xml
new file mode 100644
index 0000000000..7c9f3ee693
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/page/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/page/customfooterlibs.html b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/page/customfooterlibs.html
new file mode 100644
index 0000000000..218f74a8d0
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/page/customfooterlibs.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/page/customheaderlibs.html b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/page/customheaderlibs.html
new file mode 100644
index 0000000000..3e90d7ba98
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/page/customheaderlibs.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/search/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/search/.content.xml
new file mode 100644
index 0000000000..61d7ddc845
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/search/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/separator/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/separator/.content.xml
new file mode 100644
index 0000000000..0354302581
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/separator/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/tabs/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/tabs/.content.xml
new file mode 100644
index 0000000000..d778413e0d
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/tabs/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/tabs/_cq_editConfig.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/tabs/_cq_editConfig.xml
new file mode 100644
index 0000000000..e0c0bc5603
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/tabs/_cq_editConfig.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/tabs/_cq_template/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/tabs/_cq_template/.content.xml
new file mode 100644
index 0000000000..1a5b28b261
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/tabs/_cq_template/.content.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/tabs/new/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/tabs/new/.content.xml
new file mode 100644
index 0000000000..b7be84874c
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/tabs/new/.content.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/teaser/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/teaser/.content.xml
new file mode 100644
index 0000000000..6f5104d579
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/teaser/.content.xml
@@ -0,0 +1,5 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/teaser/_cq_editConfig.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/teaser/_cq_editConfig.xml
new file mode 100644
index 0000000000..e0c0bc5603
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/teaser/_cq_editConfig.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/text/.content.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/text/.content.xml
new file mode 100644
index 0000000000..4c83ca17b6
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/text/.content.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/text/_cq_dialog.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/text/_cq_dialog.xml
new file mode 100644
index 0000000000..e777607224
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/text/_cq_dialog.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/text/_cq_editConfig.xml b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/text/_cq_editConfig.xml
new file mode 100644
index 0000000000..e9ce16cc60
--- /dev/null
+++ b/it/site/ui.apps/src/main/content/jcr_root/apps/cif-components-it-site/components/text/_cq_editConfig.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+