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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ version: 2.1
orbs:
codecov: codecov/codecov@1.0.5
browser-tools: circleci/browser-tools@1.4.3
node: circleci/node@5.2.0

common:
restore_cache: &restore_cache
Expand All @@ -15,6 +16,8 @@ common:
cypress_test_steps: &cypress_test_steps
steps:
- checkout
- node/install:
node-version: '22.19.0'
- browser-tools/install-chrome
- browser-tools/install-chromedriver
- run:
Expand All @@ -39,7 +42,7 @@ common:
command: npm install chrome-launcher
- run:
name: Install lighthouse
command: npm install lighthouse
command: npm install lighthouse@13.5.0
- run:
name: Install Web Driver
command: npm install selenium-webdriver
Expand Down
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ AEM Core Forms Components — Adaptive Forms v2 component library.
When a change needs a new component version (`v1` → `v2`) vs. a fix-in-place, and the full checklist of what to update when bumping a version (clientlib self-containment, `_cq_dialog`/`_cq_styleConfig` inheritance, `pom.xml`, runtime clientlib embed lists, etc.).

### E2E Testing
- **SP/LTS Authoring Compatibility**: [`docs/e2e-testing/authoring-compatibility.md`](docs/e2e-testing/authoring-compatibility.md)
Stable selectors and Granite validation APIs, operation-specific waits, referrer requirements, normal-browser ngrok setup, and first-pass validation before repetition.
- **Feature Toggle Tests**: [`docs/e2e-testing/feature-toggles.md`](docs/e2e-testing/feature-toggles.md)
How to add Cypress e2e tests for new feature toggles: OSGi config changes, system property wiring, and the isLatestAddon + fetchFeatureToggles test pattern.
142 changes: 142 additions & 0 deletions docs/e2e-testing/authoring-compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Authoring test compatibility: 6.5 SP and LTS

Authoring assertions must describe the field or editor behavior, not the markup
of one Coral implementation. A passing repetition on one server does not prove
compatibility with another AEM service pack or LTS instance.

## Selectors and validation

- Scope fields through our component classes and field names, for example
`.cmp-adaptiveform-checkboxgroup__value input[name='./default']`.
- Avoid internal presentation classes such as `.coral3-Search-input`,
`.coral-Form-errorlabel`, and `.coral-Form-fielderror`. Error UI can be a
tooltip, an icon, or an inline label. Covered or hidden error UI is not proof
that the field is valid.
- Use `cy.assertFieldInvalid(selector, message)` to require one field,
`aria-invalid="true"`, and the expected Granite validation message. It reads
the validation state after submission; it does not manufacture an error.
- The served AEM 6.5.25.0 `foundation-validation` adapter exposes
`getValidationMessage()`. The published Granite API also describes
`validationMessage()`. The shared command checks the available accessor
instead of branching on an AEM version string. A missing adapter, unsupported
accessor, ambiguous field, or unexpected message must fail explicitly.
- Use public Coral elements and values for selection through
`cy.selectCoralOption()`. Do not assume every `coral-select` has a native
`select[handle="nativeSelect"]`.
- Scope component insertion to the visible Insert dialog and require a unique
visible native input. Use the public dialog `open` property rather than
Coral's internal state classes when checking for a leftover dialog.

