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
4 changes: 4 additions & 0 deletions docs/architecture/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ curl -s -u admin:admin \
| `cy.previewForm(pagePath)` | Loads form at `<pagePath>?wcmmode=disabled`, waits for AF runtime init, returns `formContainer` |
| `cy.previewFormWithPanel(pagePath)` | Same as above but waits for panel-specific initialization |
| `cy.fetchFeatureToggles()` | `GET /etc.clientlibs/toggles.json`, returns `{ enabled: ["FT_FORMS-XXXXX", ...] }` |
| `cy.selectLayer(layer)` | Keeps an already-selected layer; otherwise waits for the selected layer and overlay repositioning |
| `cy.insertComponent(dropZone, searchText, resourceType)` | Opens the Insert dialog with bounded retries, searches its visible component region, and waits for insertion and overlay refresh |

Authoring specs should reuse these commands instead of implementing their own layer-switch or insertion sequence. Insert-dialog selectors use `guideSelectors.insertComponentDialog.dialog` (`.InsertComponentDialog-components`) and replacement selectors use the Forms hook `.cmp-replace-dialog-search-components`; visibility is scoped to those content regions without depending on a `coral-dialog` element or an outer dialog wrapper.

### Key `cy.af` helpers (defined in `libs/support/functions.js`)

Expand Down
73 changes: 53 additions & 20 deletions ui.tests/test-module/libs/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,13 +297,23 @@ Cypress.Commands.add("openPage", (pagePath, options = {}) => {
cy.visit(path, options);
});

// cypress command to select layer in authoring
/**
* Selects an authoring layer and waits for its overlays to be repositioned.
* @param {string} layer Authoring layer name.
*/
Cypress.Commands.add("selectLayer", (layer) => {
// please note: when switching from style to other layer, we refresh guide, so these events need to be checked here
cy.get(siteSelectors.selectLayer.trigger).click();
cy.get(siteSelectors.selectLayer.popover.self + ' [data-layer="' + layer + '"]').should('be.visible');
cy.get(siteSelectors.selectLayer.popover.self + ' [data-layer="' + layer + '"]').click({force: true});
cy.get(siteSelectors.selectLayer.current + '[data-layer="' + layer + '"].is-selected');
const selectedLayer = siteSelectors.selectLayer.current + '[data-layer="' + layer + '"].is-selected';
return cy.get(siteSelectors.selectLayer.current).then(($layers) => {
if ($layers.filter('[data-layer="' + layer + '"].is-selected').length) {
return cy.get(selectedLayer).should('be.visible');
}
cy.initializeEventHandlerOnChannel(siteConstants.EVENT_NAME_OVERLAYS_REPOSITIONED).as("isLayerOverlaysRepositioned");
cy.get(siteSelectors.selectLayer.trigger).click();
cy.get(siteSelectors.selectLayer.popover.self + ' [data-layer="' + layer + '"]')
.should('be.visible').click({force: true});
cy.get(selectedLayer).should('be.visible');
return cy.get("@isLayerOverlaysRepositioned").its('done').should('equal', true);
});
});

// cypress command to open editable toolbar
Expand Down Expand Up @@ -656,24 +666,47 @@ Cypress.Commands.add("deleteComponentByTitle", (title) => {
});


