From e144a60a10eba60a77b1e2be068c7d2aa7b83e01 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 6 Mar 2026 00:45:42 +0000 Subject: [PATCH 01/15] Add support elements for Linked Data Editor settings management test suite --- cypress/support/api/linkedDataEditor.js | 50 ++- cypress/support/e2e.js | 1 + .../fragments/linked-data/linkedDataEditor.js | 8 + .../linked-data/manageProfileSettings.js | 349 ++++++++++++++++++ package.json | 4 +- 5 files changed, 405 insertions(+), 7 deletions(-) create mode 100644 cypress/support/fragments/linked-data/manageProfileSettings.js diff --git a/cypress/support/api/linkedDataEditor.js b/cypress/support/api/linkedDataEditor.js index 62e29b7dcf..7464068c9a 100644 --- a/cypress/support/api/linkedDataEditor.js +++ b/cypress/support/api/linkedDataEditor.js @@ -1,12 +1,50 @@ -Cypress.Commands.add('setPrefferedProfileForUser', () => { - // id is hardcoded as 3 on purpose - API will automatically identify the user ID from the folioAccessToken cookie. - const prefferedProfile = { - id: 3, - resourceType: 'http://bibfra.me/vocab/lite/Instance', +// For *ForUser commands, the API will automatically identify the +// user ID from the folioAccessToken cookie. + +Cypress.Commands.add('getAllPreferredProfilesForUser', () => { + return cy.okapiRequest({ + method: 'GET', + path: 'linked-data/profile/preferred', + }).then((response) => response.body); +}); + +Cypress.Commands.add('getPreferredProfileForUser', (resourceTypeURL) => { + return cy.okapiRequest({ + method: 'GET', + path: `linked-data/profile/preferred?resourceType=${resourceTypeURL}`, + }).then((response) => response.body); +}); + +Cypress.Commands.add('setPreferredProfileForUser', (id, resourceTypeURL) => { + const preferredProfile = { + id, + resourceType: resourceTypeURL, }; cy.okapiRequest({ method: 'POST', path: 'linked-data/profile/preferred', - body: prefferedProfile, + body: preferredProfile, + }); +}); + +Cypress.Commands.add('deletePreferredProfileForUser', (resourceTypeURL) => { + cy.okapiRequest({ + method: 'DELETE', + path: `linked-data/profile/preferred?resourceType=${resourceTypeURL}`, + }); +}); + +Cypress.Commands.add('getProfileSettingsForUser', (id) => { + return cy.okapiRequest({ + method: 'GET', + path: `linked-data/profile/settings/${id}`, + }).then((response) => response.body); +}); + +Cypress.Commands.add('setProfileSettingsForUser', (id, settings) => { + cy.okapiRequest({ + method: 'POST', + path: `linked-data/profile/settings/${id}`, + body: JSON.stringify(settings), }); }); diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js index b0e3cb5c54..b2af54f5f7 100644 --- a/cypress/support/e2e.js +++ b/cypress/support/e2e.js @@ -18,6 +18,7 @@ import './inventory'; import './users'; import 'cypress-file-upload'; import 'cypress-recurse/commands'; +import 'cypress-real-events/support'; import './commands'; registerCypressGrep(); diff --git a/cypress/support/fragments/linked-data/linkedDataEditor.js b/cypress/support/fragments/linked-data/linkedDataEditor.js index e889bfcc7c..4b1993f4ef 100644 --- a/cypress/support/fragments/linked-data/linkedDataEditor.js +++ b/cypress/support/fragments/linked-data/linkedDataEditor.js @@ -15,6 +15,9 @@ const newResourceButton = Button({ const compareSelectedButton = Button({ dataTestID: 'resources-actions-dropdown__option-ld.compareSelected', }); +const manageProfileSettingsButton = Button({ + dataTestID: 'resources-actions-dropdown__option-ld.manageProfileSettings', +}); const searchSelect = "//select[@id='id-search-select']"; const searchButton = Button({ dataTestID: 'id-search-button' }); const workPreviewPanel = "//div[@class='preview-panel']"; @@ -63,6 +66,11 @@ export default { cy.do(newHubButton.click()); }, + openManageProfileSettings: () => { + cy.do(actionsWorkButton.click()); + cy.do(manageProfileSettingsButton.click()); + }, + editWork: () => { cy.xpath("//div[@class='full-display-control-panel']//button[text()='Edit work']").click(); EditResource.waitLoading(); diff --git a/cypress/support/fragments/linked-data/manageProfileSettings.js b/cypress/support/fragments/linked-data/manageProfileSettings.js new file mode 100644 index 0000000000..2178f619be --- /dev/null +++ b/cypress/support/fragments/linked-data/manageProfileSettings.js @@ -0,0 +1,349 @@ + +const manageProfileSettingsPage = "//div[@data-testid='manage-profile-settings']"; +const profilesSection = "//div[@data-testid='profiles-list']"; +const editorSection = "//div[@data-testid='profile-settings-editor']"; + +const saveAndCloseButton = "//button[@data-testid='save-and-close']"; +const saveAndKeepEditingButton = "//button[@data-testid='save-and-keep-editing']"; +const closeButton = "//button[@data-testid='nav-close-button']"; + +const preferredProfileCheckbox = "//input[@data-testid='type-default-setting']"; +const defaultProfileSettingsRadio = "//input[@data-testid='settings-active-default']"; +const customProfileSettingsRadio = "//input[@data-testid='settings-active-custom']"; + +const selectedList = "//div[@data-testid='selected-component-list']"; +const unusedList = "//div[@data-testid='unused-component-list']"; +const profileComponent = "div.component"; +const draggingComponent = "//div[contains(@class, 'component') and contains(@class, 'dragging')]" +const componentActivateMenu = "//button[@data-testid='activate-menu']"; +const componentMoveAction = "//button[@data-testid='move-action']"; +const componentNudgeUp = "//button[@data-testid='nudge-up']"; +const componentNudgeDown = "//button[@data-testid='nudge-down']"; + +const modalUnsaved = "//div[@data-testid='modal-close-profile-settings']"; +const modalUnused = "//div[@data-testid='modal-save-unused-profile-components']"; +const modalCloseButton = ".//button[@class='close-button']"; +const modalCancelButton = ".//button[@data-testid='modal-button-cancel']"; +const modalSubmitButton = ".//button[@data-testid='modal-button-submit']"; +const loaderOverlay = "div.loader-overlay"; + +const component = (id, selector, sub) => { + return `${sub ? '.' : ''}//div[@data-testid='component-${id}']${selector}`; +}; + +const center = (el) => { + const r = el.getBoundingClientRect(); + return { x: Math.floor(r.left + r.width / 2), y: Math.floor(r.top + r.height / 2) }; +}; + +const dragDrop = (dragTarget, dragTargetList, dropTarget, dropTargetList) => { + cy.xpath(dragTargetList) + .xpath(component(dragTarget, '', true)).then(($drag) => { + cy.xpath(dropTargetList) + .xpath(component(dropTarget, '', true)).then(($drop) => { + const end = center($drop[0]); + + // The dnd-kit DragOverlay makes things work more smoothly, but in + // this context it adds an extra burden of replacing the original + // object with the drag overlay, which may have a markedly different + // position compared to the original object. We need to calculate + // the difference between them in order to move back to the start, + // before finally moving to the target object. + cy.wrap($drag) + .realMouseMove(0, 0, { position: 'center', steps: 6 }) + .realMouseDown({ button: 'left' }) + .realMouseMove(0, 1, { position: 'center', steps: 2 }) + .then(($el) => { + const doc = $el[0].ownerDocument; + const overlay = doc.querySelector('.drag-overlay') + const ov = center(overlay); + + const dx = end.x - ov.x; + const dy = end.y - ov.y; + + return { dx, dy }; + }).then(({ dx, dy }) => { + cy.wrap($drag) + .realMouseMove(dx, dy, { position: 'center', steps: 14 }) + .wait(200) + .realMouseUp({ button: 'left' }); + }); + }); + }); +}; + +export default { + waitMainLoading: () => { + cy.xpath(manageProfileSettingsPage).should('be.visible'); + cy.xpath(saveAndCloseButton).should('be.disabled'); + cy.xpath(saveAndKeepEditingButton).should('be.disabled'); + }, + + waitProfilesLoading: () => { + cy.xpath(profilesSection).should('be.visible'); + }, + + waitEditorLoading: () => { + cy.xpath(editorSection).should('be.visible'); + cy.get(loaderOverlay).should('not.exist'); + }, + + clickCloseButton: () => { + cy.xpath(closeButton).should('be.visible'); + cy.do(cy.xpath(closeButton).click()); + }, + + clickCloseButtonNoChanges: () => { + cy.xpath(closeButton).should('be.visible'); + cy.do(cy.xpath(closeButton).click()); + cy.xpath(manageProfileSettingsPage).should('not.exist'); + }, + + saveAndClose: () => { + cy.xpath(saveAndCloseButton).should('be.visible').and('be.enabled'); + cy.do(cy.xpath(saveAndCloseButton).click()); + }, + + saveAndKeepEditing: () => { + cy.xpath(saveAndKeepEditingButton).should('be.visible').and('be.enabled'); + cy.do(cy.xpath(saveAndKeepEditingButton).click()); + }, + + togglePreferredProfile: () => { + cy.xpath(preferredProfileCheckbox).should('be.visible'); + cy.do(cy.xpath(preferredProfileCheckbox).click()); + }, + + verifyPreferredProfile: (enabled) => { + if (enabled) { + cy.xpath(preferredProfileCheckbox).should('be.checked'); + } else { + cy.xpath(preferredProfileCheckbox).should('not.be.checked'); + } + }, + + selectDefaultSettings: () => { + cy.xpath(defaultProfileSettingsRadio).should('be.visible'); + cy.do(cy.xpath(defaultProfileSettingsRadio).click()); + }, + + selectCustomSettings: () => { + cy.xpath(customProfileSettingsRadio).should('be.visible'); + cy.do(cy.xpath(customProfileSettingsRadio).click()); + }, + + verifyDefaultSettingsSelected: () => { + cy.xpath(defaultProfileSettingsRadio).should('be.checked'); + }, + + verifyCustomSettingsSelected: () => { + cy.xpath(customProfileSettingsRadio).should('be.checked'); + }, + + selectProfile: (name) => { + cy.do( + cy.contains('button', name) + .scrollIntoView() + .should('be.visible') + .click(), + ); + }, + + moveComponentToOppositeListButton: (id) => { + cy.do( + cy.xpath(component(id, componentActivateMenu)) + .should('be.visible') + .click(), + cy.xpath(component(id, componentMoveAction)) + .should('be.visible') + .click(), + ); + }, + + nudgeComponentUpButton: (id) => { + cy.do( + cy.xpath(component(id, componentNudgeUp)) + .should('be.visible') + .click(), + ); + }, + + nudgeComponentDownButton: (id) => { + cy.do( + cy.xpath(component(id, componentNudgeDown)) + .should('be.visible') + .click(), + ); + }, + + verifySelectedComponentPosition: (id, position) => { + cy.xpath(selectedList).get(profileComponent).eq(position-1) + .invoke('attr', 'data-testid').should('eq', `component-${id}`); + if (position === 1) { + cy.xpath(component(id, componentNudgeUp)) + .should('not.exist'); + } + cy.xpath(selectedList).get(profileComponent).its('length').then((length) => { + if (position === length) { + cy.xpath(component(id, componentNudgeDown)) + .should('not.exist'); + } + }); + }, + + verifyUnusedComponentPosition: (id, position) => { + cy.xpath(unusedList).get(profileComponent).eq(position-1) + .invoke('attr', 'data-testid').should('eq', `component-${id}`); + }, + + dragUnusedComponentAndCancel: (position) => { + cy.do( + cy.xpath(unusedList) + .get(profileComponent) + .eq(position-1) + .realMouseDown({ button: 'left', position: 'center' }) + .realMouseMove(0, 10, { position: 'center' }) + .wait(200), + cy.realPress('{esc}'), + ); + }, + + dragSelectedComponentAndCancel: (position) => { + cy.do( + cy.xpath(selectedList) + .get(profileComponent) + .eq(position-1) + .realMouseDown({ button: 'left', position: 'center' }) + .realMouseMove(0, 10, { position: 'center' }) + .wait(200), + cy.realPress('{esc}'), + ); + }, + + dragReorderSelectedComponent: (id, targetId) => { + dragDrop(id, selectedList, targetId, selectedList); + }, + + // test + dragReorderUnusedComponent: (id, targetId) => { + dragDrop(id, unusedList, targetId, unusedList); + }, + + // test + dragSelectedToUnused: (id, targetId) => { + dragDrop(id, selectedList, targetId, unusedList); + }, + + // test + dragSelectedToUnusedContainer: (id) => { + // div.unused-container + }, + + // test + dragUnusedToSelected: (id, targetId) => { + dragDrop(id, unusedList, targetId, selectedList); + }, + + // test + dragComponentToUndroppableRegion: (id) => { + + }, + + // test + keyboardReorderUnusedComponent: () => { + + }, + + // test + keyboardDragUnusedComponentAndCancel: (position) => { + // TODO + // focus on element somehow? + cy.do( + cy.xpath(unusedList) + .get(profileComponent) + .eq(position-1) + .realMouseDown({ button: 'left', position: 'center' }) + .realMouseMove(0, 10, { position: 'center' }) + .wait(200), + cy.realPress(' '), + cy.realPress('{downarrow}'), + cy.realPress('{downarrow}'), + cy.realPress('{esc}'), + ); + }, + + // test + keyboardDragSelectedComponentAndCancel: (position) => { + // TODO + // focus on element somehow? + cy.do( + cy.xpath(selectedList) + .get(profileComponent) + .eq(position-1) + .realMouseDown({ button: 'left', position: 'center' }) + .realMouseMove(0, 10, { position: 'center' }) + .wait(200), + cy.realPress(' '), + cy.realPress('{downarrow}'), + cy.realPress('{downarrow}'), + cy.realPress('{esc}'), + ); + }, + + verifyModalUnsavedOpen: () => { + cy.xpath(modalUnsaved).should('be.visible'); + }, + + modalUnsavedClose: () => { + cy.xpath(modalUnsaved) + .xpath(modalCloseButton) + .should('be.enabled') + .click(); + cy.xpath(modalUnsaved).should('not.exist'); + }, + + modalUnsavedContinueWithoutSaving: () => { + cy.xpath(modalUnsaved) + .xpath(modalCancelButton) + .should('be.enabled') + .click(); + cy.xpath(modalUnsaved).should('not.exist'); + }, + + modalUnsavedContinueWithSaving: () => { + cy.xpath(modalUnsaved) + .xpath(modalSubmitButton) + .should('be.enabled') + .click(); + cy.xpath(modalUnsaved).should('not.exist'); + }, + + verifyModalUnusedOpen: () => { + cy.xpath(modalUnused).should('be.visible'); + }, + + modalUnusedClose: () => { + cy.xpath(modalUnused) + .xpath(modalCloseButton) + .should('be.enabled') + .click(); + cy.xpath(modalUnused).should('not.exist') + }, + + modalUnusedCancel: () => { + cy.xpath(modalUnused) + .xpath(modalCancelButton) + .should('be.enabled') + .click(); + cy.xpath(modalUnused).should('not.exist') + }, + + // test more thoroughly - leave and come back + modalUnusedSave: () => { + cy.xpath(modalUnused) + .xpath(modalSubmitButton) + .should('be.enabled') + .click(); + cy.xpath(modalUnused).should('not.exist') + }, +}; \ No newline at end of file diff --git a/package.json b/package.json index af21de6dc9..816f70b004 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "sharp": "0.32.6", "tsx": "3.14.0", "glob": "10.4.5", - "eslint-visitor-keys": "4.2.1" + "eslint-visitor-keys": "4.2.1" }, "dependencies": { "@babel/core": "^7.19.3", @@ -51,6 +51,7 @@ "@interactors/with-cypress": "1.0.0", "@reportportal/agent-js-cypress": ">=5.3.1 <6.0.0", "@shelex/cypress-allure-plugin": "^2.40.0", + "ally.js": "^1.4.1", "axe-core": "4.3.3", "axios": "^1.7.7", "babel-loader": "^9.2.1", @@ -59,6 +60,7 @@ "cypress-downloadfile": "^1.2.1", "cypress-file-upload": "^5.0.8", "cypress-plugin-tab": "^1.0.5", + "cypress-real-events": "^1.15.0", "cypress-recurse": "^1.13.1", "cypress-testrail-simple": "^3.1.0", "cypress-xpath": "^1.6.2", From 55b72410459d60b8973fb116c1f9ae23e514048f Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Mon, 9 Mar 2026 22:37:42 +0000 Subject: [PATCH 02/15] Add mechanisms to adjust for Cypress-specific DnDKit overlay rendering issues - move the translated overlay back to the original position for mouse and keyboard, before performing the requested action --- .../linked-data/manageProfileSettings.js | 286 ++++++++++++------ 1 file changed, 200 insertions(+), 86 deletions(-) diff --git a/cypress/support/fragments/linked-data/manageProfileSettings.js b/cypress/support/fragments/linked-data/manageProfileSettings.js index 2178f619be..e7bdef23f9 100644 --- a/cypress/support/fragments/linked-data/manageProfileSettings.js +++ b/cypress/support/fragments/linked-data/manageProfileSettings.js @@ -1,4 +1,3 @@ - const manageProfileSettingsPage = "//div[@data-testid='manage-profile-settings']"; const profilesSection = "//div[@data-testid='profiles-list']"; const editorSection = "//div[@data-testid='profile-settings-editor']"; @@ -13,8 +12,8 @@ const customProfileSettingsRadio = "//input[@data-testid='settings-active-custom const selectedList = "//div[@data-testid='selected-component-list']"; const unusedList = "//div[@data-testid='unused-component-list']"; +const unusedContainer = "//div[@data-droppable-id='unused-container']"; const profileComponent = "div.component"; -const draggingComponent = "//div[contains(@class, 'component') and contains(@class, 'dragging')]" const componentActivateMenu = "//button[@data-testid='activate-menu']"; const componentMoveAction = "//button[@data-testid='move-action']"; const componentNudgeUp = "//button[@data-testid='nudge-up']"; @@ -36,42 +35,100 @@ const center = (el) => { return { x: Math.floor(r.left + r.width / 2), y: Math.floor(r.top + r.height / 2) }; }; -const dragDrop = (dragTarget, dragTargetList, dropTarget, dropTargetList) => { +const doDrag = (node, endCoords) => { + // The dnd-kit DragOverlay makes things work more smoothly, but in + // this context it adds an extra burden of replacing the original + // object with the drag overlay, which may have a markedly different + // position compared to the original object. We need to calculate + // the difference between them in order to move the overlay over the + // original start before finally moving to the target object. The + // mouse pointer does not actually move, so we have to acquire the + // position of the overlay to make the coordinate calculations. + cy.wrap(node) + .realMouseMove(0, 0, { position: 'center', steps: 6 }) + .realMouseDown({ button: 'left' }) + .realMouseMove(0, 1, { position: 'center', steps: 2 }) + .then(($el) => { + const doc = $el[0].ownerDocument; + const overlay = doc.querySelector('.drag-overlay') + const ov = center(overlay); + + const dx = endCoords.x - ov.x; + const dy = endCoords.y - ov.y; + + return { dx, dy }; + }).then(({ dx, dy }) => { + cy.wrap(node) + .realMouseMove(dx, dy, { position: 'center', steps: 14 }) + .wait(200) + .realMouseUp({ button: 'left' }); + }); +}; + +const dragDropGeneral = (dragTarget, dragTargetList, dropTarget) => { + cy.xpath(dragTargetList) + .xpath(component(dragTarget, '', true)).then((drag) => { + cy.xpath(dropTarget).then((drop) => { + const end = center(drop[0]); + doDrag(drag, end); + }); + }); +}; + +const dragDropList = (dragTarget, dragTargetList, dropTarget, dropTargetList) => { cy.xpath(dragTargetList) - .xpath(component(dragTarget, '', true)).then(($drag) => { + .xpath(component(dragTarget, '', true)).then((drag) => { cy.xpath(dropTargetList) - .xpath(component(dropTarget, '', true)).then(($drop) => { - const end = center($drop[0]); - - // The dnd-kit DragOverlay makes things work more smoothly, but in - // this context it adds an extra burden of replacing the original - // object with the drag overlay, which may have a markedly different - // position compared to the original object. We need to calculate - // the difference between them in order to move back to the start, - // before finally moving to the target object. - cy.wrap($drag) - .realMouseMove(0, 0, { position: 'center', steps: 6 }) - .realMouseDown({ button: 'left' }) - .realMouseMove(0, 1, { position: 'center', steps: 2 }) - .then(($el) => { - const doc = $el[0].ownerDocument; - const overlay = doc.querySelector('.drag-overlay') - const ov = center(overlay); - - const dx = end.x - ov.x; - const dy = end.y - ov.y; - - return { dx, dy }; - }).then(({ dx, dy }) => { - cy.wrap($drag) - .realMouseMove(dx, dy, { position: 'center', steps: 14 }) - .wait(200) - .realMouseUp({ button: 'left' }); - }); + .xpath(component(dropTarget, '', true)).then((drop) => { + const end = center(drop[0]); + doDrag(drag, end); }); }); }; +const getComponentPosition = (id, list) => { + return cy.xpath(list) + .find(profileComponent) + .then((els) => { + const idx = els.toArray().findIndex((el) => { + const val = el.getAttribute('data-testid'); + return val === `component-${id}`; + }); + return idx; + }); +}; + +const getListLength = (list) => { + return cy + .xpath(list) + .find(profileComponent) + .its('length') + .then((count) => { + return count; + }); +} + +const keyBackToStartingPosition = (list, listLength, initialPosition) => { + // Keyboard interactions suffer from the same recalibrating overlay + // position issue as mouse, and since the mouse isn't involved, it can't be + // rectified in the same way with a coordinate diff caluclation. But + // switching (non-required) fields between lists should return the horizontal + // positioning, then resetting with up arrows to the top of the list + // before finally using down arrows to reach the original position + // should help put it into the correct original starting position. + + if (list === 'selected') { + cy.realPress('ArrowLeft') + .realPress('ArrowRight'); + } else if (list === 'unused') { + cy.realPress('ArrowRight') + .realPress('ArrowLeft'); + } + + Cypress._.times(listLength, () => cy.realPress('ArrowUp', { pressDelay: 100 })); + Cypress._.times(initialPosition, () => cy.realPress('ArrowDown', { pressDelay: 100 })); +}; + export default { waitMainLoading: () => { cy.xpath(manageProfileSettingsPage).should('be.visible'); @@ -142,7 +199,7 @@ export default { selectProfile: (name) => { cy.do( - cy.contains('button', name) + cy.contains('button', new RegExp(`^${name}$`)) .scrollIntoView() .should('be.visible') .click(), @@ -176,14 +233,18 @@ export default { ); }, + verifySelectedComponent: (id) => { + cy.xpath(selectedList).xpath(component(id, '', true)).should('exist'); + }, + verifySelectedComponentPosition: (id, position) => { - cy.xpath(selectedList).get(profileComponent).eq(position-1) + cy.xpath(selectedList).find(profileComponent).eq(position-1) .invoke('attr', 'data-testid').should('eq', `component-${id}`); if (position === 1) { cy.xpath(component(id, componentNudgeUp)) .should('not.exist'); } - cy.xpath(selectedList).get(profileComponent).its('length').then((length) => { + cy.xpath(selectedList).find(profileComponent).its('length').then((length) => { if (position === length) { cy.xpath(component(id, componentNudgeDown)) .should('not.exist'); @@ -191,103 +252,157 @@ export default { }); }, + verifyUnusedComponent: (id) => { + cy.xpath(unusedList).xpath(component(id, '', true)).should('exist'); + }, + verifyUnusedComponentPosition: (id, position) => { - cy.xpath(unusedList).get(profileComponent).eq(position-1) + cy.xpath(unusedList).find(profileComponent).eq(position-1) .invoke('attr', 'data-testid').should('eq', `component-${id}`); }, dragUnusedComponentAndCancel: (position) => { cy.do( cy.xpath(unusedList) - .get(profileComponent) + .find(profileComponent) .eq(position-1) .realMouseDown({ button: 'left', position: 'center' }) .realMouseMove(0, 10, { position: 'center' }) .wait(200), - cy.realPress('{esc}'), + cy.realPress('Escape'), ); }, dragSelectedComponentAndCancel: (position) => { cy.do( cy.xpath(selectedList) - .get(profileComponent) + .find(profileComponent) .eq(position-1) .realMouseDown({ button: 'left', position: 'center' }) .realMouseMove(0, 10, { position: 'center' }) .wait(200), - cy.realPress('{esc}'), + cy.realPress('Escape'), ); }, dragReorderSelectedComponent: (id, targetId) => { - dragDrop(id, selectedList, targetId, selectedList); + dragDropList(id, selectedList, targetId, selectedList); }, - // test dragReorderUnusedComponent: (id, targetId) => { - dragDrop(id, unusedList, targetId, unusedList); + dragDropList(id, unusedList, targetId, unusedList); }, - // test dragSelectedToUnused: (id, targetId) => { - dragDrop(id, selectedList, targetId, unusedList); + dragDropList(id, selectedList, targetId, unusedList); }, - // test dragSelectedToUnusedContainer: (id) => { - // div.unused-container + dragDropGeneral(id, selectedList, unusedContainer); }, - // test dragUnusedToSelected: (id, targetId) => { - dragDrop(id, unusedList, targetId, selectedList); + dragDropList(id, unusedList, targetId, selectedList); }, - // test - dragComponentToUndroppableRegion: (id) => { - + dragSelectedToUndroppableRegion: (id) => { + dragDropGeneral(id, selectedList, profilesSection); }, - // test - keyboardReorderUnusedComponent: () => { - + dragUnusedToUndroppableRegion: (id) => { + dragDropGeneral(id, unusedList, profilesSection); }, - // test keyboardDragUnusedComponentAndCancel: (position) => { - // TODO - // focus on element somehow? - cy.do( - cy.xpath(unusedList) - .get(profileComponent) - .eq(position-1) - .realMouseDown({ button: 'left', position: 'center' }) - .realMouseMove(0, 10, { position: 'center' }) - .wait(200), - cy.realPress(' '), - cy.realPress('{downarrow}'), - cy.realPress('{downarrow}'), - cy.realPress('{esc}'), - ); + cy.xpath(unusedList) + .find(profileComponent) + .eq(position-1) + .focus() + .wait(200), + cy.realPress(' '), + cy.realPress('ArrowDown'), + cy.realPress('ArrowDown'), + cy.realPress('Escape'); }, - // test keyboardDragSelectedComponentAndCancel: (position) => { - // TODO - // focus on element somehow? - cy.do( - cy.xpath(selectedList) - .get(profileComponent) - .eq(position-1) - .realMouseDown({ button: 'left', position: 'center' }) - .realMouseMove(0, 10, { position: 'center' }) - .wait(200), - cy.realPress(' '), - cy.realPress('{downarrow}'), - cy.realPress('{downarrow}'), - cy.realPress('{esc}'), - ); + cy.xpath(selectedList) + .find(profileComponent) + .eq(position-1) + .focus() + .wait(200), + cy.realPress(' '), + cy.realPress('ArrowDown'), + cy.realPress('ArrowDown'), + cy.realPress('Escape'); + }, + + keyboardDragReorderSelectedComponent: (id, targetId) => { + getListLength(selectedList).then((length) => { + getComponentPosition(id, selectedList).then((focusedPosition) => { + getComponentPosition(targetId, selectedList).then((targetPosition) => { + cy.xpath(selectedList) + .xpath(component(id, '', true)) + .focus(); + cy.realPress(' '); + keyBackToStartingPosition('selected', length, focusedPosition-1); + if (focusedPosition < targetPosition) { + Cypress._.times(targetPosition - focusedPosition-1, () => cy.realPress('ArrowDown', { pressDelay: 100 })); + } else if (focusedPosition > targetPosition) { + Cypress._.times(focusedPosition - targetPosition-1, () => cy.realPress('ArrowUp', { pressDelay: 100 })); + } + cy.realPress('Enter'); + }); + }); + }); + }, + + keyboardDragReorderUnusedComponent: (id, targetId) => { + getListLength(unusedList).then((length) => { + getComponentPosition(id, unusedList).then((focusedPosition) => { + getComponentPosition(targetId, unusedList).then((targetPosition) => { + cy.xpath(unusedList) + .xpath(component(id, '', true)) + .focus(); + cy.realPress(' '); + keyBackToStartingPosition('unused', length, focusedPosition-1); + if (focusedPosition < targetPosition) { + Cypress._.times(targetPosition - focusedPosition-1, () => cy.realPress('ArrowDown', { pressDelay: 100 })); + } else if (focusedPosition > targetPosition) { + Cypress._.times(focusedPosition - targetPosition-1, () => cy.realPress('ArrowUp', { pressDelay: 100 })); + } + cy.realPress('Enter'); + }); + }); + }); + }, + + keyboardDragSelectedToUnused: (id) => { + getListLength(selectedList).then((length) => { + getComponentPosition(id, selectedList).then((focusedPosition) => { + cy.xpath(selectedList) + .xpath(component(id, '', true)) + .focus(); + cy.realPress(' '); + keyBackToStartingPosition('selected', length, focusedPosition); + cy.realPress('ArrowLeft', { pressDelay: 100 }); + cy.realPress('Enter'); + }); + }); + }, + + keyboardDragUnusedToSelected: (id, targetId) => { + getListLength(unusedList).then((length) => { + getComponentPosition(id, unusedList).then((focusedPosition) => { + cy.xpath(unusedList) + .xpath(component(id, '', true)) + .focus(); + cy.realPress(' '); + keyBackToStartingPosition('unused', length, focusedPosition); + cy.realPress('ArrowRight', { pressDelay: 100 }); + cy.realPress('Enter'); + }); + }); }, verifyModalUnsavedOpen: () => { @@ -338,7 +453,6 @@ export default { cy.xpath(modalUnused).should('not.exist') }, - // test more thoroughly - leave and come back modalUnusedSave: () => { cy.xpath(modalUnused) .xpath(modalSubmitButton) From e4d8faf54e5efbfb9b4ac33a3ca32a93beecc68e Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Tue, 10 Mar 2026 19:24:07 +0000 Subject: [PATCH 03/15] Clear data between tests, continue trying to make building blocks consistent --- .../manage-profile-settings/drag-drop.cy.js | 267 ++++++++++++++++++ cypress/support/api/linkedDataEditor.js | 14 + cypress/support/dictionary/permissions.js | 5 + .../linked-data/manageProfileSettings.js | 26 +- 4 files changed, 308 insertions(+), 4 deletions(-) create mode 100644 cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js diff --git a/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js b/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js new file mode 100644 index 0000000000..d6e4f5ab40 --- /dev/null +++ b/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js @@ -0,0 +1,267 @@ +import TopMenu from '../../../support/fragments/topMenu'; +import LinkedDataEditor from '../../../support/fragments/linked-data/linkedDataEditor'; +import { LDE_ROLES } from '../../../support/constants'; +import Users from '../../../support/fragments/users/users'; +import Permissions from '../../../support/dictionary/permissions'; +import ManageProfileSettings from '../../../support/fragments/linked-data/manageProfileSettings'; +import { id } from 'date-fns/locale'; + +let user; +const roleNames = [LDE_ROLES.CATALOGER, LDE_ROLES.CATALOGER_LDE]; +const profileUris = [ + 'http://bibfra.me/vocab/lite/Hub', + 'http://bibfra.me/vocab/lite/Instance', + 'http://bibfra.me/vocab/lite/Work', +]; + +// Assumes a viewport resolution greater than 720; less than 720, +// responsiveness adds a new screen to move between profiles list +// and settings editor. + +describe('Manage profile settings', () => { + before('Create test data', () => { + const roleIds = []; + cy.getAdminToken(); + + roleNames.forEach((roleName) => { + cy.getUserRoleIdByNameApi(roleName).then((roleId) => { + if (roleId) { + roleIds.push(roleId); + } + }); + }); + + cy.createTempUser([ + Permissions.linkedDataDeletePreferredProfile.gui, + ]).then((userProperties) => { + user = userProperties; + }); + + cy.then(() => { + if (roleIds.length > 0) { + cy.updateRolesForUserApi(user.userId, roleIds); + } + }); + }); + + after('Delete test data', () => { + cy.getAdminToken(); + + Users.deleteViaApi(user.userId); + }); + + beforeEach(() => { + cy.login(user.username, user.password, { + path: TopMenu.linkedDataEditor, + waiter: LinkedDataEditor.waitLoading, + authRefresh: true, + }); + }); + + afterEach(() => { + // Clear preferred profiles and reset profile settings to basic inactive state + profileUris.forEach((uri) => { + cy.getPreferredProfileForUser(uri).then((profiles) => { + if (profiles.length > 0) { + cy.deletePreferredProfileForUser(uri); + } + }); + cy.getProfileMetadataByResourceType(uri).then((profiles) => { + profiles.forEach((profile) => { + cy.setProfileSettingsForUser(profile.id, { + active: false, + children: [], + }); + }); + }) + }); + }); + + it.skip('settings are applied to linked data editor') + + it.skip('changes to settings persist'); + + it.only( + 'required components cannot be moved to unused list', + { tags: ['citation'] }, + () => { + LinkedDataEditor.openManageProfileSettings(); + ManageProfileSettings.waitMainLoading(); + ManageProfileSettings.waitProfilesLoading(); + + ManageProfileSettings.selectProfile('Serials'); + + ManageProfileSettings.keyboardDragSelectedToUnused('Profile:Instance:TitleInformation'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Instance:TitleInformation', 1); + + ManageProfileSettings.dragSelectedToUnusedContainer('Profile:Instance:TitleInformation'); + // Cypress may end up reordering, but the overall point is that it didn't move to unused. + ManageProfileSettings.verifySelectedComponent('Profile:Instance:TitleInformation'); + + ManageProfileSettings.moveComponentUnavailable('Profile:Instance:TitleInformation'); + ManageProfileSettings.verifySelectedComponent('Profile:Instance:TitleInformation'); + }, + ); + + it( + 'keyboard reordering and moving between lists', + { tags: ['citation'] }, + () => { + LinkedDataEditor.openManageProfileSettings(); + ManageProfileSettings.waitMainLoading(); + ManageProfileSettings.waitProfilesLoading(); + + ManageProfileSettings.selectProfile('Books'); + ManageProfileSettings.waitEditorLoading(); + + ManageProfileSettings.keyboardDragSelectedComponentAndCancel(3); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:Hubs', 3); + + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Work:DateOfWork'); + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Work:LanguageCode'); + ManageProfileSettings.keyboardDragUnusedComponentAndCancel(2); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Work:LanguageCode', 2); + + ManageProfileSettings.selectDefaultSettings(); + ManageProfileSettings.keyboardDragReorderSelectedComponent('Profile:Work:ContentType', 'Profile:Work:SupplementaryContent'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:ContentType', 6); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:SupplementaryContent', 7); + + ManageProfileSettings.selectDefaultSettings(); + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Work:DateOfWork'); + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Work:LanguageCode'); + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Work:ContentType'); + ManageProfileSettings.keyboardDragReorderUnusedComponent('Profile:Work:ContentType', 'Profile:Work:DateOfWork'); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Work:ContentType', 1); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Work:LanguageCode', 3); + + ManageProfileSettings.keyboardDragSelectedToUnused('Profile:Work:Hubs'); + ManageProfileSettings.verifyUnusedComponent('Profile:Work:Hubs'); + + ManageProfileSettings.keyboardDragUnusedToSelected('Profile:Work:DateOfWork'); + ManageProfileSettings.verifySelectedComponent('Profile:Work:DateOfWork'); + } + ); + + it( + 'mouse reordering and dragging between lists', + { tags: ['citation'] }, + () => { + LinkedDataEditor.openManageProfileSettings(); + ManageProfileSettings.waitMainLoading(); + ManageProfileSettings.waitProfilesLoading(); + + ManageProfileSettings.selectProfile('Hubs'); + ManageProfileSettings.waitEditorLoading(); + ManageProfileSettings.dragReorderSelectedComponent('Profile:Hub:CreatorOfHub', 'Profile:Hub:LanguageCode'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Hub:CreatorOfHub', 3); + + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Hub:CreatorOfHub'); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:CreatorOfHub', 1); + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Hub:LanguageCode'); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:LanguageCode', 2); + ManageProfileSettings.dragReorderUnusedComponent('Profile:Hub:CreatorOfHub', 'Profile:Hub:LanguageCode'); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:CreatorOfHub', 2); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:LanguageCode', 1); + + ManageProfileSettings.dragUnusedToSelected('Profile:Hub:LanguageCode', 'Profile:Hub:TitleInformation') + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:CreatorOfHub', 1); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Hub:LanguageCode', 1); + + ManageProfileSettings.dragSelectedToUnused('Profile:Hub:LanguageCode', 'Profile:Hub:CreatorOfHub') + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:LanguageCode', 1); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Hub:TitleInformation', 1); + + ManageProfileSettings.selectDefaultSettings(); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Hub:TitleInformation', 2); + ManageProfileSettings.dragSelectedToUnusedContainer('Profile:Hub:LanguageCode'); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:LanguageCode', 1); + ManageProfileSettings.dragSelectedToUnusedContainer('Profile:Hub:CreatorOfHub'); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:CreatorOfHub', 1); + + ManageProfileSettings.dragUnusedToUndroppableRegion('Profile:Hub:CreatorOfHub'); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:CreatorOfHub', 1); + + ManageProfileSettings.selectDefaultSettings(); + ManageProfileSettings.dragSelectedToUndroppableRegion('Profile:Hub:CreatorOfHub'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Hub:CreatorOfHub', 1); + }); + + it( + 'move component to other list by button press', + { tags: ['citation'] }, + () => { + LinkedDataEditor.openManageProfileSettings(); + ManageProfileSettings.waitMainLoading(); + ManageProfileSettings.waitProfilesLoading(); + + ManageProfileSettings.selectProfile('Hubs'); + ManageProfileSettings.dragSelectedComponentAndCancel(1); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Hub:CreatorOfHub', 1); + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Hub:CreatorOfHub'); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:CreatorOfHub', 1); + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Hub:CreatorOfHub'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Hub:CreatorOfHub', 3); + + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Hub:CreatorOfHub'); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:CreatorOfHub', 1); + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Hub:LanguageCode'); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:LanguageCode', 2); + ManageProfileSettings.dragUnusedComponentAndCancel(1); + ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:CreatorOfHub', 1); + + ManageProfileSettings.saveAndKeepEditing(); + ManageProfileSettings.verifyModalUnusedOpen(); + ManageProfileSettings.modalUnusedClose(); + ManageProfileSettings.saveAndKeepEditing(); + ManageProfileSettings.verifyModalUnusedOpen(); + ManageProfileSettings.modalUnusedCancel(); + ManageProfileSettings.saveAndKeepEditing(); + ManageProfileSettings.verifyModalUnusedOpen(); + ManageProfileSettings.modalUnusedSave(); + }); + + it( + 'nudge buttons in selected component list', + { tags: ['citation'] }, + () => { + LinkedDataEditor.openManageProfileSettings(); + ManageProfileSettings.waitMainLoading(); + ManageProfileSettings.waitProfilesLoading(); + + ManageProfileSettings.selectProfile('Rare Books'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Instance:StatementOfResponsibility', 2); + ManageProfileSettings.nudgeComponentUpButton('Profile:Instance:StatementOfResponsibility'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Instance:StatementOfResponsibility', 1); + ManageProfileSettings.nudgeComponentDownButton('Profile:Instance:StatementOfResponsibility'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Instance:StatementOfResponsibility', 2); + ManageProfileSettings.nudgeComponentDownButton('Profile:Instance:StatementOfResponsibility'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Instance:StatementOfResponsibility', 3); + }); + + it( + 'verifies preferred profile setting persists across profile selection changes', + { tags: ['citation'] }, + () => { + LinkedDataEditor.openManageProfileSettings(); + ManageProfileSettings.waitMainLoading(); + ManageProfileSettings.waitProfilesLoading(); + + ManageProfileSettings.selectProfile('Rare Books'); + ManageProfileSettings.waitEditorLoading(); + + ManageProfileSettings.verifyPreferredProfile(false); + ManageProfileSettings.togglePreferredProfile(); + ManageProfileSettings.verifyPreferredProfile(true); + + ManageProfileSettings.selectProfile('Hubs'); + ManageProfileSettings.verifyModalUnsavedOpen(); + ManageProfileSettings.modalUnsavedContinueWithSaving(); + + ManageProfileSettings.selectProfile('Rare Books'); + ManageProfileSettings.waitEditorLoading(); + ManageProfileSettings.verifyPreferredProfile(true); + ManageProfileSettings.togglePreferredProfile(); + ManageProfileSettings.verifyPreferredProfile(false); + }); +}); \ No newline at end of file diff --git a/cypress/support/api/linkedDataEditor.js b/cypress/support/api/linkedDataEditor.js index 7464068c9a..6d978e3c3f 100644 --- a/cypress/support/api/linkedDataEditor.js +++ b/cypress/support/api/linkedDataEditor.js @@ -1,10 +1,19 @@ // For *ForUser commands, the API will automatically identify the // user ID from the folioAccessToken cookie. +Cypress.Commands.add('getProfileMetadataByResourceType', (resourceTypeURL) => { + return cy.okapiRequest({ + method: 'GET', + path: `linked-data/profile/metadata?resourceType=${resourceTypeURL}`, + isDefaultSearchParamsRequired: false, + }).then((response) => response.body); +}); + Cypress.Commands.add('getAllPreferredProfilesForUser', () => { return cy.okapiRequest({ method: 'GET', path: 'linked-data/profile/preferred', + isDefaultSearchParamsRequired: false, }).then((response) => response.body); }); @@ -12,6 +21,7 @@ Cypress.Commands.add('getPreferredProfileForUser', (resourceTypeURL) => { return cy.okapiRequest({ method: 'GET', path: `linked-data/profile/preferred?resourceType=${resourceTypeURL}`, + isDefaultSearchParamsRequired: false, }).then((response) => response.body); }); @@ -24,6 +34,7 @@ Cypress.Commands.add('setPreferredProfileForUser', (id, resourceTypeURL) => { method: 'POST', path: 'linked-data/profile/preferred', body: preferredProfile, + isDefaultSearchParamsRequired: false, }); }); @@ -31,6 +42,7 @@ Cypress.Commands.add('deletePreferredProfileForUser', (resourceTypeURL) => { cy.okapiRequest({ method: 'DELETE', path: `linked-data/profile/preferred?resourceType=${resourceTypeURL}`, + isDefaultSearchParamsRequired: false, }); }); @@ -38,6 +50,7 @@ Cypress.Commands.add('getProfileSettingsForUser', (id) => { return cy.okapiRequest({ method: 'GET', path: `linked-data/profile/settings/${id}`, + isDefaultSearchParamsRequired: false, }).then((response) => response.body); }); @@ -46,5 +59,6 @@ Cypress.Commands.add('setProfileSettingsForUser', (id, settings) => { method: 'POST', path: `linked-data/profile/settings/${id}`, body: JSON.stringify(settings), + isDefaultSearchParamsRequired: false, }); }); diff --git a/cypress/support/dictionary/permissions.js b/cypress/support/dictionary/permissions.js index f79e1323f9..810c2934e1 100644 --- a/cypress/support/dictionary/permissions.js +++ b/cypress/support/dictionary/permissions.js @@ -1504,6 +1504,11 @@ export default { internal: 'ui-users.settings.departments.all', gui: 'Settings (Users): Can create, edit, view, and delete departments', }, + // Linked Data Editor + linkedDataDeletePreferredProfile: { + internal: 'linked-data.profiles.preferred.delete', + gui: 'Linked Data: Delete the preferred profile for a resource type for the current user', + }, ebsconetAll: { internal: 'ebsconet.all', diff --git a/cypress/support/fragments/linked-data/manageProfileSettings.js b/cypress/support/fragments/linked-data/manageProfileSettings.js index e7bdef23f9..48075aa158 100644 --- a/cypress/support/fragments/linked-data/manageProfileSettings.js +++ b/cypress/support/fragments/linked-data/manageProfileSettings.js @@ -210,18 +210,34 @@ export default { cy.do( cy.xpath(component(id, componentActivateMenu)) .should('be.visible') - .click(), + .focus() + .wait(100) + .realClick(), cy.xpath(component(id, componentMoveAction)) .should('be.visible') - .click(), + .focus() + .wait(100) + .realClick(), ); }, + moveComponentUnavailable: (id) => { + cy.xpath(component(id, componentActivateMenu)) + .should('be.visible') + .focus() + .wait(100) + .realClick(); + cy.xpath(component(id, componentMoveAction)) + .should('not.exist') + }, + nudgeComponentUpButton: (id) => { cy.do( cy.xpath(component(id, componentNudgeUp)) .should('be.visible') - .click(), + .focus() + .wait(100) + .realPress('Enter'), ); }, @@ -229,7 +245,9 @@ export default { cy.do( cy.xpath(component(id, componentNudgeDown)) .should('be.visible') - .click(), + .focus() + .wait(100) + .realPress('Enter'), ); }, From 307d80e9dafe9d9887241bea5c623c8c5b11070b Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Thu, 12 Mar 2026 09:40:01 -0700 Subject: [PATCH 04/15] Resolve consistency issues by waiting for overlay to be removed before proceeding to next step, also some waits to in-progress motions to account for animation --- .../linked-data/manageProfileSettings.js | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/cypress/support/fragments/linked-data/manageProfileSettings.js b/cypress/support/fragments/linked-data/manageProfileSettings.js index 48075aa158..60bc856ef2 100644 --- a/cypress/support/fragments/linked-data/manageProfileSettings.js +++ b/cypress/support/fragments/linked-data/manageProfileSettings.js @@ -14,6 +14,7 @@ const selectedList = "//div[@data-testid='selected-component-list']"; const unusedList = "//div[@data-testid='unused-component-list']"; const unusedContainer = "//div[@data-droppable-id='unused-container']"; const profileComponent = "div.component"; +const draggingComponent = "div.drag-overlay"; const componentActivateMenu = "//button[@data-testid='activate-menu']"; const componentMoveAction = "//button[@data-testid='move-action']"; const componentNudgeUp = "//button[@data-testid='nudge-up']"; @@ -41,9 +42,9 @@ const doDrag = (node, endCoords) => { // object with the drag overlay, which may have a markedly different // position compared to the original object. We need to calculate // the difference between them in order to move the overlay over the - // original start before finally moving to the target object. The - // mouse pointer does not actually move, so we have to acquire the - // position of the overlay to make the coordinate calculations. + // the target object. The mouse pointer does not actually move, so we + // have to acquire the position of the overlay to make the coordinate + // calculations. cy.wrap(node) .realMouseMove(0, 0, { position: 'center', steps: 6 }) .realMouseDown({ button: 'left' }) @@ -60,9 +61,11 @@ const doDrag = (node, endCoords) => { }).then(({ dx, dy }) => { cy.wrap(node) .realMouseMove(dx, dy, { position: 'center', steps: 14 }) - .wait(200) + .wait(500) .realMouseUp({ button: 'left' }); }); + cy.get(draggingComponent).should('not.exist') + .wait(1000); }; const dragDropGeneral = (dragTarget, dragTargetList, dropTarget) => { @@ -236,8 +239,9 @@ export default { cy.xpath(component(id, componentNudgeUp)) .should('be.visible') .focus() - .wait(100) - .realPress('Enter'), + .wait(100) + .click() + .wait(500), ); }, @@ -247,7 +251,8 @@ export default { .should('be.visible') .focus() .wait(100) - .realPress('Enter'), + .click() + .wait(500), ); }, @@ -303,10 +308,16 @@ export default { ); }, + // Note that dragging downwards will drag past the target by one, because + // the remainder of the list shifts upward in response to 'removing' the + // selected component. Plan accordingly. dragReorderSelectedComponent: (id, targetId) => { dragDropList(id, selectedList, targetId, selectedList); }, + // Note that dragging downwards will drag past the target by one, because + // the remainder of the list shifts upward in response to 'removing' the + // selected component. Plan accordingly. dragReorderUnusedComponent: (id, targetId) => { dragDropList(id, unusedList, targetId, unusedList); }, @@ -373,6 +384,8 @@ export default { }); }); }); + cy.get(draggingComponent).should('not.exist') + .wait(1000); }, keyboardDragReorderUnusedComponent: (id, targetId) => { @@ -393,6 +406,8 @@ export default { }); }); }); + cy.get(draggingComponent).should('not.exist') + .wait(1000); }, keyboardDragSelectedToUnused: (id) => { @@ -401,12 +416,14 @@ export default { cy.xpath(selectedList) .xpath(component(id, '', true)) .focus(); - cy.realPress(' '); + cy.realPress(' ', { pressDelay: 100 }); keyBackToStartingPosition('selected', length, focusedPosition); cy.realPress('ArrowLeft', { pressDelay: 100 }); cy.realPress('Enter'); }); }); + cy.get(draggingComponent).should('not.exist') + .wait(1000); }, keyboardDragUnusedToSelected: (id, targetId) => { @@ -415,12 +432,14 @@ export default { cy.xpath(unusedList) .xpath(component(id, '', true)) .focus(); - cy.realPress(' '); + cy.realPress(' ', { pressDelay: 100 }); keyBackToStartingPosition('unused', length, focusedPosition); cy.realPress('ArrowRight', { pressDelay: 100 }); cy.realPress('Enter'); }); }); + cy.get(draggingComponent).should('not.exist') + .wait(1000); }, verifyModalUnsavedOpen: () => { From 152248e14c67107f0d68f69a0738669a2a55f253 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Mon, 16 Mar 2026 17:56:02 -0700 Subject: [PATCH 05/15] Test that changes in profile settings are applied to the resource editor --- .../manage-profile-settings/drag-drop.cy.js | 123 +++++++++++++++++- .../fragments/linked-data/editResource.js | 12 ++ .../linked-data/manageProfileSettings.js | 6 + .../fragments/linked-data/workProfileModal.js | 4 + 4 files changed, 141 insertions(+), 4 deletions(-) diff --git a/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js b/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js index d6e4f5ab40..62b6a12cab 100644 --- a/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js +++ b/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js @@ -1,10 +1,11 @@ import TopMenu from '../../../support/fragments/topMenu'; import LinkedDataEditor from '../../../support/fragments/linked-data/linkedDataEditor'; +import WorkProfileModal from '../../../support/fragments/linked-data/workProfileModal'; +import EditResource from '../../../support/fragments/linked-data/editResource'; import { LDE_ROLES } from '../../../support/constants'; import Users from '../../../support/fragments/users/users'; import Permissions from '../../../support/dictionary/permissions'; import ManageProfileSettings from '../../../support/fragments/linked-data/manageProfileSettings'; -import { id } from 'date-fns/locale'; let user; const roleNames = [LDE_ROLES.CATALOGER, LDE_ROLES.CATALOGER_LDE]; @@ -77,11 +78,121 @@ describe('Manage profile settings', () => { }); }); - it.skip('settings are applied to linked data editor') + it.only( + 'settings are applied to linked data editor', + { tags: ['citation'] }, + () => { + // Modify settings + LinkedDataEditor.openManageProfileSettings(); + ManageProfileSettings.waitMainLoading(); + ManageProfileSettings.waitProfilesLoading(); + ManageProfileSettings.selectProfile('Books'); + ManageProfileSettings.waitEditorLoading(); + ManageProfileSettings.dragSelectedToUnusedContainer('Profile:Work:LanguageCode'); + ManageProfileSettings.verifyUnusedComponent('Profile:Work:LanguageCode'); + ManageProfileSettings.nudgeComponentDownButton('Profile:Work:CreatorOfWork'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:CreatorOfWork', 2); + ManageProfileSettings.nudgeComponentDownButton('Profile:Work:CreatorOfWork'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:CreatorOfWork', 3); + ManageProfileSettings.nudgeComponentDownButton('Profile:Work:CreatorOfWork'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:CreatorOfWork', 4); + ManageProfileSettings.saveAndClose(); + ManageProfileSettings.modalUnusedSave(); - it.skip('changes to settings persist'); + // Check that settings apply, and also toggle profile default + LinkedDataEditor.openNewResourceForm(); + WorkProfileModal.waitLoading(); + WorkProfileModal.checkOptionSelected('Books'); + WorkProfileModal.toggleDefaultProfile(); + WorkProfileModal.selectDefaultOption(); + EditResource.waitLoading(); + EditResource.checkSectionIsNotVisible('Profile::0__Work::0___languages::0'); + EditResource.checkSectionInPosition('Profile::0__Work::0___creatorReference::0', 4); + EditResource.clickCloseResourceButton(); + + /* Follow up required. For whatever reason, in the Cypress environment, + re-opening profile settings clearly shows the correct profile settings + on initial load, but somehow the 'default' radio is then selected, and all + settings are cleared as a result. This does not happen during actual use. + Either this is some sort of timing bug manifesting in Cypress, or it's + an issue with Cypress. + + // Make more changes to settings + LinkedDataEditor.waitLoading(); + LinkedDataEditor.openManageProfileSettings(); + ManageProfileSettings.waitMainLoading(); + ManageProfileSettings.waitProfilesLoading(); + ManageProfileSettings.selectProfile('Books'); + ManageProfileSettings.waitEditorLoading(); + ManageProfileSettings.verifyPreferredProfile(true); + ManageProfileSettings.verifyCustomSettingsSelected(); + ManageProfileSettings.verifyUnusedComponent('Profile:Work:LanguageCode'); + ManageProfileSettings.dragUnusedToSelected('Profile:Work:LanguageCode', 'Profile:Work:DateOfWork'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:LanguageCode', 7); + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Work:IntendedAudience'); + ManageProfileSettings.verifyUnusedComponent('Profile:Work:IntendedAudience'); + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Work:ContentType'); + ManageProfileSettings.verifyUnusedComponent('Profile:Work:ContentType'); + ManageProfileSettings.saveAndClose(); + ManageProfileSettings.modalUnusedSave(); - it.only( + // Check that modifications applied + LinkedDataEditor.openNewResourceForm(); + // No modal this time, default was chosen + EditResource.waitLoading(); + EditResource.checkSectionInPosition('Profile::0__Work::0___languages::0', 7); + EditResource.checkSectionIsNotVisible('Profile::0__Work::0__targetAudience::0'); + EditResource.checkSectionIsNotVisible('Profile::0__Work::0__content::0'); + */ + } + ); + + it( + 'changes to settings persist', + { tags: ['citation'] }, + () => { + LinkedDataEditor.openManageProfileSettings(); + ManageProfileSettings.waitMainLoading(); + ManageProfileSettings.waitProfilesLoading(); + + ManageProfileSettings.selectProfile('Serials Work'); + ManageProfileSettings.waitEditorLoading(); + ManageProfileSettings.togglePreferredProfile(); + + ManageProfileSettings.dragSelectedToUnusedContainer('Profile:Work:ContentsNote'); + ManageProfileSettings.verifyUnusedComponent('Profile:Work:ContentsNote'); + ManageProfileSettings.dragSelectedToUnusedContainer('Profile:Work:SummaryNote'); + ManageProfileSettings.verifyUnusedComponent('Profile:Work:SummaryNote'); + ManageProfileSettings.dragReorderSelectedComponent('Profile:Work:TitleInformation', 'Profile:Work:CreatorOfWork'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:TitleInformation', 1); + ManageProfileSettings.nudgeComponentDownButton('Profile:Work:Hubs'); + ManageProfileSettings.nudgeComponentDownButton('Profile:Work:Hubs'); + ManageProfileSettings.nudgeComponentDownButton('Profile:Work:Hubs'); + ManageProfileSettings.nudgeComponentDownButton('Profile:Work:Hubs'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:Hubs', 7); + ManageProfileSettings.nudgeComponentUpButton('Profile:Work:ClassificationNumbers'); + ManageProfileSettings.nudgeComponentUpButton('Profile:Work:ClassificationNumbers'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:ClassificationNumbers', 12); + + // Save settings, swap to a different profile, then return to see that settings persisted. + ManageProfileSettings.saveAndKeepEditing(); + ManageProfileSettings.modalUnusedSave(); + ManageProfileSettings.selectProfile('Books'); + ManageProfileSettings.waitEditorLoading(); + ManageProfileSettings.verifyPreferredProfile(false); + + ManageProfileSettings.selectProfile('Serials Work'); + ManageProfileSettings.waitEditorLoading(); + ManageProfileSettings.verifyPreferredProfile(true); + ManageProfileSettings.verifyUnusedComponent('Profile:Work:ContentsNote'); + ManageProfileSettings.verifyUnusedComponent('Profile:Work:SummaryNote'); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:TitleInformation', 1); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:Hubs', 7); + ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:ClassificationNumbers', 12); + }, + ); + + it( 'required components cannot be moved to unused list', { tags: ['citation'] }, () => { @@ -91,6 +202,7 @@ describe('Manage profile settings', () => { ManageProfileSettings.selectProfile('Serials'); + // TitleInformation is a required field and cannot be hidden ManageProfileSettings.keyboardDragSelectedToUnused('Profile:Instance:TitleInformation'); ManageProfileSettings.verifySelectedComponentPosition('Profile:Instance:TitleInformation', 1); @@ -254,6 +366,9 @@ describe('Manage profile settings', () => { ManageProfileSettings.togglePreferredProfile(); ManageProfileSettings.verifyPreferredProfile(true); + ManageProfileSettings.selectProfile('Hubs'); + ManageProfileSettings.verifyModalUnsavedOpen(); + ManageProfileSettings.modalUnsavedClose(); ManageProfileSettings.selectProfile('Hubs'); ManageProfileSettings.verifyModalUnsavedOpen(); ManageProfileSettings.modalUnsavedContinueWithSaving(); diff --git a/cypress/support/fragments/linked-data/editResource.js b/cypress/support/fragments/linked-data/editResource.js index 72d53f3dc5..90c1f7f860 100644 --- a/cypress/support/fragments/linked-data/editResource.js +++ b/cypress/support/fragments/linked-data/editResource.js @@ -170,6 +170,18 @@ export default { .should('be.visible'); }, + checkSectionIsNotVisible(section) { + cy.xpath(`//div[@id="${section}"]`).should('not.be.visible'); + }, + + // 1-indexed + checkSectionInPosition(section, position) { + cy.xpath('//div[@id="edit-section"]/div[contains(@class,"edit-section-group")]/div[1]/div[1]') + .eq(position-1) + .invoke('attr', 'id') + .should('eq', section); + }, + clickCloseResourceButton() { cy.xpath('//button[@data-testid="close-record-button"]').click(); }, diff --git a/cypress/support/fragments/linked-data/manageProfileSettings.js b/cypress/support/fragments/linked-data/manageProfileSettings.js index 60bc856ef2..55d815a672 100644 --- a/cypress/support/fragments/linked-data/manageProfileSettings.js +++ b/cypress/support/fragments/linked-data/manageProfileSettings.js @@ -1,3 +1,5 @@ +//const manageProfileSettingsPage = "//main[@data-testid='manage-profile-settings']"; +//const profilesSection = "//section[@data-testid='profiles-list']"; const manageProfileSettingsPage = "//div[@data-testid='manage-profile-settings']"; const profilesSection = "//div[@data-testid='profiles-list']"; const editorSection = "//div[@data-testid='profile-settings-editor']"; @@ -10,6 +12,8 @@ const preferredProfileCheckbox = "//input[@data-testid='type-default-setting']"; const defaultProfileSettingsRadio = "//input[@data-testid='settings-active-default']"; const customProfileSettingsRadio = "//input[@data-testid='settings-active-custom']"; +//const selectedList = "//section[@data-testid='selected-component-list']"; +//const unusedList = "//section[@data-testid='unused-component-list']"; const selectedList = "//div[@data-testid='selected-component-list']"; const unusedList = "//div[@data-testid='unused-component-list']"; const unusedContainer = "//div[@data-droppable-id='unused-container']"; @@ -132,6 +136,7 @@ const keyBackToStartingPosition = (list, listLength, initialPosition) => { Cypress._.times(initialPosition, () => cy.realPress('ArrowDown', { pressDelay: 100 })); }; +// All position methods are 1-indexed. export default { waitMainLoading: () => { cy.xpath(manageProfileSettingsPage).should('be.visible'); @@ -496,5 +501,6 @@ export default { .should('be.enabled') .click(); cy.xpath(modalUnused).should('not.exist') + cy.wait(2000); }, }; \ No newline at end of file diff --git a/cypress/support/fragments/linked-data/workProfileModal.js b/cypress/support/fragments/linked-data/workProfileModal.js index 0481ab7945..3bd552e314 100644 --- a/cypress/support/fragments/linked-data/workProfileModal.js +++ b/cypress/support/fragments/linked-data/workProfileModal.js @@ -14,6 +14,10 @@ export default { } }); }, + toggleDefaultProfile() { + cy.xpath('//input[@type="checkbox"][@name="My default work profile"]') + .click(); + }, selectDefaultOption() { cy.get(profileModalSelector).then((modal) => { if (modal.is(':visible')) { From 930a1c2d62f3517eaf9cd7a16208d94703d14d18 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Wed, 18 Mar 2026 11:40:51 -0700 Subject: [PATCH 06/15] Address Lint issues --- .../manage-profile-settings/drag-drop.cy.js | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js b/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js index 62b6a12cab..aac06b2252 100644 --- a/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js +++ b/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js @@ -74,11 +74,11 @@ describe('Manage profile settings', () => { children: [], }); }); - }) + }); }); }); - it.only( + it( 'settings are applied to linked data editor', { tags: ['citation'] }, () => { @@ -144,7 +144,7 @@ describe('Manage profile settings', () => { EditResource.checkSectionIsNotVisible('Profile::0__Work::0__targetAudience::0'); EditResource.checkSectionIsNotVisible('Profile::0__Work::0__content::0'); */ - } + }, ); it( @@ -252,7 +252,7 @@ describe('Manage profile settings', () => { ManageProfileSettings.keyboardDragUnusedToSelected('Profile:Work:DateOfWork'); ManageProfileSettings.verifySelectedComponent('Profile:Work:DateOfWork'); - } + }, ); it( @@ -276,11 +276,11 @@ describe('Manage profile settings', () => { ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:CreatorOfHub', 2); ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:LanguageCode', 1); - ManageProfileSettings.dragUnusedToSelected('Profile:Hub:LanguageCode', 'Profile:Hub:TitleInformation') + ManageProfileSettings.dragUnusedToSelected('Profile:Hub:LanguageCode', 'Profile:Hub:TitleInformation'); ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:CreatorOfHub', 1); ManageProfileSettings.verifySelectedComponentPosition('Profile:Hub:LanguageCode', 1); - ManageProfileSettings.dragSelectedToUnused('Profile:Hub:LanguageCode', 'Profile:Hub:CreatorOfHub') + ManageProfileSettings.dragSelectedToUnused('Profile:Hub:LanguageCode', 'Profile:Hub:CreatorOfHub'); ManageProfileSettings.verifyUnusedComponentPosition('Profile:Hub:LanguageCode', 1); ManageProfileSettings.verifySelectedComponentPosition('Profile:Hub:TitleInformation', 1); @@ -297,7 +297,8 @@ describe('Manage profile settings', () => { ManageProfileSettings.selectDefaultSettings(); ManageProfileSettings.dragSelectedToUndroppableRegion('Profile:Hub:CreatorOfHub'); ManageProfileSettings.verifySelectedComponentPosition('Profile:Hub:CreatorOfHub', 1); - }); + }, + ); it( 'move component to other list by button press', @@ -331,7 +332,8 @@ describe('Manage profile settings', () => { ManageProfileSettings.saveAndKeepEditing(); ManageProfileSettings.verifyModalUnusedOpen(); ManageProfileSettings.modalUnusedSave(); - }); + }, + ); it( 'nudge buttons in selected component list', @@ -349,7 +351,8 @@ describe('Manage profile settings', () => { ManageProfileSettings.verifySelectedComponentPosition('Profile:Instance:StatementOfResponsibility', 2); ManageProfileSettings.nudgeComponentDownButton('Profile:Instance:StatementOfResponsibility'); ManageProfileSettings.verifySelectedComponentPosition('Profile:Instance:StatementOfResponsibility', 3); - }); + }, + ); it( 'verifies preferred profile setting persists across profile selection changes', @@ -365,7 +368,7 @@ describe('Manage profile settings', () => { ManageProfileSettings.verifyPreferredProfile(false); ManageProfileSettings.togglePreferredProfile(); ManageProfileSettings.verifyPreferredProfile(true); - + ManageProfileSettings.selectProfile('Hubs'); ManageProfileSettings.verifyModalUnsavedOpen(); ManageProfileSettings.modalUnsavedClose(); @@ -378,5 +381,6 @@ describe('Manage profile settings', () => { ManageProfileSettings.verifyPreferredProfile(true); ManageProfileSettings.togglePreferredProfile(); ManageProfileSettings.verifyPreferredProfile(false); - }); -}); \ No newline at end of file + }, + ); +}); From 5a30e59efbf029b829f856e13afa228305eabad9 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Wed, 18 Mar 2026 11:58:51 -0700 Subject: [PATCH 07/15] Continue addressing lint issues --- cypress/support/api/linkedDataEditor.js | 8 +- .../fragments/linked-data/editResource.js | 2 +- .../linked-data/manageProfileSettings.js | 86 ++++++++++--------- 3 files changed, 50 insertions(+), 46 deletions(-) diff --git a/cypress/support/api/linkedDataEditor.js b/cypress/support/api/linkedDataEditor.js index 6d978e3c3f..a50492d88e 100644 --- a/cypress/support/api/linkedDataEditor.js +++ b/cypress/support/api/linkedDataEditor.js @@ -56,9 +56,9 @@ Cypress.Commands.add('getProfileSettingsForUser', (id) => { Cypress.Commands.add('setProfileSettingsForUser', (id, settings) => { cy.okapiRequest({ - method: 'POST', - path: `linked-data/profile/settings/${id}`, - body: JSON.stringify(settings), - isDefaultSearchParamsRequired: false, + method: 'POST', + path: `linked-data/profile/settings/${id}`, + body: JSON.stringify(settings), + isDefaultSearchParamsRequired: false, }); }); diff --git a/cypress/support/fragments/linked-data/editResource.js b/cypress/support/fragments/linked-data/editResource.js index 90c1f7f860..d30d2957f0 100644 --- a/cypress/support/fragments/linked-data/editResource.js +++ b/cypress/support/fragments/linked-data/editResource.js @@ -177,7 +177,7 @@ export default { // 1-indexed checkSectionInPosition(section, position) { cy.xpath('//div[@id="edit-section"]/div[contains(@class,"edit-section-group")]/div[1]/div[1]') - .eq(position-1) + .eq(position - 1) .invoke('attr', 'id') .should('eq', section); }, diff --git a/cypress/support/fragments/linked-data/manageProfileSettings.js b/cypress/support/fragments/linked-data/manageProfileSettings.js index 55d815a672..639b032cc7 100644 --- a/cypress/support/fragments/linked-data/manageProfileSettings.js +++ b/cypress/support/fragments/linked-data/manageProfileSettings.js @@ -1,5 +1,5 @@ -//const manageProfileSettingsPage = "//main[@data-testid='manage-profile-settings']"; -//const profilesSection = "//section[@data-testid='profiles-list']"; +// const manageProfileSettingsPage = "//main[@data-testid='manage-profile-settings']"; +// const profilesSection = "//section[@data-testid='profiles-list']"; const manageProfileSettingsPage = "//div[@data-testid='manage-profile-settings']"; const profilesSection = "//div[@data-testid='profiles-list']"; const editorSection = "//div[@data-testid='profile-settings-editor']"; @@ -12,13 +12,13 @@ const preferredProfileCheckbox = "//input[@data-testid='type-default-setting']"; const defaultProfileSettingsRadio = "//input[@data-testid='settings-active-default']"; const customProfileSettingsRadio = "//input[@data-testid='settings-active-custom']"; -//const selectedList = "//section[@data-testid='selected-component-list']"; -//const unusedList = "//section[@data-testid='unused-component-list']"; +// const selectedList = "//section[@data-testid='selected-component-list']"; +// const unusedList = "//section[@data-testid='unused-component-list']"; const selectedList = "//div[@data-testid='selected-component-list']"; const unusedList = "//div[@data-testid='unused-component-list']"; const unusedContainer = "//div[@data-droppable-id='unused-container']"; -const profileComponent = "div.component"; -const draggingComponent = "div.drag-overlay"; +const profileComponent = 'div.component'; +const draggingComponent = 'div.drag-overlay'; const componentActivateMenu = "//button[@data-testid='activate-menu']"; const componentMoveAction = "//button[@data-testid='move-action']"; const componentNudgeUp = "//button[@data-testid='nudge-up']"; @@ -29,7 +29,7 @@ const modalUnused = "//div[@data-testid='modal-save-unused-profile-components']" const modalCloseButton = ".//button[@class='close-button']"; const modalCancelButton = ".//button[@data-testid='modal-button-cancel']"; const modalSubmitButton = ".//button[@data-testid='modal-button-submit']"; -const loaderOverlay = "div.loader-overlay"; +const loaderOverlay = 'div.loader-overlay'; const component = (id, selector, sub) => { return `${sub ? '.' : ''}//div[@data-testid='component-${id}']${selector}`; @@ -55,21 +55,23 @@ const doDrag = (node, endCoords) => { .realMouseMove(0, 1, { position: 'center', steps: 2 }) .then(($el) => { const doc = $el[0].ownerDocument; - const overlay = doc.querySelector('.drag-overlay') + const overlay = doc.querySelector('.drag-overlay'); const ov = center(overlay); const dx = endCoords.x - ov.x; const dy = endCoords.y - ov.y; return { dx, dy }; - }).then(({ dx, dy }) => { + }) + .then(({ dx, dy }) => { cy.wrap(node) .realMouseMove(dx, dy, { position: 'center', steps: 14 }) .wait(500) .realMouseUp({ button: 'left' }); - }); - cy.get(draggingComponent).should('not.exist') - .wait(1000); + } + ); + cy.get(draggingComponent).should('not.exist') + .wait(1000); }; const dragDropGeneral = (dragTarget, dragTargetList, dropTarget) => { @@ -113,7 +115,7 @@ const getListLength = (list) => { .then((count) => { return count; }); -} +}; const keyBackToStartingPosition = (list, listLength, initialPosition) => { // Keyboard interactions suffer from the same recalibrating overlay @@ -236,7 +238,7 @@ export default { .wait(100) .realClick(); cy.xpath(component(id, componentMoveAction)) - .should('not.exist') + .should('not.exist'); }, nudgeComponentUpButton: (id) => { @@ -255,7 +257,7 @@ export default { cy.xpath(component(id, componentNudgeDown)) .should('be.visible') .focus() - .wait(100) + .wait(100) .click() .wait(500), ); @@ -266,8 +268,9 @@ export default { }, verifySelectedComponentPosition: (id, position) => { - cy.xpath(selectedList).find(profileComponent).eq(position-1) - .invoke('attr', 'data-testid').should('eq', `component-${id}`); + cy.xpath(selectedList).find(profileComponent).eq(position - 1) + .invoke('attr', 'data-testid') + .should('eq', `component-${id}`); if (position === 1) { cy.xpath(component(id, componentNudgeUp)) .should('not.exist'); @@ -285,8 +288,9 @@ export default { }, verifyUnusedComponentPosition: (id, position) => { - cy.xpath(unusedList).find(profileComponent).eq(position-1) - .invoke('attr', 'data-testid').should('eq', `component-${id}`); + cy.xpath(unusedList).find(profileComponent).eq(position - 1) + .invoke('attr', 'data-testid') + .should('eq', `component-${id}`); }, dragUnusedComponentAndCancel: (position) => { @@ -305,7 +309,7 @@ export default { cy.do( cy.xpath(selectedList) .find(profileComponent) - .eq(position-1) + .eq(position - 1) .realMouseDown({ button: 'left', position: 'center' }) .realMouseMove(0, 10, { position: 'center' }) .wait(200), @@ -350,24 +354,24 @@ export default { keyboardDragUnusedComponentAndCancel: (position) => { cy.xpath(unusedList) .find(profileComponent) - .eq(position-1) + .eq(position - 1) .focus() - .wait(200), - cy.realPress(' '), - cy.realPress('ArrowDown'), - cy.realPress('ArrowDown'), + .wait(200); + cy.realPress(' '); + cy.realPress('ArrowDown'); + cy.realPress('ArrowDown'); cy.realPress('Escape'); }, keyboardDragSelectedComponentAndCancel: (position) => { cy.xpath(selectedList) .find(profileComponent) - .eq(position-1) + .eq(position - 1) .focus() - .wait(200), - cy.realPress(' '), - cy.realPress('ArrowDown'), - cy.realPress('ArrowDown'), + .wait(200); + cy.realPress(' '); + cy.realPress('ArrowDown'); + cy.realPress('ArrowDown'); cy.realPress('Escape'); }, @@ -379,11 +383,11 @@ export default { .xpath(component(id, '', true)) .focus(); cy.realPress(' '); - keyBackToStartingPosition('selected', length, focusedPosition-1); + keyBackToStartingPosition('selected', length, focusedPosition - 1); if (focusedPosition < targetPosition) { - Cypress._.times(targetPosition - focusedPosition-1, () => cy.realPress('ArrowDown', { pressDelay: 100 })); + Cypress._.times(targetPosition - focusedPosition - 1, () => cy.realPress('ArrowDown', { pressDelay: 100 })); } else if (focusedPosition > targetPosition) { - Cypress._.times(focusedPosition - targetPosition-1, () => cy.realPress('ArrowUp', { pressDelay: 100 })); + Cypress._.times(focusedPosition - targetPosition - 1, () => cy.realPress('ArrowUp', { pressDelay: 100 })); } cy.realPress('Enter'); }); @@ -401,11 +405,11 @@ export default { .xpath(component(id, '', true)) .focus(); cy.realPress(' '); - keyBackToStartingPosition('unused', length, focusedPosition-1); + keyBackToStartingPosition('unused', length, focusedPosition - 1); if (focusedPosition < targetPosition) { - Cypress._.times(targetPosition - focusedPosition-1, () => cy.realPress('ArrowDown', { pressDelay: 100 })); + Cypress._.times(targetPosition - focusedPosition - 1, () => cy.realPress('ArrowDown', { pressDelay: 100 })); } else if (focusedPosition > targetPosition) { - Cypress._.times(focusedPosition - targetPosition-1, () => cy.realPress('ArrowUp', { pressDelay: 100 })); + Cypress._.times(focusedPosition - targetPosition - 1, () => cy.realPress('ArrowUp', { pressDelay: 100 })); } cy.realPress('Enter'); }); @@ -431,7 +435,7 @@ export default { .wait(1000); }, - keyboardDragUnusedToSelected: (id, targetId) => { + keyboardDragUnusedToSelected: (id) => { getListLength(unusedList).then((length) => { getComponentPosition(id, unusedList).then((focusedPosition) => { cy.xpath(unusedList) @@ -484,7 +488,7 @@ export default { .xpath(modalCloseButton) .should('be.enabled') .click(); - cy.xpath(modalUnused).should('not.exist') + cy.xpath(modalUnused).should('not.exist'); }, modalUnusedCancel: () => { @@ -492,7 +496,7 @@ export default { .xpath(modalCancelButton) .should('be.enabled') .click(); - cy.xpath(modalUnused).should('not.exist') + cy.xpath(modalUnused).should('not.exist'); }, modalUnusedSave: () => { @@ -500,7 +504,7 @@ export default { .xpath(modalSubmitButton) .should('be.enabled') .click(); - cy.xpath(modalUnused).should('not.exist') + cy.xpath(modalUnused).should('not.exist'); cy.wait(2000); }, -}; \ No newline at end of file +}; From adf78f2d333ded7bbfcbe3b12a391ab87c495f43 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Wed, 18 Mar 2026 12:09:36 -0700 Subject: [PATCH 08/15] Continue addressing lint issues --- .../support/fragments/linked-data/manageProfileSettings.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cypress/support/fragments/linked-data/manageProfileSettings.js b/cypress/support/fragments/linked-data/manageProfileSettings.js index 639b032cc7..0c00ddc067 100644 --- a/cypress/support/fragments/linked-data/manageProfileSettings.js +++ b/cypress/support/fragments/linked-data/manageProfileSettings.js @@ -68,8 +68,7 @@ const doDrag = (node, endCoords) => { .realMouseMove(dx, dy, { position: 'center', steps: 14 }) .wait(500) .realMouseUp({ button: 'left' }); - } - ); + }); cy.get(draggingComponent).should('not.exist') .wait(1000); }; @@ -297,7 +296,7 @@ export default { cy.do( cy.xpath(unusedList) .find(profileComponent) - .eq(position-1) + .eq(position - 1) .realMouseDown({ button: 'left', position: 'center' }) .realMouseMove(0, 10, { position: 'center' }) .wait(200), From f129051ec63e33951e0635b0e0ff6ad21657ae16 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 20 Mar 2026 11:42:49 -0700 Subject: [PATCH 09/15] Adjust to modified structural tags --- .../fragments/linked-data/manageProfileSettings.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/cypress/support/fragments/linked-data/manageProfileSettings.js b/cypress/support/fragments/linked-data/manageProfileSettings.js index 0c00ddc067..b8ef0ff28d 100644 --- a/cypress/support/fragments/linked-data/manageProfileSettings.js +++ b/cypress/support/fragments/linked-data/manageProfileSettings.js @@ -1,7 +1,5 @@ -// const manageProfileSettingsPage = "//main[@data-testid='manage-profile-settings']"; -// const profilesSection = "//section[@data-testid='profiles-list']"; -const manageProfileSettingsPage = "//div[@data-testid='manage-profile-settings']"; -const profilesSection = "//div[@data-testid='profiles-list']"; +const manageProfileSettingsPage = "//main[@data-testid='manage-profile-settings']"; +const profilesSection = "//section[@data-testid='profiles-list']"; const editorSection = "//div[@data-testid='profile-settings-editor']"; const saveAndCloseButton = "//button[@data-testid='save-and-close']"; @@ -12,10 +10,8 @@ const preferredProfileCheckbox = "//input[@data-testid='type-default-setting']"; const defaultProfileSettingsRadio = "//input[@data-testid='settings-active-default']"; const customProfileSettingsRadio = "//input[@data-testid='settings-active-custom']"; -// const selectedList = "//section[@data-testid='selected-component-list']"; -// const unusedList = "//section[@data-testid='unused-component-list']"; -const selectedList = "//div[@data-testid='selected-component-list']"; -const unusedList = "//div[@data-testid='unused-component-list']"; +const selectedList = "//section[@data-testid='selected-component-list']"; +const unusedList = "//section[@data-testid='unused-component-list']"; const unusedContainer = "//div[@data-droppable-id='unused-container']"; const profileComponent = 'div.component'; const draggingComponent = 'div.drag-overlay'; From a0d357dd0e03fde6b6b73feee6791f0e4aad9ea5 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 24 Apr 2026 17:13:59 -0700 Subject: [PATCH 10/15] Update to match brand change --- cypress/support/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/support/constants.js b/cypress/support/constants.js index baf396bcbb..64878d97a4 100644 --- a/cypress/support/constants.js +++ b/cypress/support/constants.js @@ -1611,7 +1611,7 @@ export const LDE_ADVANCED_SEARCH_CONDITIONS = { export const LDE_ROLES = { CATALOGER: 'Cataloger', - CATALOGER_LDE: 'Cataloger - Linked Data Editor', + CATALOGER_LDE: 'Cataloger - Marigold', }; export const AUTHORIZATION_ROLE_TYPES = { From 22b394153e8f873b987bf98d9d502a1004f20d28 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sat, 25 Apr 2026 00:25:13 +0000 Subject: [PATCH 11/15] Update selectors to latest layout structure, add some waits. Remove cy.do since generally not using interactors. --- .../linked-data/manageProfileSettings.js | 115 ++++++++---------- 1 file changed, 53 insertions(+), 62 deletions(-) diff --git a/cypress/support/fragments/linked-data/manageProfileSettings.js b/cypress/support/fragments/linked-data/manageProfileSettings.js index b8ef0ff28d..b1035acb5f 100644 --- a/cypress/support/fragments/linked-data/manageProfileSettings.js +++ b/cypress/support/fragments/linked-data/manageProfileSettings.js @@ -20,9 +20,9 @@ const componentMoveAction = "//button[@data-testid='move-action']"; const componentNudgeUp = "//button[@data-testid='nudge-up']"; const componentNudgeDown = "//button[@data-testid='nudge-down']"; -const modalUnsaved = "//div[@data-testid='modal-close-profile-settings']"; -const modalUnused = "//div[@data-testid='modal-save-unused-profile-components']"; -const modalCloseButton = ".//button[@class='close-button']"; +const modalUnsaved = "//dialog[@data-testid='modal-close-profile-settings']"; +const modalUnused = "//dialog[@data-testid='modal-save-unused-profile-components']"; +const modalCloseButton = ".//button[contains(@class, 'close-button')]"; const modalCancelButton = ".//button[@data-testid='modal-button-cancel']"; const modalSubmitButton = ".//button[@data-testid='modal-button-submit']"; const loaderOverlay = 'div.loader-overlay'; @@ -152,28 +152,28 @@ export default { clickCloseButton: () => { cy.xpath(closeButton).should('be.visible'); - cy.do(cy.xpath(closeButton).click()); + cy.xpath(closeButton).click(); }, clickCloseButtonNoChanges: () => { cy.xpath(closeButton).should('be.visible'); - cy.do(cy.xpath(closeButton).click()); + cy.xpath(closeButton).click(); cy.xpath(manageProfileSettingsPage).should('not.exist'); }, saveAndClose: () => { cy.xpath(saveAndCloseButton).should('be.visible').and('be.enabled'); - cy.do(cy.xpath(saveAndCloseButton).click()); + cy.xpath(saveAndCloseButton).click(); }, saveAndKeepEditing: () => { cy.xpath(saveAndKeepEditingButton).should('be.visible').and('be.enabled'); - cy.do(cy.xpath(saveAndKeepEditingButton).click()); + cy.xpath(saveAndKeepEditingButton).click(); }, togglePreferredProfile: () => { cy.xpath(preferredProfileCheckbox).should('be.visible'); - cy.do(cy.xpath(preferredProfileCheckbox).click()); + cy.xpath(preferredProfileCheckbox).click(); }, verifyPreferredProfile: (enabled) => { @@ -185,13 +185,14 @@ export default { }, selectDefaultSettings: () => { + cy.xpath(defaultProfileSettingsRadio).scrollIntoView(); cy.xpath(defaultProfileSettingsRadio).should('be.visible'); - cy.do(cy.xpath(defaultProfileSettingsRadio).click()); + cy.xpath(defaultProfileSettingsRadio).click(); }, selectCustomSettings: () => { cy.xpath(customProfileSettingsRadio).should('be.visible'); - cy.do(cy.xpath(customProfileSettingsRadio).click()); + cy.xpath(customProfileSettingsRadio).click(); }, verifyDefaultSettingsSelected: () => { @@ -203,27 +204,23 @@ export default { }, selectProfile: (name) => { - cy.do( - cy.contains('button', new RegExp(`^${name}$`)) - .scrollIntoView() - .should('be.visible') - .click(), - ); + cy.contains('button', new RegExp(`^${name}$`)) + .scrollIntoView() + .should('be.visible') + .click(); }, moveComponentToOppositeListButton: (id) => { - cy.do( - cy.xpath(component(id, componentActivateMenu)) - .should('be.visible') - .focus() - .wait(100) - .realClick(), - cy.xpath(component(id, componentMoveAction)) - .should('be.visible') - .focus() - .wait(100) - .realClick(), - ); + cy.xpath(component(id, componentActivateMenu)) + .should('be.visible') + .focus() + .wait(100) + .realClick(); + cy.xpath(component(id, componentMoveAction)) + .should('be.visible') + .focus() + .wait(100) + .realClick(); }, moveComponentUnavailable: (id) => { @@ -237,25 +234,21 @@ export default { }, nudgeComponentUpButton: (id) => { - cy.do( - cy.xpath(component(id, componentNudgeUp)) - .should('be.visible') - .focus() - .wait(100) - .click() - .wait(500), - ); + cy.xpath(component(id, componentNudgeUp)) + .should('be.visible') + .focus() + .wait(100) + .click() + .wait(500); }, nudgeComponentDownButton: (id) => { - cy.do( - cy.xpath(component(id, componentNudgeDown)) - .should('be.visible') - .focus() - .wait(100) - .click() - .wait(500), - ); + cy.xpath(component(id, componentNudgeDown)) + .should('be.visible') + .focus() + .wait(100) + .click() + .wait(500); }, verifySelectedComponent: (id) => { @@ -289,27 +282,25 @@ export default { }, dragUnusedComponentAndCancel: (position) => { - cy.do( - cy.xpath(unusedList) - .find(profileComponent) - .eq(position - 1) - .realMouseDown({ button: 'left', position: 'center' }) - .realMouseMove(0, 10, { position: 'center' }) - .wait(200), - cy.realPress('Escape'), - ); + cy.xpath(unusedList) + .find(profileComponent) + .eq(position - 1) + .realMouseDown({ button: 'left', position: 'center' }) + .realMouseMove(0, 10, { position: 'center' }) + .wait(200); + cy.realPress('Escape') + .wait(50); }, dragSelectedComponentAndCancel: (position) => { - cy.do( - cy.xpath(selectedList) - .find(profileComponent) - .eq(position - 1) - .realMouseDown({ button: 'left', position: 'center' }) - .realMouseMove(0, 10, { position: 'center' }) - .wait(200), - cy.realPress('Escape'), - ); + cy.xpath(selectedList) + .find(profileComponent) + .eq(position - 1) + .realMouseDown({ button: 'left', position: 'center' }) + .realMouseMove(0, 10, { position: 'center' }) + .wait(200); + cy.realPress('Escape') + .wait(50); }, // Note that dragging downwards will drag past the target by one, because From 249386fff37fda78ed217dde4e0128a0118cc44c Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sat, 25 Apr 2026 00:32:07 +0000 Subject: [PATCH 12/15] Remove comments, should no longer be an obstacle; add waits after profile selection for better stability --- .../manage-profile-settings/drag-drop.cy.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js b/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js index aac06b2252..31106d00ca 100644 --- a/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js +++ b/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js @@ -110,14 +110,6 @@ describe('Manage profile settings', () => { EditResource.checkSectionInPosition('Profile::0__Work::0___creatorReference::0', 4); EditResource.clickCloseResourceButton(); - /* Follow up required. For whatever reason, in the Cypress environment, - re-opening profile settings clearly shows the correct profile settings - on initial load, but somehow the 'default' radio is then selected, and all - settings are cleared as a result. This does not happen during actual use. - Either this is some sort of timing bug manifesting in Cypress, or it's - an issue with Cypress. - - // Make more changes to settings LinkedDataEditor.waitLoading(); LinkedDataEditor.openManageProfileSettings(); ManageProfileSettings.waitMainLoading(); @@ -143,7 +135,6 @@ describe('Manage profile settings', () => { EditResource.checkSectionInPosition('Profile::0__Work::0___languages::0', 7); EditResource.checkSectionIsNotVisible('Profile::0__Work::0__targetAudience::0'); EditResource.checkSectionIsNotVisible('Profile::0__Work::0__content::0'); - */ }, ); @@ -309,6 +300,7 @@ describe('Manage profile settings', () => { ManageProfileSettings.waitProfilesLoading(); ManageProfileSettings.selectProfile('Hubs'); + ManageProfileSettings.waitEditorLoading(); ManageProfileSettings.dragSelectedComponentAndCancel(1); ManageProfileSettings.verifySelectedComponentPosition('Profile:Hub:CreatorOfHub', 1); ManageProfileSettings.moveComponentToOppositeListButton('Profile:Hub:CreatorOfHub'); @@ -344,6 +336,7 @@ describe('Manage profile settings', () => { ManageProfileSettings.waitProfilesLoading(); ManageProfileSettings.selectProfile('Rare Books'); + ManageProfileSettings.waitEditorLoading(); ManageProfileSettings.verifySelectedComponentPosition('Profile:Instance:StatementOfResponsibility', 2); ManageProfileSettings.nudgeComponentUpButton('Profile:Instance:StatementOfResponsibility'); ManageProfileSettings.verifySelectedComponentPosition('Profile:Instance:StatementOfResponsibility', 1); From 7bdc1e562a806460f8687bfa2d42cd32e788dead Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Sat, 25 Apr 2026 00:36:03 +0000 Subject: [PATCH 13/15] Update to Marigold --- .../e2e/linked-data/manage-profile-settings/drag-drop.cy.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js b/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js index 31106d00ca..03e6ec3daf 100644 --- a/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js +++ b/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js @@ -2,13 +2,13 @@ import TopMenu from '../../../support/fragments/topMenu'; import LinkedDataEditor from '../../../support/fragments/linked-data/linkedDataEditor'; import WorkProfileModal from '../../../support/fragments/linked-data/workProfileModal'; import EditResource from '../../../support/fragments/linked-data/editResource'; -import { LDE_ROLES } from '../../../support/constants'; +import { MARIGOLD_ROLES } from '../../../support/constants'; import Users from '../../../support/fragments/users/users'; import Permissions from '../../../support/dictionary/permissions'; import ManageProfileSettings from '../../../support/fragments/linked-data/manageProfileSettings'; let user; -const roleNames = [LDE_ROLES.CATALOGER, LDE_ROLES.CATALOGER_LDE]; +const roleNames = [MARIGOLD_ROLES.CATALOGER, MARIGOLD_ROLES.CATALOGER_MARIGOLD]; const profileUris = [ 'http://bibfra.me/vocab/lite/Hub', 'http://bibfra.me/vocab/lite/Instance', From c177220e79e06851ee04e05f854ca262d71471f7 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Tue, 28 Apr 2026 19:55:47 +0000 Subject: [PATCH 14/15] Update from div to dialog --- .../support/fragments/linked-data/assignAuthorityHubModal.js | 2 +- cypress/support/fragments/linked-data/closeResourceModal.js | 4 ++-- cypress/support/fragments/linked-data/instanceProfileModal.js | 2 +- .../support/fragments/linked-data/uncontrolledAuthModal.js | 2 +- cypress/support/fragments/linked-data/workProfileModal.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cypress/support/fragments/linked-data/assignAuthorityHubModal.js b/cypress/support/fragments/linked-data/assignAuthorityHubModal.js index a7918c56e6..0666d28a6a 100644 --- a/cypress/support/fragments/linked-data/assignAuthorityHubModal.js +++ b/cypress/support/fragments/linked-data/assignAuthorityHubModal.js @@ -1,6 +1,6 @@ import { Button, TextInput } from '../../../../interactors'; -const MARCAuthorityModal = "//div[@data-testid='modal']"; +const MARCAuthorityModal = "//dialog[@data-testid='modal']"; const searchOption = Button({ dataTestID: 'id-search-segment-button-authorities:search' }); const searchInput = TextInput({ id: 'id-search-textarea' }); const searchButton = Button({ dataTestID: 'id-search-button' }); diff --git a/cypress/support/fragments/linked-data/closeResourceModal.js b/cypress/support/fragments/linked-data/closeResourceModal.js index f4f165fcaa..4aebf2d5b1 100644 --- a/cypress/support/fragments/linked-data/closeResourceModal.js +++ b/cypress/support/fragments/linked-data/closeResourceModal.js @@ -1,6 +1,6 @@ const closeResourceModal = - "//div[@data-testid='modal']//h3[@class='title' and text()='Close resource']"; -const closeResourceModalByTestId = "//div[@data-testid='modal']"; + "//dialog[@data-testid='modal']//h3[@class='title' and text()='Close resource']"; +const closeResourceModalByTestId = "//dialog[@data-testid='modal']"; const modalContent = "//div[@data-testid='modal-close-record-content']"; const closeButton = "//button[@class='close-button']"; const yesButton = "//button[@data-testid='modal-button-cancel']"; diff --git a/cypress/support/fragments/linked-data/instanceProfileModal.js b/cypress/support/fragments/linked-data/instanceProfileModal.js index 2ab9c598e7..88c61a828d 100644 --- a/cypress/support/fragments/linked-data/instanceProfileModal.js +++ b/cypress/support/fragments/linked-data/instanceProfileModal.js @@ -1,4 +1,4 @@ -const instanceProfileModal = 'div[class="modal modal-choose-profile"]'; +const instanceProfileModal = 'dialog[class="modal modal-choose-profile"]'; export default { waitLoading() { diff --git a/cypress/support/fragments/linked-data/uncontrolledAuthModal.js b/cypress/support/fragments/linked-data/uncontrolledAuthModal.js index eecf348332..e8f6e49055 100644 --- a/cypress/support/fragments/linked-data/uncontrolledAuthModal.js +++ b/cypress/support/fragments/linked-data/uncontrolledAuthModal.js @@ -1,7 +1,7 @@ export default { closeIfDisplayed() { cy.get('body').then(($body) => { - if ($body.find('div[class="modal modal-uncontrolled-authorities-warning"]').length > 0) { + if ($body.find('dialog[class="modal modal-uncontrolled-authorities-warning"]').length > 0) { cy.xpath('//button[@data-testid="modal-button-submit"]').click(); cy.wait(1000); } diff --git a/cypress/support/fragments/linked-data/workProfileModal.js b/cypress/support/fragments/linked-data/workProfileModal.js index 3bd552e314..5440dda932 100644 --- a/cypress/support/fragments/linked-data/workProfileModal.js +++ b/cypress/support/fragments/linked-data/workProfileModal.js @@ -1,4 +1,4 @@ -const profileModalSelector = 'div[class="modal modal-choose-profile"]'; +const profileModalSelector = 'dialog[class="modal modal-choose-profile"]'; export default { waitLoading() { From c0692cfa863520abc81d71985fb1734be4590179 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Tue, 28 Apr 2026 20:57:16 +0000 Subject: [PATCH 15/15] Switch from LDE to Marigold, working test --- .../manage-profile-settings/drag-drop.cy.js | 46 ++++++++++--------- .../linked-data/manageProfileSettings.js | 2 + 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js b/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js index 03e6ec3daf..2fc7880d64 100644 --- a/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js +++ b/cypress/e2e/linked-data/manage-profile-settings/drag-drop.cy.js @@ -1,5 +1,5 @@ import TopMenu from '../../../support/fragments/topMenu'; -import LinkedDataEditor from '../../../support/fragments/linked-data/linkedDataEditor'; +import Marigold from '../../../support/fragments/linked-data/marigold'; import WorkProfileModal from '../../../support/fragments/linked-data/workProfileModal'; import EditResource from '../../../support/fragments/linked-data/editResource'; import { MARIGOLD_ROLES } from '../../../support/constants'; @@ -54,7 +54,7 @@ describe('Manage profile settings', () => { beforeEach(() => { cy.login(user.username, user.password, { path: TopMenu.linkedDataEditor, - waiter: LinkedDataEditor.waitLoading, + waiter: Marigold.waitLoading, authRefresh: true, }); }); @@ -79,16 +79,16 @@ describe('Manage profile settings', () => { }); it( - 'settings are applied to linked data editor', + 'settings are applied to Marigold editor', { tags: ['citation'] }, () => { // Modify settings - LinkedDataEditor.openManageProfileSettings(); + Marigold.openManageProfileSettings(); ManageProfileSettings.waitMainLoading(); ManageProfileSettings.waitProfilesLoading(); ManageProfileSettings.selectProfile('Books'); ManageProfileSettings.waitEditorLoading(); - ManageProfileSettings.dragSelectedToUnusedContainer('Profile:Work:LanguageCode'); + ManageProfileSettings.moveComponentToOppositeListButton('Profile:Work:LanguageCode'); ManageProfileSettings.verifyUnusedComponent('Profile:Work:LanguageCode'); ManageProfileSettings.nudgeComponentDownButton('Profile:Work:CreatorOfWork'); ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:CreatorOfWork', 2); @@ -100,7 +100,10 @@ describe('Manage profile settings', () => { ManageProfileSettings.modalUnusedSave(); // Check that settings apply, and also toggle profile default - LinkedDataEditor.openNewResourceForm(); + Marigold.waitLoading(); + // TODO - bug, the menu bar is missing after closing profile settings editor; remove when merged + cy.xpath('//button[@data-testid="resources-actions-dropdown"]').focus(); + Marigold.openNewResourceForm(); WorkProfileModal.waitLoading(); WorkProfileModal.checkOptionSelected('Books'); WorkProfileModal.toggleDefaultProfile(); @@ -110,8 +113,8 @@ describe('Manage profile settings', () => { EditResource.checkSectionInPosition('Profile::0__Work::0___creatorReference::0', 4); EditResource.clickCloseResourceButton(); - LinkedDataEditor.waitLoading(); - LinkedDataEditor.openManageProfileSettings(); + Marigold.waitLoading(); + Marigold.openManageProfileSettings(); ManageProfileSettings.waitMainLoading(); ManageProfileSettings.waitProfilesLoading(); ManageProfileSettings.selectProfile('Books'); @@ -119,22 +122,21 @@ describe('Manage profile settings', () => { ManageProfileSettings.verifyPreferredProfile(true); ManageProfileSettings.verifyCustomSettingsSelected(); ManageProfileSettings.verifyUnusedComponent('Profile:Work:LanguageCode'); - ManageProfileSettings.dragUnusedToSelected('Profile:Work:LanguageCode', 'Profile:Work:DateOfWork'); - ManageProfileSettings.verifySelectedComponentPosition('Profile:Work:LanguageCode', 7); + ManageProfileSettings.selectDefaultSettings(); ManageProfileSettings.moveComponentToOppositeListButton('Profile:Work:IntendedAudience'); ManageProfileSettings.verifyUnusedComponent('Profile:Work:IntendedAudience'); - ManageProfileSettings.moveComponentToOppositeListButton('Profile:Work:ContentType'); - ManageProfileSettings.verifyUnusedComponent('Profile:Work:ContentType'); ManageProfileSettings.saveAndClose(); ManageProfileSettings.modalUnusedSave(); // Check that modifications applied - LinkedDataEditor.openNewResourceForm(); + Marigold.waitLoading(); + // TODO - bug, the menu bar is missing after closing profile settings editor; remove when merged + cy.xpath('//button[@data-testid="resources-actions-dropdown"]').focus(); + Marigold.openNewResourceForm(); // No modal this time, default was chosen EditResource.waitLoading(); - EditResource.checkSectionInPosition('Profile::0__Work::0___languages::0', 7); + EditResource.checkSectionInPosition('Profile::0__Work::0___languages::0', 17); EditResource.checkSectionIsNotVisible('Profile::0__Work::0__targetAudience::0'); - EditResource.checkSectionIsNotVisible('Profile::0__Work::0__content::0'); }, ); @@ -142,7 +144,7 @@ describe('Manage profile settings', () => { 'changes to settings persist', { tags: ['citation'] }, () => { - LinkedDataEditor.openManageProfileSettings(); + Marigold.openManageProfileSettings(); ManageProfileSettings.waitMainLoading(); ManageProfileSettings.waitProfilesLoading(); @@ -187,7 +189,7 @@ describe('Manage profile settings', () => { 'required components cannot be moved to unused list', { tags: ['citation'] }, () => { - LinkedDataEditor.openManageProfileSettings(); + Marigold.openManageProfileSettings(); ManageProfileSettings.waitMainLoading(); ManageProfileSettings.waitProfilesLoading(); @@ -210,7 +212,7 @@ describe('Manage profile settings', () => { 'keyboard reordering and moving between lists', { tags: ['citation'] }, () => { - LinkedDataEditor.openManageProfileSettings(); + Marigold.openManageProfileSettings(); ManageProfileSettings.waitMainLoading(); ManageProfileSettings.waitProfilesLoading(); @@ -250,7 +252,7 @@ describe('Manage profile settings', () => { 'mouse reordering and dragging between lists', { tags: ['citation'] }, () => { - LinkedDataEditor.openManageProfileSettings(); + Marigold.openManageProfileSettings(); ManageProfileSettings.waitMainLoading(); ManageProfileSettings.waitProfilesLoading(); @@ -295,7 +297,7 @@ describe('Manage profile settings', () => { 'move component to other list by button press', { tags: ['citation'] }, () => { - LinkedDataEditor.openManageProfileSettings(); + Marigold.openManageProfileSettings(); ManageProfileSettings.waitMainLoading(); ManageProfileSettings.waitProfilesLoading(); @@ -331,7 +333,7 @@ describe('Manage profile settings', () => { 'nudge buttons in selected component list', { tags: ['citation'] }, () => { - LinkedDataEditor.openManageProfileSettings(); + Marigold.openManageProfileSettings(); ManageProfileSettings.waitMainLoading(); ManageProfileSettings.waitProfilesLoading(); @@ -351,7 +353,7 @@ describe('Manage profile settings', () => { 'verifies preferred profile setting persists across profile selection changes', { tags: ['citation'] }, () => { - LinkedDataEditor.openManageProfileSettings(); + Marigold.openManageProfileSettings(); ManageProfileSettings.waitMainLoading(); ManageProfileSettings.waitProfilesLoading(); diff --git a/cypress/support/fragments/linked-data/manageProfileSettings.js b/cypress/support/fragments/linked-data/manageProfileSettings.js index b1035acb5f..83f157a1dd 100644 --- a/cypress/support/fragments/linked-data/manageProfileSettings.js +++ b/cypress/support/fragments/linked-data/manageProfileSettings.js @@ -212,11 +212,13 @@ export default { moveComponentToOppositeListButton: (id) => { cy.xpath(component(id, componentActivateMenu)) + .scrollIntoView() .should('be.visible') .focus() .wait(100) .realClick(); cy.xpath(component(id, componentMoveAction)) + .scrollIntoView() .should('be.visible') .focus() .wait(100)