The [Granite validation reference](https://developer.adobe.com/experience-manager/reference-materials/6-5/granite-ui/api/jcr_root/libs/granite/ui/components/coral/foundation/clientlibs/foundation/js/validation/index.html)
describes the adapter contract. Check the served clientlib when its API differs
from the reference; do not assume a documented method exists on every instance.

## Layer selection and synchronization

`cy.selectLayer()` waits for the requested layer to be visibly selected.
An overlay-reposition event is not an unconditional layer-selection contract.
Requiring it caused existing tests to time out on both SP and LTS even after
the selected-layer assertion succeeded.

Keep operation-specific refresh waits for insertion, save, and deletion.
An event wait justified for one operation must not be imposed on every editor
transition. Test an actual layer change as well as an already-selected layer:
the latter returns early and cannot validate a transition wait.

## Button inline editing

An empty caption can remove both `jcr:title` and the rendered caption span.
Do not require a nonempty span after explicitly clearing the label. The inline
test saves a nonempty caption and verifies preview text and persisted title,
then repeats the original empty-label edit and verifies the empty native button
and absent/empty title before deletion. Both edits wait for the inline toolbar
to close. API teardown removes only the named test fixture if an assertion fails.

Each RTE inline-editor case creates its own Button and enables and saves
`isTitleRichText` before opening the editor; it cannot reuse the preceding
case's fixture because `afterEach` deletes it. Opening retries the overlay/EDIT
action only while the inline toolbar is absent, within the existing ten-second
budget. Once the toolbar is visible, EDIT is not invoked again. A persistent
opening failure still fails the test rather than retrying the whole test.

The Forms toolbar selects the plaintext editor when the rendered label/text
has no `data-richtext` attribute. A default Button therefore need not expose
`.rte-toolbar`: retrying EDIT or increasing the timeout cannot turn plaintext
editing into rich-text editing. RTE caption-edit tests must explicitly enable
rich text and wait for the configure save to refresh the editable.

## Rule-save assertions

Parse the rule-save POST's `:content` JSON and compare `fd:events.change`
structurally, as on master. Require the exact expected scripts rather than a
substring of serialized JSON. The equality-rule test cleans its named text-input
fixture before insertion and after each attempt so failed saves cannot leave
rules that accumulate on subsequent runs. It selects the edited field's generated
name explicitly as the hide target, rather than the first option: a leftover
sibling otherwise changes a self-hide rule into a `dispatchEvent` rule.

## Authentication and browser user agents

Use the normal browser user agent for acceptance. Sling's referrer filter
classifies browser requests using `Mozilla`/`Opera` in the user agent; changing
it to a non-browser value can hide a missing-referrer POST regression.

Authenticated preferences, policy restoration, and fixture cleanup POSTs need
the session, CSRF token, and same-server referrer. Request-based login also
includes the configured `baseUrl` as its referrer. Await requests and preserve
HTTP failures; do not set `failOnStatusCode: false` to make rejected writes pass.

## Remote ngrok runs with normal Chrome

An ngrok browser warning can return HTTP 200 HTML instead of AEM JSON or
JavaScript (`ERR_NGROK_6024`). A missing `body.token` or JavaScript
`Unexpected token '<'` can therefore be a tunnel response, not an AEM selector
failure. Inspect the response content type before changing application code.

For temporary remote support, forward
`ngrok-skip-browser-warning: true` on the initial `cy.visit()` and node-side
`cy.request()` calls. Browser subresources need the header too. The following
Chrome-only support hook was used successfully on the remote SP instance:

```js
before(() => {
cy.then(() => Cypress.automation('remote:debugger:protocol', {
command: 'Network.enable'
}).then(() => Cypress.automation('remote:debugger:protocol', {
command: 'Network.setExtraHTTPHeaders',
params: {headers: {'ngrok-skip-browser-warning': 'true'}}
})));
});
```

This is tunnel-specific support, not a replacement for the normal CI browser
configuration. Broad middleware interception introduced while debugging stalled
the remote runner; removing it and setting the native browser header allowed the
same Text case to complete. Do not diagnose an AEM `visit()` problem solely from
a stalled custom runner.

When inspecting Chrome, use the test browser's actual
`--remote-debugging-port`. The first `DevTools listening` line can belong to
Cypress's Electron process, not the test Chrome. An empty target list from the
wrong debugger does not show that the test browser has no page.

## Acceptance order

1. Verify the actual product version from
`/system/console/status-productinfo.txt` and the active component bundles.
A Core Components bundle version does not distinguish SP from LTS.
2. Capture fixture and policy baselines; preserve unrelated authored content.
3. Run every reported failing case once with global and per-test retries
disabled. Verify selected cases were collected and actually passed.
4. Include earlier reported cases when changing shared helpers. New-case passes
are not evidence that the original fixes still work.
5. Publish the verified fixes, then run the entire configured Cypress suite.
In `release/650`, the current inventory is 153 JavaScript specs.
6. Verify cleanup/restoration and obtain results for each CI variant. Record
pending, unsupported, skipped-after-hook, and retry-assisted results
separately from clean passes.

A helper contract check against both accessor shapes is not a live LTS test.
Finite repetitions cannot guarantee that a race never recurs. Keep the PR
evidence explicit about the server, source revision, selected cases, and
remaining validation gaps.
4 changes: 4 additions & 0 deletions ui.tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ npm run cypress:run:file -- ./specs/textinput/textinput.runtime.spec.js
* Chrome and/or Firefox browser installed locally in default location
* An AEM author instance running at http://localhost:4502

CircleCI installs Node.js 22.19.0 before the shared UI-test dependencies and
uses Lighthouse 13.5.0. Lighthouse requires Node.js >=22.19; the older Node.js
bundled in the AEM test-runner images cannot parse its JSON import attributes.
Keep the CI Node.js and Lighthouse versions compatible when upgrading either.

#### Remarks
* After execution, reports and logs are available in `test-module/target/reports` folder
Expand Down
50 changes: 50 additions & 0 deletions ui.tests/test-module/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,53 @@ npm install
```

After execution, reports and logs are available in `target/reports` folder

## Remote authoring and stability checks

Override `baseUrl` rather than changing the committed localhost configuration:

```sh
npx --no-install cypress run --browser chrome --headless \
--spec 'specs/wizard/wizard.authoring.cy.js,specs/checkboxgroup/checkboxgroup.authoring.cy.js,specs/dropdown/dropdown.authoring.cy.js,specs/contentfragment/contentfragment.authoring.cy.js,specs/telephoneinput/telephoneinput.authoring.cy.js' \
--config 'baseUrl=https://author.example,retries=0,video=false' \
--env 'crx.loginViaRequest=true'
```

`crx.loginViaRequest` is optional and defaults to the existing UI login flow.
Enable it when an external author endpoint requires a session before navigation
or its UI login redirects escape the Cypress frame. It uses AEM's form-login
endpoint and the existing `crx.username`, `crx.password`, and `crx.contextPath`
settings. Supply credentials using Cypress environment configuration; do not
commit them. For an ngrok browser-warning endpoint, send its warning-bypass
header without replacing the normal browser user agent. See
[SP/LTS authoring compatibility](../../docs/e2e-testing/authoring-compatibility.md)
for the verified remote setup and cross-version assertion patterns.

Component insertion must finish the editor refresh before a caller configures
or deletes the new component.
Use `cleanTest` after entering the Edit layer, `cancelConfigureDialog` for
configure-dialog cancellation, and `submitConfigureDialog` when a save must
finish before the next toolbar action. Select classic Coral options through
`selectCoralOption`, rather than assuming a native `select` or modern Coral
CSS classes.

Use `createRule` to wait for the rule editor's statement builder, rather than
assuming its first Create click is handled while the iframe initializes.

Layer selection waits for the selected-layer UI state, not an overlay-reposition
event that every layer transition may not emit. Use `assertFieldInvalid` to
check the field's invalid state and Granite validation message without depending
on Coral's version-specific tooltip or error-label markup.

Use `cleanTestFixture` in teardown to remove only the named test-owned component
through authenticated requests, even if the editor or configure dialog failed.
Tutorial preferences are saved through awaited requests; request-login authoring
does not depend on the landing-page redirect. Authenticated preferences, policy,
and fixture-cleanup POSTs include both the CSRF token and the configured AEM
`baseUrl` as their referrer, including with the default browser user agent.

Repeat stability runs with retries disabled, including per-test retry overrides.
Verify that test-owned components are removed and the telephone policy matches
its original snapshot after both passing and failing runs.
Use the default browser user agent for acceptance: a non-browser user agent can
bypass Sling's browser referrer checks and mask requests that fail in CI.
Loading
Loading