// cypress command to insert component
/**
* Inserts a component through the visible dialog and waits for the editor refresh.
* @param {string} selector Drop-zone overlay selector.
* @param {string} componentString Component search text.
* @param {string} componentType Component resource type.
*/
Cypress.Commands.add("insertComponent", (selector, componentString, componentType) => {
//Open toolbar of root panel
const insertComponentDialog_Selector = '.InsertComponentDialog-components [value="' + componentType + '"]',
insertComponentDialog_searchField = ".InsertComponentDialog-components input[type='search']";
cy.openEditableToolbar(selector);
cy.get(guideSelectors.editableToolbar.actions.insert).should('be.visible').click();
const insertComponentDialog = guideSelectors.insertComponentDialog.dialog + ':visible',
insertComponentDialog_Selector = insertComponentDialog + ' [value="' + componentType + '"]',
insertComponentDialog_searchField = insertComponentDialog + " input[type='search']";
// Overlay refreshes can discard an Insert click; retry opening, not interactions with a hidden dialog.
recurse(
// the commands to repeat, and they yield the input element
() => cy.get(insertComponentDialog_searchField).clear().type(componentString),
// the predicate takes the output of the above commands
// and returns a boolean. If it returns true, the recursion stops
() => {
cy.get('body').then(($body) => {
if (!$body.find(insertComponentDialog).length) {
cy.openEditableToolbar(selector);
cy.get(guideSelectors.editableToolbar.actions.insert).should('be.visible').click();
}
});
return cy.get('body');
},
($body) => $body.find(insertComponentDialog).length === 1,
{limit: 5, delay: 1000, timeout: 30000, log: false}
);
cy.get(insertComponentDialog).should('have.length', 1).and('be.visible');
recurse(
() => {
cy.get(insertComponentDialog_searchField).should('be.visible').clear();
cy.get(insertComponentDialog_searchField).should('be.visible').type(componentString);
return cy.get(insertComponentDialog_searchField);
},
($input) => $input.val() === componentString,
)
);
cy.get(insertComponentDialog_searchField).type('{enter}');
cy.get(insertComponentDialog_Selector).should('be.visible');// basically should assertions does implicit retry in cypress
// refer https://docs.cypress.io/guides/references/error-messages.html#cy-failed-because-the-element-you-are-chaining-off-of-has-become-detached-or-removed-from-the-dom
cy.get(insertComponentDialog_Selector).click({force: true}); // sometimes AEM popover is visible, hence adding force here
cy.get(insertComponentDialog_Selector).should('be.visible');
cy.initializeEventHandlerOnChannel(siteConstants.EVENT_NAME_EDITABLES_UPDATED).as("isInsertEditableUpdated");
cy.initializeEventHandlerOnChannel(siteConstants.EVENT_NAME_OVERLAYS_REPOSITIONED).as("isInsertOverlaysRepositioned");
cy.get(insertComponentDialog_Selector).click();
cy.get(insertComponentDialog).should('not.exist');
cy.get("@isInsertEditableUpdated").its('done').should('equal', true);
return cy.get("@isInsertOverlaysRepositioned").its('done').should('equal', true);
});

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
*/


/* global Cypress */

const sitesSelectors = require('../../libs/commons/sitesSelectors'),
sitesConstants = require('../../libs/commons/sitesConstants'),
afConstants = require('../../libs/commons/formsConstants');

/**
Expand All @@ -26,6 +29,19 @@ describe('Replace functionality - sites', function () {
const pagePath = "/content/forms/sites/core-components-it/blank",
pageDropZoneSuffix = "/jcr:content/root/responsivegrid/container";

const replaceDialog = '.cmp-replace-dialog-search-components:visible';

const selectReplacement = (resourceTypeSelector, componentPath) => {
cy.initializeEventHandlerOnChannel(sitesConstants.EVENT_NAME_EDITABLES_UPDATED).as('replacementEditableUpdated');
cy.initializeEventHandlerOnChannel(sitesConstants.EVENT_NAME_OVERLAYS_REPOSITIONED).as('replacementOverlaysRepositioned');
cy.intercept('POST', '**' + componentPath).as('replaceComponent');
cy.get(replaceDialog + ' ' + resourceTypeSelector).should('be.visible').click();
cy.wait('@replaceComponent').its('response.statusCode').should('be.oneOf', [200, 201]);
cy.get(replaceDialog).should('not.exist');
cy.get('@replacementEditableUpdated').its('done').should('equal', true);
cy.get('@replacementOverlaysRepositioned').its('done').should('equal', true);
};

const dropComponentInSites = function (componentName, resourceType) {
const dataPath = "/content/forms/sites/core-components-it/blank/jcr:content/root/responsivegrid/container/container/*",
responsiveGridDropZoneSelector = sitesSelectors.overlays.overlay.component + "[data-path='" + dataPath + "']";
Expand All @@ -46,14 +62,13 @@ describe('Replace functionality - sites', function () {
cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + editPathSelector);
cy.invokeEditableAction("[data-action='replace']"); // this line is causing frame busting which is causing cypress to fail

cy.get('.cmp-replace-dialog-search-components').should('exist');
cy.get(replaceDialog).should('be.visible');
// Check If Dialog Options Are Visible
cy.get(textInput)
.should("not.exist");
cy.get(submitButton)
.should("exist");
cy.get(title)
.click();
selectReplacement(title, pagePath + pageDropZoneSuffix + "/container/button");

cy.get('[title="'+titleName+'"]')
.should('exist');
Expand Down Expand Up @@ -85,8 +100,7 @@ describe('Replace functionality - sites', function () {
.should("exist");
cy.get(wizard)
.should("exist");
cy.get(accordion)
.click();
selectReplacement(accordion, pagePath + pageDropZoneSuffix + "/container/panelcontainer");
cy.get(accordionDefaultPanel)
.should("not.exist");

Expand All @@ -107,38 +121,56 @@ describe('Replace functionality - sites', function () {
testButtonReplaceBehaviour(buttonEditPathSelector);
});

it('test behaviour of replace button', function () {
it('test behaviour of replace panel with accordion', function () {
testPanelReplaceBehaviourWithAccordion(panelEditPathSelector);
});

});

const checkTestGroupPolicy = () => {
cy.openAuthoring("/conf/core-components-examples/settings/wcm/templates/content-page/structure");
cy.get('[data-text="Layout Container"]').eq(0).click()
.then(() => {
cy.get('.cq-editable-action').eq(3).click().then(() => {
cy.get('[value="group:replace test group"]').eq(0).click().then(() => {
cy.get('[title="Done"]').scrollIntoView().click();
})
});
it('recovers when the first insert action is lost during an editor refresh', function () {
cy.intercept('POST', '**/content/forms/sites/core-components-it/blank/**', (request) => {
request.on('response', (response) => response.setDelay(500));
});
}

const uncheckTestGroupPolicy = () => {
cy.openSiteAuthoring("/conf/core-components-examples/settings/wcm/templates/content-page/structure");
cy.get('[data-text="Layout Container"]').eq(0).click()
.then(() => {
cy.get('.cq-editable-action').eq(3).click().then(() => {
cy.get('[value="group:replace test group"]').eq(0).click().then(() => {
cy.get('[title="Done"]').scrollIntoView().click();
})
});
const lostInsertAction = cy.stub().as('lostInsertAction');
cy.window().then((win) => {
const discardFirstInsert = (event) => {
if (event.target.closest(sitesSelectors.editableToolbar.actions.insert)) {
event.preventDefault();
event.stopImmediatePropagation();
win.document.removeEventListener('click', discardFirstInsert, true);
lostInsertAction();
}
};
win.document.addEventListener('click', discardFirstInsert, true);
});
}
testButtonReplaceBehaviour(buttonEditPathSelector);
cy.get('@lostInsertAction').should('have.been.calledOnce');
});

});

context('Test replace action within different groups', function () {
const templatePath = "/conf/core-components-examples/settings/wcm/templates/content-page/structure";
const policyDialog = '.cq-dialog:visible:has([value="group:replace test group"])',
policyCheckbox = policyDialog + ' [value="group:replace test group"]';
let originalTestGroupAllowed;

const updateTestGroupPolicy = (allow) => {
cy.openEditableToolbar(sitesSelectors.overlays.overlay.self + '[data-path="' + templatePath + '/jcr:content/root/responsivegrid"]');
cy.invokeEditableAction(sitesSelectors.editableToolbar.actions.policy);
cy.get(policyCheckbox).first().scrollIntoView().should('be.visible').invoke('prop', 'checked')
.should('be.a', 'boolean').then((checked) => {
if (originalTestGroupAllowed === undefined) {
originalTestGroupAllowed = checked;
}
if (checked !== allow) {
cy.get(policyCheckbox).first().click();
}
});
cy.get(policyCheckbox).first().should('have.prop', 'checked', allow);
cy.intercept('POST', '**/conf/core-components-examples/settings/wcm/policies/**').as('saveReplacePolicy');
cy.get(policyDialog + ' [title="Done"]').scrollIntoView().should('be.visible').click();
cy.wait('@saveReplacePolicy').its('response.statusCode').should('be.oneOf', [200, 201]);
cy.get(policyDialog).should('not.exist');
};

const pagePath = "/content/forms/sites/core-components-it/blank",
replaceCompTestGroup = "/apps/forms-core-components-it/form/image",
Expand All @@ -153,23 +185,31 @@ describe('Replace functionality - sites', function () {
replaceCompTestGroupDrop = pagePath + containerSuffix + "/container/image";

beforeEach(function () {
checkTestGroupPolicy();
originalTestGroupAllowed = undefined;
cy.openAuthoring(templatePath);
updateTestGroupPolicy(true);
});

afterEach(function () {
uncheckTestGroupPolicy();
if (originalTestGroupAllowed !== undefined) {
cy.openSiteAuthoring(templatePath);
updateTestGroupPolicy(originalTestGroupAllowed);
}
});

it('test behaviour of replace within different groups same component type', function () {
cy.openSiteAuthoring(pagePath);
cy.selectLayer("Edit");
cy.cleanTest(editPath);
cy.insertComponent(responsiveGridDropZoneSelector, "Adaptive Form Image", afConstants.components.forms.resourceType.formimage);
cy.get('body').click( 0,0);

cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + editPathSelector);
cy.invokeEditableAction("[data-action='replace']");

cy.get(image).click();
selectReplacement(image, editPath);
cy.request((Cypress.env('crx.contextPath') || '') + editPath + '.0.json')
.its('body.sling:resourceType').should('equal', replaceCompTestGroup.replace('/apps/', ''));

cy.deleteComponentByPath(replaceCompTestGroupDrop);
});
Expand All @@ -180,13 +220,16 @@ describe('Replace functionality - sites', function () {

cy.openSiteAuthoring(pagePath);
cy.selectLayer("Edit");
cy.cleanTest(buttonEditPath);

cy.insertComponent(responsiveGridDropZoneSelector, "Adaptive Form Button", afConstants.components.forms.resourceType.formbutton);
cy.get('body').click( 0,0);

cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + buttonEditPathSelector);
cy.invokeEditableAction("[data-action='replace']");
cy.get(image).click();
selectReplacement(image, buttonEditPath);
cy.request((Cypress.env('crx.contextPath') || '') + buttonEditPath + '.0.json')
.its('body.sling:resourceType').should('equal', replaceCompTestGroup.replace('/apps/', ''));
cy.deleteComponentByPath(buttonEditPath);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/


/* global Cypress */

const sitesSelectors = require('../../libs/commons/sitesSelectors'),
afConstants = require('../../libs/commons/formsConstants');

Expand Down Expand Up @@ -103,13 +105,26 @@ describe('Page - Authoring', function () {
telephoneInputEditPathSelector = "[data-path='" + telephoneInputEditPath + "']";
const customKey = 'customKey',
customValue = 'customValue',
// Shared, persistent design policy this test mutates — reset it each run so a leftover custom
// format from a prior run/retry can't leave the dialog in a dirty state (see reset command).
// The policy is shared across specs and retries.
telephoneInputPolicyPath = '/conf/core-components-examples/settings/wcm/policies/forms-components-examples/components/form/telephoneinput/default';
let policyPrepared = false;

beforeEach(function () {
cy.openAuthoring(templateDataPath + ".html");
policyPrepared = false;
cy.openAuthoring(templateDataPath);
cy.resetTelephoneInputDesignPolicy(telephoneInputPolicyPath).then(() => {
policyPrepared = true;
});
});

afterEach(function () {
if (!policyPrepared) {
return;
}
cy.resetTelephoneInputDesignPolicy(telephoneInputPolicyPath);
const contextPath = Cypress.env('crx.contextPath') || '';
cy.request(contextPath + telephoneInputPolicyPath + '.json')
.its('body').should('not.have.property', 'allowedCustomFormats');
});

it('Adding removing patterns from design policy', function () {
Expand All @@ -121,9 +136,18 @@ describe('Page - Authoring', function () {
cy.get('[name="./allowedCustomFormats/item0/customFormatKey"]').should('exist').then(() => {
cy.get('[name="./allowedCustomFormats/item0/customFormatKey"]').focus().type(customKey);
cy.get('[name="./allowedCustomFormats/item0/customFormatValue"]').focus().type(customValue);
cy.get('[title="Done"]').click();
cy.intercept('POST', '**' + telephoneInputPolicyPath + '*').as('saveTelephonePolicy');
cy.get('[title="Done"]:visible').click();
cy.wait('@saveTelephonePolicy').its('response.statusCode').should('be.oneOf', [200, 201]);
cy.get(bemDesignDialog + ':visible').should('not.exist');
const contextPath = Cypress.env('crx.contextPath') || '';
cy.request(contextPath + telephoneInputPolicyPath + '.infinity.json')
.its('body.allowedCustomFormats.item0')
.should('include', {customFormatKey: customKey, customFormatValue: customValue});
}).then(() => {
cy.openSiteAuthoring(authoringPagePath);
cy.selectLayer('Edit');
cy.cleanTest(telephoneInputDrop);
dropTelephoneInputInContainer();
cy.openEditableToolbar(sitesSelectors.overlays.overlay.component + telephoneInputEditPathSelector);
cy.invokeEditableAction("[data-action='CONFIGURE']");
Expand All @@ -132,6 +156,7 @@ describe('Page - Authoring', function () {
cy.get('.cmp-adaptiveform-telephoneinput__validationpattern select').select(customKey, {force: true}).then(() => {
cy.get('.cmp-adaptiveform-telephoneinput__validationformat').should('have.value', customValue);
cy.get('.cq-dialog-cancel').click();
cy.get(bemEditDialog + ':visible').should('not.exist');
cy.deleteComponentByPath(telephoneInputDrop);
})
});
Expand Down
Loading