diff --git a/cypress-tests/cypress/e2e/admin/fileManager/searchAndFilterFiles.disabled.cy.js b/cypress-tests/cypress/e2e/admin/fileManager/searchAndFilterFiles.disabled.cy.js new file mode 100644 index 00000000000..d3fe717cb87 --- /dev/null +++ b/cypress-tests/cypress/e2e/admin/fileManager/searchAndFilterFiles.disabled.cy.js @@ -0,0 +1,294 @@ +import uniqid from "uniqid"; + +/** + * Sometimes file details drawer gets hidden due the race condition in "showFileDetails" and "hideFileDetails" dispatch-actions. + * That's why we make sure the drawer in visible. + */ +const openFileDetails = index => { + cy.waitUntil( + () => + cy + .findByTestId("fm-list-wrapper") + .within(() => { + cy.findAllByTestId("fm-list-wrapper-file") + .eq(index) + .within(() => { + cy.findByTestId("fm-file-wrapper-file-info-icon").click({ + force: true + }); + }); + }) + .then(() => + cy.findByTestId("fm.file-details.drawer").then($el => { + const [aside] = $el; + return aside.classList.contains("mdc-drawer--open"); + }) + ), + { + description: `Wait until "File Details" model is visible` + } + ); +}; + +const deleteFile = () => { + openFileDetails(0); + // Delete file + cy.findByTestId("fm-delete-file-button").click(); + cy.findByTestId("fm-delete-file-confirmation-dialog").within(() => { + cy.findByText("Confirm").click(); + }); + cy.findByText("File deleted successfully."); + + cy.waitUntil( + () => + cy.findByTestId("fm.file-details.drawer").then($el => { + const [aside] = $el; + return !aside.classList.contains("mdc-drawer--open"); + }), + { + description: "wait until file details is hidden" + } + ); +}; + +const updateFileName = newName => { + // Edit the name and save it + cy.findByTestId("fm.file-details.drawer").within(() => { + cy.findByPlaceholderText("Enter name").clear().type(newName).blur(); + }); + cy.findByText("Name successfully updated.").should("exist"); + // Exit file details view + cy.get("body").click(); + + cy.waitUntil( + () => + cy.findByTestId("fm.file-details.drawer").then($el => { + const [aside] = $el; + return !aside.classList.contains("mdc-drawer--open"); + }), + { + description: "wait until file details is hidden" + } + ); +}; + +const addTagsToFile = (tags, map) => { + // Edit tag and save it + cy.findByTestId("fm.file-details.drawer").within(() => { + // Save name value for later + cy.findByPlaceholderText(/Enter name/i).then($input => { + tags.forEach(tag => { + map[tag] = $input.attr("value"); + }); + }); + + cy.findByText(/Add tags.../i).click(); + // Add tags + tags.forEach(tag => { + cy.findByPlaceholderText(/homepage asset/i) + .clear() + .type(tag); + cy.wait(1000); + cy.findByText(tag).click(); + }); + // Save changes + cy.findByTestId("fm.tags.submit").click(); + }); + // Verify success message + cy.findByText("Tags successfully updated.").should("exist"); + // Exit file details view + cy.get("body").click(); + // Check tags in list + cy.findByTestId("fm.left-drawer.tag-list").within(() => { + tags.forEach(tag => { + cy.findByText(tag).should("exist"); + }); + }); + cy.waitUntil( + () => + cy.findByTestId("fm.file-details.drawer").then($el => { + const [aside] = $el; + return !aside.classList.contains("mdc-drawer--open"); + }), + { + description: "wait until file details is hidden" + } + ); +}; + +context("File Manager - Update file details", () => { + const files = [ + { fileName: "sample.jpeg", type: "image/jpeg" }, + { fileName: "sample_2.jpeg", type: "image/jpeg" } + ]; + + beforeEach(() => { + cy.login(); + cy.visit("/"); + // Open drawer + cy.findByTestId("apps-menu").click(); + // Open "File Manage" view + cy.findByTestId("admin-drawer-footer-menu-file-manager").click(); + + // Check if there are existing file and delete them + cy.fmListFiles({}).then(files => { + for (let i = 0; i < files.length; i++) { + deleteFile(); + } + }); + // Add files + files.forEach(({ fileName, type }) => { + cy.findByTestId("fm-list-wrapper").dropFile(fileName, type); + }); + cy.findByText("File upload complete.").should("exist"); + // All files should be there + cy.findByTestId("fm-list-wrapper").within(() => { + cy.findAllByTestId("fm-list-wrapper-file").should("have.length", files.length); + }); + }); + + // TODO - fix this test + it.skip("should update file's name and search by name", () => { + // Edit files name one by one + const newFileName1 = uniqid("File "); + const newFileName2 = uniqid("File "); + + // Select a file and open its details + openFileDetails(0); + + updateFileName(newFileName1); + + // Check file name is there in the list + cy.findByTestId("fm-list-wrapper").within(() => { + cy.findAllByTestId("fm-list-wrapper-file") + .first() + .within(() => { + cy.findByText(newFileName1).should("exist"); + }); + }); + + // Select a file and open its details + openFileDetails(1); + updateFileName(newFileName2); + // Check file name is there in the list + cy.findByTestId("fm-list-wrapper").within(() => { + cy.findAllByTestId("fm-list-wrapper-file") + .first() + .next() + .within(() => { + cy.findByText(newFileName2).should("exist"); + }); + }); + /** + * The search input is not responding to the first couple of clicks(interactions) while running the Cypress test. + * So, at the moment we forcefully "awake" the sleeping input element before continuing with the search. + */ + + cy.get(`[data-testid="file-manager.search-input"]`).as("search-input"); + cy.get("@search-input").dblclick(); + cy.get("@search-input").dblclick(); + cy.get("@search-input").should("be.focused"); + + // Search files by name + cy.get("@search-input").type(newFileName1); + cy.get(".react-spinner-material").should("not.exist"); + // File should be in list + cy.findByTestId("fm-list-wrapper").within(() => { + cy.findByText(newFileName1).should("exist"); + }); + cy.get("@search-input").clear().type(newFileName2); + cy.get(".react-spinner-material").should("not.exist"); + // File should be in list + cy.findByTestId("fm-list-wrapper").within(() => { + cy.findByText(newFileName2).should("exist"); + }); + + // Clear search + cy.get("@search-input").clear(); + }); + + // TODO - fix this test + it.skip("should add tags, search and by tags", () => { + // Add tags one by one + const tagNew = "new"; + const tagOld = "old"; + const tagCommon = "common"; + + const map = {}; + + openFileDetails(0); + // Add tags to first file + addTagsToFile([tagNew, tagCommon], map); + /** + * Make sure tags are indexed in elastic search before continue. + */ + cy.waitUntil(() => cy.fmListTags().then(tags => tags.length === 2), { + description: `Wait until tags are indexed`, + timeout: 2000, + interval: 2000 + }); + + openFileDetails(1); + // Add tags to second file + addTagsToFile([tagOld, tagCommon], map); + /** + * Make sure tags are indexed in elastic search before continue. + */ + cy.waitUntil(() => cy.fmListTags().then(tags => tags.length === 3), { + description: `Wait until tags are indexed`, + timeout: 2000, + interval: 2000 + }); + + /** + * The search input is not responding to the first couple of clicks(interactions) while running the Cypress test. + * So, at the moment we forcefully "awake" the sleeping input element before continuing with the search. + */ + cy.findByPlaceholderText("Search by filename or tags").as("search-input"); + cy.get("@search-input").dblclick(); + cy.get("@search-input").dblclick(); + cy.get("@search-input").should("be.focused"); + + // Search files by tags + cy.get("@search-input").type(tagNew); + cy.get(".react-spinner-material").should("not.exist"); + // File should be in list + cy.findByTestId("fm-list-wrapper").within(() => { + cy.findByText(map[tagNew]).should("exist"); + }); + + cy.get("@search-input").clear().type(tagOld); + cy.get(".react-spinner-material").should("not.exist"); + // File should be in list + cy.findByTestId("fm-list-wrapper").within(() => { + cy.findByText(map[tagOld]).should("exist"); + }); + + // Search file for common tag + cy.get("@search-input").clear().type(tagCommon); + cy.get(".react-spinner-material").should("not.exist"); + // Both files should be in list + cy.findByTestId("fm-list-wrapper").within(() => { + cy.findByText(map[tagOld]).should("exist"); + cy.findByText(map[tagNew]).should("exist"); + }); + + // Clear search + cy.get("@search-input").clear(); + cy.get(".react-spinner-material").should("not.exist"); + + // Filter files by selecting a tag + cy.findByTestId("fm.left-drawer.tag-list").within(() => { + cy.findByText(tagNew).click(); + }); + cy.get(".react-spinner-material").should("not.exist"); + // File should be in list + cy.findByTestId("fm-list-wrapper").within(() => { + cy.findByText(map[tagNew]).should("exist"); + }); + // Clear filter + cy.findByTestId("fm.left-drawer.tag-list").within(() => { + cy.findByText(tagNew).click(); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/admin/formBuilder/forms/createForm.cy.js b/cypress-tests/cypress/e2e/admin/formBuilder/forms/createForm.cy.js new file mode 100644 index 00000000000..0474d546bf6 --- /dev/null +++ b/cypress-tests/cypress/e2e/admin/formBuilder/forms/createForm.cy.js @@ -0,0 +1,99 @@ +import uniqid from "uniqid"; + +context("Forms Creation", () => { + beforeEach(() => cy.login()); + + describe("Create Form", () => { + const newFormTitle = `Test form 1 ${uniqid()}`; + const newFormTitle2 = `Test form 2 ${uniqid()}`; + + it("should be able to create form, rename it, publish it, create new revision and delete it", () => { + cy.visit("/form-builder/forms"); + + // Creating new form. + // After creating new form we should be redirected to the form editing page. + cy.findAllByTestId("new-record-button").first().click(); + cy.findByTestId("fb-new-form-modal").within(() => { + cy.findByPlaceholderText("Enter a name for your new form").type(newFormTitle); + cy.findByTestId("fb.form.create").click(); + }); + + // Check if we got redirected on form editor page. + cy.findByTestId("add-step-action", { timeout: 15000 }); + + // Renaming Form. + cy.findByTestId("fb-editor-form-title").click({ force: true }); + cy.get(`input[value="${newFormTitle}"]`).clear().type(newFormTitle2).blur(); + + // Publishing form after we changed name of it. + cy.findByTestId("fb.editor.default-bar.publish").click({ force: true }); + // Confirming publishing operation in the confirmation dialog. + cy.findByTestId("fb.editor.default-bar.publish-dialog").within(() => { + cy.findByTestId("confirmationdialog-confirm-action").click(); + }); + // Should see this text if publishing operation was successfull. + cy.findByText("Your form was published successfully!"); + + // Check if we have renamed form in the list of forms. + cy.findByTestId("default-data-list").within(() => { + cy.findAllByTestId("default-data-list-element") + .first() + .within(() => { + cy.findByText(newFormTitle2); + }); + }); + + // Should open form edit page for the form with title "newFormTitle2". + cy.findByTestId("default-data-list").within(() => { + cy.findAllByTestId("default-data-list-element") + .first() + .within(() => { + cy.findByText(newFormTitle2).should("be.visible"); + cy.findByTestId("edit-form-action").click({ force: true }); + }); + }); + + // Check if we got redirected on form editor page. + cy.findByTestId("add-step-action", { timeout: 15000 }).click({ force: true }); + + // Confirm that we have added a new step. + cy.findAllByTestId("form-step-element").should("have.length", "2"); + + // Publishing form after we added a new step. + cy.findByTestId("fb.editor.default-bar.publish").click({ force: true }); + // Confirming publishing operation in the confirmation dialog. + cy.findByTestId("fb.editor.default-bar.publish-dialog").within(() => { + cy.findByTestId("confirmationdialog-confirm-action").click(); + }); + // Should see this text if publishing operation was successfull. + cy.findByText("Your form was published successfully!"); + + // Check that revision version is 2. + cy.findByTestId("default-data-list").within(() => { + cy.findAllByTestId("default-data-list-element") + .first() + .within(() => { + cy.findByText(newFormTitle2).should("be.visible"); + cy.findByTestId("fb.form.status").within(() => { + cy.findByText("Published (v2)"); + }); + }); + }); + + // Deleting form. + cy.findByTestId("default-data-list").within(() => { + cy.findAllByTestId("default-data-list-element") + .first() + .within(() => { + cy.findByTestId("delete-form-action").click({ force: true }); + }); + }); + + cy.findAllByTestId("form-deletion-confirmation-dialog", { timeout: 15000 }) + .first() + .within(() => { + cy.findByTestId("confirmationdialog-confirm-action").click({ force: true }); + }); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/admin/formBuilder/forms/publishAndUnpublishForm.cy.js b/cypress-tests/cypress/e2e/admin/formBuilder/forms/publishAndUnpublishForm.cy.js index 56f1ddd3c31..5b303527a71 100644 --- a/cypress-tests/cypress/e2e/admin/formBuilder/forms/publishAndUnpublishForm.cy.js +++ b/cypress-tests/cypress/e2e/admin/formBuilder/forms/publishAndUnpublishForm.cy.js @@ -4,7 +4,6 @@ context("Forms Creation", () => { beforeEach(() => cy.login()); it("should be able to create, publish, unpublish, re-publish, and immediately delete everything", () => { - cy.fbDeleteAllForms(); const newFormTitle = `Test form ${uniqid()}`; // 1. Create form cy.visit("/form-builder/forms"); @@ -138,8 +137,9 @@ context("Forms Creation", () => { cy.findByText(/\(v3\)/i).should("exist"); }); }); - - // Delete form. + // Finally, delete the form and it's all revisions + //Delete form + cy.findByTestId("fb.form-details.tab.form-preview").click(); cy.findByTestId("fb.form-preview.header.delete").click(); cy.wait(500); cy.findByTestId("fb.form-preview.header.delete-dialog").within(() => { @@ -147,5 +147,6 @@ context("Forms Creation", () => { cy.findByTestId("confirmationdialog-confirm-action").click(); }); cy.findByText(/Form was deleted successfully/i).should("exist"); + cy.wait(500); }); }); diff --git a/cypress-tests/cypress/e2e/admin/headlessCms/contentEntry/contentEntries.disabled.cy.js b/cypress-tests/cypress/e2e/admin/headlessCms/contentEntry/contentEntries.disabled.cy.js new file mode 100644 index 00000000000..13ea988ad00 --- /dev/null +++ b/cypress-tests/cypress/e2e/admin/headlessCms/contentEntry/contentEntries.disabled.cy.js @@ -0,0 +1,284 @@ +import uniqid from "uniqid"; +import kebabCase from "lodash/kebabCase"; +import upperFirst from "lodash/upperFirst"; +import camelCase from "lodash/camelCase"; +import { CONTENT_MODEL_DATA } from "../mocks"; + +describe("Headless CMS - Content Entries", () => { + context("CRUD", () => { + const newModel = uniqid("Book-"); + const singularApiName = upperFirst(camelCase(uniqid("Book"))); + const pluralApiName = upperFirst(camelCase(uniqid("Books"))); + + let model; + let group; + // Runs once before all tests in the block + before(() => { + return cy + .cmsCreateContentModelGroup({ + data: { name: uniqid("Group-"), icon: "fas/star" } + }) + .then(data => { + group = data; + return cy + .cmsCreateContentModel({ + data: { + name: newModel, + modelId: kebabCase(newModel.toLowerCase()), + singularApiName, + pluralApiName, + group: group.id, + description: "Testing 123" + } + }) + .then(data => { + model = data; + return cy.cmsUpdateContentModel({ + modelId: data.modelId, + data: CONTENT_MODEL_DATA + }); + }); + }); + }); + + beforeEach(() => cy.login()); + + after(() => { + cy.waitUntil( + () => { + return cy + .cmsDeleteContentModel({ modelId: model.modelId }) + .then(data => data === true); + }, + { + description: `Wait until "ContentModel" is deleted` + } + ); + + cy.waitUntil( + () => cy.cmsDeleteContentModelGroup({ id: group.id }).then(data => data === true), + { + description: `Wait until "ContentModelGroup" is deleted` + } + ); + }); + + it("should create, edit, publish, unpublish, and delete content entry", () => { + cy.visit("/cms/content-models"); + + cy.findByTestId("default-data-list").within(() => { + cy.get("li") + .first() + .within(() => { + cy.findByText(newModel).should("exist"); + cy.findByTestId("cms-view-content-model-button").click({ force: true }); + }); + }); + + // Create an entry + const newEntryTitle = `Atomic Habits`; + const newEntryEdition = "4"; + const newEntryTitle2 = newEntryTitle + " - 2nd"; + + // a) Click on "New Entry" button + cy.findByTestId("new-entry-button").should("exist"); + cy.findByTestId("new-entry-button").click(); + //cy.findAllByTestId("new-entry-button").first().click({ force: true }); + + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + + // b) Fill entry details + cy.findByTestId("cms-content-form").within(() => { + cy.findByTestId("fr.input.text.Title").clear(); + cy.findByTestId("fr.input.text.Title").type(newEntryTitle); + cy.findByTestId("fr.input.number.Edition").clear(); + cy.findByTestId("fr.input.number.Edition").type(newEntryEdition); + }); + // c) Save entry + cy.findByTestId("cms-content-save-content-button").click(); + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + // d) Verify success message + cy.findByText(`${newModel} entry created successfully!`).should("exist"); + /** + * As ACO was introduced, there is a new step - navigate to root folder + */ + cy.acoNavigateToRootFolder(); + cy.wait(500); + + // Check the new entry in list + cy.findByTestId("default-data-list").within(() => { + cy.get("tbody") + .first() + .within(() => { + cy.get("tr").within(() => { + cy.findByText(newEntryTitle).should("exist"); + cy.findByText(/Draft \(v1\)/i).should("exist"); + }); + }); + }); + + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + // We should navigate to the new entry + cy.get("div.cms-data-list-record-title") + .first() + .within(() => { + cy.findByText(newEntryTitle).should("exist"); + }) + .click({ force: true }); + cy.get(".mdc-text-field__input").should("exist"); + + // Publish entry + cy.findByTestId("cms-content-save-publish-content-button").click(); + cy.findByTestId("cms-confirm-save-and-publish").within(() => { + cy.findByRole("button", { name: "Confirm" }).click(); + }); + cy.get(".react-spinner-material").should("not.exist"); + cy.findByText(/Successfully published revision/i).should("exist"); + + /** + * As ACO was introduced, there is a new step - navigate to root folder + */ + cy.acoNavigateToRootFolder(); + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + cy.wait(500); + + // Check publish status + cy.findByTestId("default-data-list").within(() => { + cy.get("tbody") + .first() + .within(() => { + cy.get("tr").within(() => { + cy.findByText(newEntryTitle).should("exist"); + cy.findByText(/Published \(v1\)/i).should("exist"); + }); + }); + }); + // Navigate to published entry + cy.get("div.cms-data-list-record-title") + .first() + .within(() => { + cy.findByText(newEntryTitle).should("exist"); + }) + .click({ force: true }); + cy.get(".mdc-text-field__input").should("exist").wait(100); + + // Edit an entry + cy.findByTestId("fr.input.text.Title").clear(); + cy.wait(500); + cy.findByTestId("fr.input.text.Title").wait(200).type(newEntryTitle2); + cy.findByTestId("fr.input.text.Title").should("have.value", newEntryTitle2); + cy.findByTestId("cms-content-save-content-button").click({ force: true }); + + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + /** + * As ACO was introduced, there is a new step - navigate to root folder + */ + cy.acoNavigateToRootFolder(); + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + + cy.findByTestId("default-data-list").should("exist"); + + // Check the update entry in list + cy.findByTestId("default-data-list").within(() => { + cy.get("tbody") + .first() + .within(() => { + cy.get("tr").within(() => { + cy.findByText(newEntryTitle2).should("exist"); + cy.findByText(/Draft \(v2\)/i).should("exist"); + }); + }); + }); + + // Navigate to updated entry + cy.get("div.cms-data-list-record-title") + .first() + .within(() => { + cy.findByText(newEntryTitle2).should("exist"); + }) + .click({ force: true }); + cy.get(".mdc-text-field__input").should("exist"); + + // Publish entry + cy.findByTestId("cms-content-save-publish-content-button").click(); + + cy.findByTestId("cms-confirm-save-and-publish").within(() => { + cy.findByRole("button", { name: "Confirm" }).click(); + }); + cy.findByText(/Successfully published revision/i).should("exist"); + + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + + cy.acoNavigateToRootFolder(); + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + + // Check publish status + cy.findByTestId("default-data-list").within(() => { + cy.get("tbody") + .first() + .within(() => { + cy.get("tr").within(() => { + cy.findByText(newEntryTitle2).should("exist"); + cy.findByText(/Published \(v2\)/i).should("exist"); + }); + }); + }); + + // Navigate to published updated entry + cy.get("div.cms-data-list-record-title") + .first() + .within(() => { + cy.findByText(newEntryTitle2).should("exist"); + }) + .click({ force: true }); + cy.get(".mdc-text-field__input").should("exist"); + + // Check revisions tab + cy.findByTestId("cms.content-form.tabs.revisions").click(); + // check revisions + cy.findByTestId("cms.content-form.revisions").within(() => { + cy.get("li") + .first() + .within(() => { + cy.findByTestId("cms.revision.status.published").should("exist"); + }); + cy.get("li") + .next() + .within(() => { + cy.findByTestId("cms.revision.status.locked").should("exist"); + }); + }); + + // unpublish latest revision + cy.findByTestId("cms.content-form.revisions").within(() => { + cy.get("li") + .first() + .within(() => { + cy.findByTestId("cms.revision.status.published").should("exist"); + cy.findByTestId("cms.content-form.revisions.more-options").click(); + }); + }); + cy.findByTestId("cms.revision.unpublish").click(); + cy.findByText(/Successfully unpublished revision/i).should("exist"); + + cy.findByTestId("cms.content-form.tabs.content").click(); + + // Delete the entry + cy.findByTestId("cms.content-form.header.more-options").click(); + cy.findByTestId("cms.content-form.header.delete").click(); + cy.findByTestId("cms.content-form.header.delete-dialog").within(() => { + cy.findByText(/Delete content entry/i); + cy.findByRole("button", { name: "Confirm" }).click(); + }); + cy.findByText(/deleted successfully!/i).should("exist"); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/admin/headlessCms/contentEntry/searchSortAndFilterEntries.disabled.cy.js b/cypress-tests/cypress/e2e/admin/headlessCms/contentEntry/searchSortAndFilterEntries.disabled.cy.js new file mode 100644 index 00000000000..eb142a5abbd --- /dev/null +++ b/cypress-tests/cypress/e2e/admin/headlessCms/contentEntry/searchSortAndFilterEntries.disabled.cy.js @@ -0,0 +1,224 @@ +import uniqid from "uniqid"; +import kebabCase from "lodash/kebabCase"; +import upperFirst from "lodash/upperFirst"; +import camelCase from "lodash/camelCase"; +import { CONTENT_MODEL_DATA } from "../mocks"; + +const STATUS = { + draft: "draft", + published: "published", + all: "all" +}; + +const createContentEntry = ({ model, entries }) => { + // Create an entry + const newEntryTitle = uniqid(`Atomic Habits `); + const newEntryEdition = Math.round(Math.random() * 10); + // Save for later + entries.push({ title: newEntryTitle }); + cy.wait(500); + cy.get(".react-spinner-material").should("not.exist"); + + // a) Click on "New Entry" button + cy.findAllByTestId("new-entry-button").first().click({ force: true }); + // b) Fill entry details + cy.wait(500); + cy.findByTestId("fr.input.text.Title").type(newEntryTitle); + cy.findByTestId("fr.input.number.Edition").type(newEntryEdition.toString()); + // c) Save entry + cy.findByTestId("cms-content-save-content-button").click({ force: true }); + // d) Verify success message + cy.findByText(`${model.name} entry created successfully!`).should("exist"); + cy.get(".react-spinner-material").should("not.exist"); + /** + * As ACO was introduced, there is a new step - navigate to root folder + */ + cy.acoNavigateToRootFolder(); +}; + +const deleteContentEntry = () => { + // Select entry + cy.get("div.cms-data-list-record-title").first().click({ force: true }); + // Delete the entry + cy.findByTestId("cms.content-form.header.more-options").click(); + cy.findByTestId("cms.content-form.header.delete").click(); + cy.findByTestId("cms.content-form.header.delete-dialog").within(() => { + cy.findByText(/Delete content entry/i); + cy.findByText(/Confirm/i).click({ force: true }); + }); + // Verify + cy.findByText(/deleted successfully!/i).should("exist"); + cy.get(".react-spinner-material").should("not.exist"); + /** + * As ACO was introduced, there is a new step - navigate to root folder + */ + cy.acoNavigateToRootFolder(); +}; + +context("Search, Sort and Filter Content Entries", () => { + const newModel = uniqid("Book-"); + const singularApiName = upperFirst(camelCase(uniqid("Book"))); + const pluralApiName = upperFirst(camelCase(uniqid("Books"))); + const totalEntries = 3; + const entries = []; + let createdModel; + let createdGroup; + // Runs once before all tests in the block + before(() => { + cy.cmsCreateContentModelGroup({ + data: { name: uniqid("Group-"), icon: "fas/star" } + }).then(group => { + createdGroup = group; + cy.cmsCreateContentModel({ + data: { + ...CONTENT_MODEL_DATA, + name: newModel, + modelId: kebabCase(newModel.toLowerCase()), + singularApiName, + pluralApiName, + group: group.id, + description: "Testing 123" + } + }).then(model => { + createdModel = model; + cy.visit(`/cms/content-entries/${model.modelId}?folderId=root`); + cy.wait(2000); + // Create few entries + for (let i = 0; i < totalEntries; i++) { + createContentEntry({ model, entries }); + } + }); + }); + }); + + beforeEach(() => cy.login()); + + after(() => { + cy.login(); + cy.visit(`/cms/content-entries/${createdModel.modelId}?folderId=root`); + + cy.wait(2000); + // Delete all entries + for (let i = 0; i < totalEntries; i++) { + deleteContentEntry(); + } + + cy.waitUntil( + () => + cy + .cmsDeleteContentModel({ modelId: createdModel.modelId }) + .then(data => data === true), + { + description: `Wait until "ContentModel" is deleted` + } + ); + + cy.waitUntil( + () => + cy.cmsDeleteContentModelGroup({ id: createdGroup.id }).then(data => data === true), + { + description: `Wait until "ContentModelGroup" is deleted` + } + ); + }); + + it("should search entries", () => { + // Should show "no records found" when searching for non existing entry + cy.findByTestId("default-data-list.search").within(() => { + cy.get(".search__input").wait(200).clear(); + cy.get(".search__input").wait(200).type("NON_EXISTING_ENTRY"); + cy.wait(500); + }); + cy.get(".title__container").within(() => { + cy.findByText("No results found.").should("exist"); + }); + + // Should able to search for a specific entry + cy.findByTestId("default-data-list.search").within(() => { + cy.get(".search__input").wait(100).clear(); + cy.get(".search__input").wait(100).type(entries[0].title); + cy.wait(500); + }); + + cy.get(".cms-data-list-record-title").should("have.length", 1); + cy.get(".cms-data-list-record-title") + .first() + .within(() => { + cy.findByText(entries[0].title).should("exist"); + }); + + // Clear search + cy.findByTestId("default-data-list.search").within(() => { + cy.get(".search__input").wait(100).clear(); + }); + }); + + it("should sort entries", () => { + cy.visit(`/cms/content-entries/${createdModel.modelId}?folderId=root`); + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + cy.wait(500); + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + + // Last entry should be on the top + cy.findByTestId("default-data-list").within(() => { + cy.get(".cms-data-list-record-title") + .first() + .within(() => { + cy.findByText(entries[totalEntries - 1].title).should("exist"); + }); + }); + + // Sort by ASC + cy.get(".cms-aco-list-savedOn").within(() => { + cy.get("div").first().click(); + }); + cy.wait(500); + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + + // First entry should be on the top + cy.findByTestId("default-data-list").within(() => { + cy.get(".cms-data-list-record-title") + .first() + .within(() => { + cy.findByText(entries[0].title).should("exist"); + }); + }); + }); + + it("should filter entries by status", () => { + cy.visit(`/cms/content-entries/${createdModel.modelId}?folderId=root`); + + // open the filters bar + cy.findByTestId("cms.list-entries.toggle-filters").click(); + + cy.findByTestId("filters-container").within(() => { + cy.get("select").first().select(STATUS.draft); + cy.wait(500); + }); + + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + + // Should contain all entries + cy.get(".cms-data-list-record-title").should("have.length", entries.length); + + // Get all items with "published" status + + cy.findByTestId("filters-container").within(() => { + cy.get("select").first().select(STATUS.published); + cy.wait(500); + }); + + // Loading should not be visible + cy.get(".react-spinner-material").should("not.exist"); + + // Should contain no entries + cy.get(".cms-data-list-record-title").should("have.length", 0); + cy.get(".title__container").within(() => { + cy.findByText("No results found.").should("exist"); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/admin/headlessCms/contentModel/contentModels.cy.js b/cypress-tests/cypress/e2e/admin/headlessCms/contentModel/contentModels.cy.js index 131511b2307..9ca72ad7824 100644 --- a/cypress-tests/cypress/e2e/admin/headlessCms/contentModel/contentModels.cy.js +++ b/cypress-tests/cypress/e2e/admin/headlessCms/contentModel/contentModels.cy.js @@ -17,9 +17,7 @@ context("Headless CMS - Content Models CRUD", () => { // 2.3 Click save button cy.findByTestId("new-record-button").click(); cy.findByTestId("cms-new-content-model-modal").within(() => { - // Ensures we start typing once the content model group select is loaded. - // This is important because otherwise, typing into the first field would be partially lost. - cy.contains("Ungrouped").should("exist"); + cy.findByText("New Content Model").should("exist"); cy.findByTestId("cms.newcontentmodeldialog.name") .focus() @@ -181,9 +179,7 @@ context("Headless CMS - Content Models CRUD", () => { // 2.3 Click save button cy.findByTestId("new-record-button").click(); cy.findByTestId("cms-new-content-model-modal").within(() => { - // Ensures we start typing once the content model group select is loaded. - // This is important because otherwise, typing into the first field would be partially lost. - cy.contains("Ungrouped").should("exist"); + cy.findByText("New Content Model").should("exist"); cy.findByTestId("cms.newcontentmodeldialog.name") .focus() diff --git a/cypress-tests/cypress/e2e/admin/headlessCms/contentModelGroups.cy.js b/cypress-tests/cypress/e2e/admin/headlessCms/contentModelGroups.cy.js index b4fc05d3f13..a73b8be7a65 100644 --- a/cypress-tests/cypress/e2e/admin/headlessCms/contentModelGroups.cy.js +++ b/cypress-tests/cypress/e2e/admin/headlessCms/contentModelGroups.cy.js @@ -1,15 +1,51 @@ -import { generateId } from "../../../support/utils"; +/** + * These tests sometimes start failing because data is not deleted after test runs. + * Ideally, we want to clear all data BEFORE executing tests, just to be sure that there + * is no unexpected data. This is especially important because we test sorting, and it + * fails if there are unexpected items in the list. + */ +import uniqid from "uniqid"; + +const groupNames = ["Group", "Group", "A Group", "Z Group"]; context("Headless CMS - Content Model Groups", () => { - beforeEach(() => { - cy.login(); - cy.cmsDeleteAllContentModelGroups(); + beforeEach(() => cy.login()); + + after(() => { + return cy.cmsListContentModelGroup().then(result => { + const groups = result.filter(group => { + return groupNames.some(name => { + group.name.startsWith(name); + }); + }); + return cy.waitUntil( + () => { + return Promise.all( + groups.map(async group => { + return cy.cmsDeleteContentModelGroup({ + id: group.id + }); + }) + ).then(data => { + if (!Array.isArray(data)) { + return false; + } else if (data.length !== groups.length) { + return false; + } + return data.every(item => !!item); + }); + }, + { + description: `Wait until all groups are deleted.` + } + ); + }); }); it("should able to create, update, and immediately delete everything", () => { cy.visit("/cms/content-model-groups"); - const newGroup = `Group ${generateId()}`; - const newGroup2 = `Group-2 ${generateId()}`; + const newGroup = `Group ${uniqid()}`; + const newGroup2 = `Group-2 ${uniqid()}`; // Create a new group cy.findAllByTestId("new-record-button").first().click(); cy.findByTestId("cms.form.group.name").type(newGroup); @@ -23,11 +59,9 @@ context("Headless CMS - Content Model Groups", () => { // Check newly created group in list cy.findByTestId("default-data-list").within(() => { - cy.get("li") - .first() - .within(() => { - cy.findByText(newGroup).should("exist"); - }); + cy.get("li").within(() => { + cy.findByText(newGroup).should("exist"); + }); }); // Update groups' name @@ -38,18 +72,13 @@ context("Headless CMS - Content Model Groups", () => { // Loading should be completed cy.get(".react-spinner-material").should("not.exist"); cy.findByText("Content model group saved successfully!").should("exist"); - // Check if the updated group is present in the list cy.findByTestId("default-data-list").within(() => { - cy.get("li") - .first() - .within(() => { - cy.findByText(newGroup2).should("exist"); - // Initiate delete - cy.findByTestId("cms.contentModelGroup.list-item.delete").click({ - force: true - }); - }); + cy.get("li").within(() => { + cy.findByText(newGroup2).should("exist"); + // Initiate delete + cy.findByTestId("cms.contentModelGroup.list-item.delete").click({ force: true }); + }); }); // Delete the newly created group @@ -71,8 +100,8 @@ context("Headless CMS - Content Model Groups", () => { it("should able to create, search, sort, and immediately delete everything", () => { cy.visit("/cms/content-model-groups"); // Create few content model groups - const newGroup1 = `A Group ${generateId()}`; - const newGroup2 = `Z Group ${generateId()}`; + const newGroup1 = `A Group ${uniqid()}`; + const newGroup2 = `Z Group ${uniqid()}`; // Create a Group one cy.findAllByTestId("new-record-button").first().click(); @@ -182,37 +211,27 @@ context("Headless CMS - Content Model Groups", () => { // Finally, delete group2 cy.findByTestId("default-data-list").within(() => { - cy.get("li") - .first() - .within(() => { - cy.findByText(newGroup2).should("exist"); - // Initiate delete - cy.findByTestId("cms.contentModelGroup.list-item.delete").click({ - force: true - }); - }); + cy.get("li").within(() => { + cy.findByText(newGroup2).should("exist"); + // Initiate delete + cy.findByTestId("cms.contentModelGroup.list-item.delete").click({ force: true }); + }); }); - // Delete the newly created group cy.findByTestId("cms.contentModelGroup.list-item.delete-dialog").within(() => { cy.findByText(/Confirmation/i).should("exist"); cy.findByText(/confirm$/i).click({ force: true }); }); - // Confirm that group is deleted successfully cy.findByText(`Content model group "${newGroup2}" deleted.`); // Delete group1 cy.findByTestId("default-data-list").within(() => { - cy.get("li") - .first() - .within(() => { - cy.findByText(newGroup1).should("exist"); - // Initiate delete - cy.findByTestId("cms.contentModelGroup.list-item.delete").click({ - force: true - }); - }); + cy.get("li").within(() => { + cy.findByText(newGroup1).should("exist"); + // Initiate delete + cy.findByTestId("cms.contentModelGroup.list-item.delete").click({ force: true }); + }); }); // Delete the newly created group cy.findByTestId("cms.contentModelGroup.list-item.delete-dialog").within(() => { diff --git a/cypress-tests/cypress/e2e/admin/pageBuilder/menus/createMenu.cy.js b/cypress-tests/cypress/e2e/admin/pageBuilder/menus/createMenu.cy.js index a944981c28d..f7434e449fc 100644 --- a/cypress-tests/cypress/e2e/admin/pageBuilder/menus/createMenu.cy.js +++ b/cypress-tests/cypress/e2e/admin/pageBuilder/menus/createMenu.cy.js @@ -37,6 +37,7 @@ context("Menus Module", () => { cy.contains("Are you sure you want to continue?").should("exist"); cy.findAllByTestId("confirmationdialog-confirm-action").click(); + cy.findByText(/Menu ".*" deleted\./).should("exist"); cy.wait(500); @@ -78,6 +79,7 @@ context("Menus Module", () => { cy.contains("Are you sure you want to continue?").should("exist"); cy.findAllByTestId("confirmationdialog-confirm-action").click(); + cy.findByText(/Menu ".*" deleted\./).should("exist"); cy.wait(500); diff --git a/cypress-tests/cypress/e2e/admin/pageBuilder/menus/menuItems.cy.js b/cypress-tests/cypress/e2e/admin/pageBuilder/menus/menuItems.cy.js index 3c7aaa86852..5bb4ca08b9a 100644 --- a/cypress-tests/cypress/e2e/admin/pageBuilder/menus/menuItems.cy.js +++ b/cypress-tests/cypress/e2e/admin/pageBuilder/menus/menuItems.cy.js @@ -102,6 +102,7 @@ context("Menus Module", () => { cy.contains("Are you sure you want to continue?").should("exist"); cy.findAllByTestId("confirmationdialog-confirm-action").click(); + cy.findByText(/Menu ".*" deleted\./).should("exist"); cy.wait(500); diff --git a/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesCrud.cy.ts b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesCrud.cy.ts new file mode 100644 index 00000000000..eeaba4ebab9 --- /dev/null +++ b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesCrud.cy.ts @@ -0,0 +1,65 @@ +import { customAlphabet } from "nanoid"; + +context("Page Builder - Blocks", () => { + const nanoid = customAlphabet("abcdefghijklmnopqrstuvwxyz"); + + beforeEach(() => { + cy.login(); + cy.pbDeleteAllTemplates(); + }); + + it("Should be able to create a template and then edit and delete it", () => { + cy.visit("/page-builder/page-templates"); + //Creates a template using the UI. + cy.findAllByTestId("pb-templates-list-new-template-btn").eq(0).click(); + cy.findByRole("textbox", { name: "Title" }).type("testingfunctionality"); + cy.findByRole("textbox", { name: "Slug" }).type("testingfunctionality"); + cy.findByRole("textbox", { name: "Description" }).type("testingfunctionality"); + cy.findByRole("button", { name: "Create" }).click(); + cy.findByRole("button", { name: "Save Changes" }).should("exist").click(); + + cy.contains("testingfunctionality").should("exist"); + + //Edits the template name using the UI. + cy.findByTestId("default-data-list").within(() => { + cy.get("li") + .first() + .within(() => { + cy.findByTestId("pb-templates-list-edit-template-btn").click({ + force: true + }); + }); + }); + cy.wait(1500).findByTestId("pb-editor-page-title").click(); + cy.get(`input[value="testingfunctionality"]`).clear().type("testingfunctionality1").blur(); + cy.findByRole("button", { name: "Save Changes" }).should("exist").click(); + cy.contains("testingfunctionality1").should("exist"); + + //Deletes the template using the UI. + cy.findByTestId("default-data-list").within(() => { + cy.get("li") + .first() + .within(() => { + cy.findByTestId("pb-templates-list-delete-template-btn").click({ + force: true + }); + }); + }); + cy.findByTestId("confirmationdialog-confirm-action").click(); + + cy.visit("/page-builder/page-templates"); + cy.contains("testingfunctionality1").should("not.exist"); + }); + + it.skip("Testing graphQL commands", () => { + cy.visit("/page-builder/page-templates"); + //cy.pbCreatePageTemplate(pageTemplateData1); + //cy.pbCreatePageTemplate(pageTemplateData2); + //cy.pbListPageTemplates().then((responseData) => { + // responseData.forEach((template) => { + // cy.log("Page Template Title:", template.title); + // }); + // }); + //cy.pbDeleteAllTemplates(); + }); +}); diff --git a/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesExportImport.cy.ts b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesExportImport.cy.ts new file mode 100644 index 00000000000..77e28be711a --- /dev/null +++ b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesExportImport.cy.ts @@ -0,0 +1,80 @@ +import { customAlphabet } from "nanoid"; + +context("Page Builder - Blocks", () => { + const nanoid = customAlphabet("abcdefghijklmnopqrstuvwxyz"); + const titleString1 = nanoid(6); + const titleString2 = nanoid(6); + const titleString3 = nanoid(6); + const titleString4 = "!#$%&/()=?*"; + const pageTemplateData1 = { + title: titleString1, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData2 = { + title: titleString2, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData3 = { + title: titleString3, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData4 = { + title: titleString4, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + + beforeEach(() => { + cy.login(); + cy.pbDeleteAllTemplates(); + cy.pbCreatePageTemplate(pageTemplateData1); + cy.pbCreatePageTemplate(pageTemplateData2); + cy.pbCreatePageTemplate(pageTemplateData3); + cy.pbCreatePageTemplate(pageTemplateData4); + }); + + it("Should be able to export templates and then import them again", () => { + cy.visit("/page-builder/page-templates"); + cy.findByTestId("export-template-button").click(); + cy.findByTestId("pb-templates-export-dialog-export-url") + .invoke("text") + .then(text => { + const url = text.trim(); + console.log(url); + cy.pbDeleteAllBlockCategories(); + cy.pbDeleteAllBlocks(); + + cy.visit("/page-builder/page-templates"); + cy.findByPlaceholderText("Search templates").should("exist"); + cy.findByTestId("pb-templates-list-options-btn").click(); + cy.findByRole("menuitem", { name: "Import Templates" }).click(); + cy.contains("Paste File URL").should("exist").click(); + cy.contains("File URL").type(url); + cy.contains("Continue").click(); + cy.findByText("All templates have been imported").should("exist"); + cy.contains("Continue").click(); + // Validation of imported blocks and categories. + + cy.findByPlaceholderText("Search templates").should("exist"); + cy.contains(pageTemplateData1.title).should("exist"); + cy.contains(pageTemplateData2.title).should("exist"); + cy.contains(pageTemplateData3.title).should("exist"); + cy.contains(pageTemplateData4.title).should("exist"); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesSearch.cy.ts b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesSearch.cy.ts new file mode 100644 index 00000000000..2a7d0276de0 --- /dev/null +++ b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesSearch.cy.ts @@ -0,0 +1,91 @@ +import { customAlphabet } from "nanoid"; + +context("Page Builder - Blocks", () => { + const nanoid = customAlphabet("abcdefghijklmnopqrstuvwxyz"); + const titleString1 = nanoid(6); + const titleString2 = nanoid(6); + const titleString3 = nanoid(6); + const titleString4 = "!#$%&/()=?*"; + const pageTemplateData1 = { + title: titleString1, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData2 = { + title: titleString2, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData3 = { + title: titleString3, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData4 = { + title: titleString4, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + + beforeEach(() => { + cy.login(); + cy.pbDeleteAllTemplates(); + cy.pbCreatePageTemplate(pageTemplateData1); + cy.pbCreatePageTemplate(pageTemplateData2); + cy.pbCreatePageTemplate(pageTemplateData3); + cy.pbCreatePageTemplate(pageTemplateData4); + }); + + it("Should be able to create templates and then search for them", () => { + cy.visit("/page-builder/page-templates"); + cy.findByPlaceholderText("Search templates").should("exist"); + cy.contains(pageTemplateData1.title).should("exist"); + cy.contains(pageTemplateData2.title).should("exist"); + cy.contains(pageTemplateData3.title).should("exist"); + cy.contains(pageTemplateData4.title).should("exist"); + + cy.findByPlaceholderText("Search templates").clear().type(titleString1); + cy.contains(pageTemplateData1.title).should("exist"); + cy.contains(pageTemplateData2.title).should("not.exist"); + cy.contains(pageTemplateData3.title).should("not.exist"); + cy.contains(pageTemplateData4.title).should("not.exist"); + + cy.findByPlaceholderText("Search templates").clear().type(titleString2); + cy.contains(pageTemplateData1.title).should("not.exist"); + cy.contains(pageTemplateData2.title).should("exist"); + cy.contains(pageTemplateData3.title).should("not.exist"); + cy.contains(pageTemplateData4.title).should("not.exist"); + + cy.findByPlaceholderText("Search templates").clear().type(titleString3); + cy.contains(pageTemplateData1.title).should("not.exist"); + cy.contains(pageTemplateData2.title).should("not.exist"); + cy.contains(pageTemplateData3.title).should("exist"); + cy.contains(pageTemplateData4.title).should("not.exist"); + + cy.findByPlaceholderText("Search templates").clear().type(titleString4); + cy.contains(pageTemplateData1.title).should("not.exist"); + cy.contains(pageTemplateData2.title).should("not.exist"); + cy.contains(pageTemplateData3.title).should("not.exist"); + cy.contains(pageTemplateData4.title).should("exist"); + + cy.findByPlaceholderText("Search templates") + .clear() + .type("This String should not return anything"); + cy.contains(pageTemplateData1.title).should("not.exist"); + cy.contains(pageTemplateData2.title).should("not.exist"); + cy.contains(pageTemplateData3.title).should("not.exist"); + cy.contains(pageTemplateData4.title).should("not.exist"); + }); +}); diff --git a/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesSearches.cy.ts b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesSearches.cy.ts new file mode 100644 index 00000000000..3ed918a058d --- /dev/null +++ b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesSearches.cy.ts @@ -0,0 +1,94 @@ +import { customAlphabet } from "nanoid"; + +context("Page Builder - Blocks", () => { + const nanoid = customAlphabet("abcdefghijklmnopqrstuvwxyz"); + const titleString1 = nanoid(6); + const titleString2 = nanoid(6); + const titleString3 = nanoid(6); + const titleString4 = "!#$%&/()=?*"; + const pageTemplateData1 = { + title: titleString1, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData2 = { + title: titleString2, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData3 = { + title: titleString3, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData4 = { + title: titleString4, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + + beforeEach(() => { + cy.login(); + cy.pbDeleteAllTemplates(); + cy.pbCreatePageTemplate(pageTemplateData1); + cy.pbCreatePageTemplate(pageTemplateData2); + cy.pbCreatePageTemplate(pageTemplateData3); + cy.pbCreatePageTemplate(pageTemplateData4); + }); + + it("Should be able to create templates and then search for them in the page templates modal", () => { + cy.visit("/page-builder/pages?folderId=root"); + cy.wait(1000); + cy.findByTestId("new-page-button").click(); + cy.wait(500); + cy.findByPlaceholderText("Search templates...").should("exist"); + cy.contains(pageTemplateData1.title).should("exist"); + cy.contains(pageTemplateData2.title).should("exist"); + cy.contains(pageTemplateData3.title).should("exist"); + cy.contains(pageTemplateData4.title).should("exist"); + + cy.findByPlaceholderText("Search templates...").clear().type(titleString1); + cy.contains(pageTemplateData1.title).should("exist"); + cy.contains(pageTemplateData2.title).should("not.exist"); + cy.contains(pageTemplateData3.title).should("not.exist"); + cy.contains(pageTemplateData4.title).should("not.exist"); + + cy.findByPlaceholderText("Search templates...").clear().type(titleString2); + cy.contains(pageTemplateData1.title).should("not.exist"); + cy.contains(pageTemplateData2.title).should("exist"); + cy.contains(pageTemplateData3.title).should("not.exist"); + cy.contains(pageTemplateData4.title).should("not.exist"); + + cy.findByPlaceholderText("Search templates...").clear().type(titleString3); + cy.contains(pageTemplateData1.title).should("not.exist"); + cy.contains(pageTemplateData2.title).should("not.exist"); + cy.contains(pageTemplateData3.title).should("exist"); + cy.contains(pageTemplateData4.title).should("not.exist"); + + cy.findByPlaceholderText("Search templates...").clear().type(titleString4); + cy.contains(pageTemplateData1.title).should("not.exist"); + cy.contains(pageTemplateData2.title).should("not.exist"); + cy.contains(pageTemplateData3.title).should("not.exist"); + cy.contains(pageTemplateData4.title).should("exist"); + + cy.findByPlaceholderText("Search templates...") + .clear() + .type("This String should not return anything"); + cy.contains(pageTemplateData1.title).should("not.exist"); + cy.contains(pageTemplateData2.title).should("not.exist"); + cy.contains(pageTemplateData3.title).should("not.exist"); + cy.contains(pageTemplateData4.title).should("not.exist"); + }); +}); diff --git a/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesSorting.cy.ts b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesSorting.cy.ts new file mode 100644 index 00000000000..8994030ac6b --- /dev/null +++ b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/pageTemplatesSorting.cy.ts @@ -0,0 +1,99 @@ +import { customAlphabet } from "nanoid"; + +context("Page Builder - Blocks", () => { + const nanoid = customAlphabet("abcdefghijklmnopqrstuvwxyz"); + const titleString1 = "ABC"; + const titleString2 = "DEF"; + const titleString3 = "GHI"; + const titleString4 = "!#$%&/()=?*"; + + const titleSlug = "TKL"; + + const pageTemplateData1 = { + title: titleString1, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData2 = { + title: titleString2, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData3 = { + title: titleString3, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData4 = { + title: titleString4, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + + beforeEach(() => { + cy.login(); + cy.pbDeleteAllTemplates(); + cy.wait(1000); + cy.pbCreatePageTemplate(pageTemplateData1); + cy.pbCreatePageTemplate(pageTemplateData2); + cy.pbCreatePageTemplate(pageTemplateData3); + cy.pbCreatePageTemplate(pageTemplateData4); + }); + + it("Should be able to create templates and then sort them correctly", () => { + cy.visit("/page-builder/page-templates"); + + cy.findByTestId("default-data-list.filter").click(); + cy.get(".webiny-ui-select select").select("Newest to oldest"); + cy.findByTestId("default-data-list").within(() => { + cy.get("li .mdc-list-item__text").first().each($span => { + cy.wrap($span) + .invoke("text") + .should("include", titleString4); + }); + }); + cy.visit("/page-builder/page-templates"); + cy.findByTestId("default-data-list.filter").click(); + cy.get(".webiny-ui-select select").select("Oldest to newest"); + cy.findByTestId("default-data-list").within(() => { + cy.get("li .mdc-list-item__text").first().each($span => { + cy.wrap($span) + .invoke("text") + .should("include", titleString1); + }); + }); + cy.visit("/page-builder/page-templates"); + cy.findByTestId("default-data-list.filter").click(); + cy.get(".webiny-ui-select select").select("Title A-Z"); + cy.findByTestId("default-data-list").first().within(() => { + cy.get("li .mdc-list-item__text").first().each($span => { + cy.wrap($span) + .invoke("text") + .should("include", titleString4); + }); + }); + + cy.visit("/page-builder/page-templates"); + cy.findByTestId("default-data-list.filter").click(); + cy.get(".webiny-ui-select select").select("Title Z-A"); + cy.findByTestId("default-data-list").first().within(() => { + cy.get("li .mdc-list-item__text").first().each($span => { + cy.wrap($span) + .invoke("text") + .should("include", titleString3); + }); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/admin/pageBuilder/templates/templatePreview.cy.ts b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/templatePreview.cy.ts new file mode 100644 index 00000000000..592c015652d --- /dev/null +++ b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/templatePreview.cy.ts @@ -0,0 +1,62 @@ +import { customAlphabet } from "nanoid"; + +context("Page Builder - Blocks", () => { + const nanoid = customAlphabet("abcdefghijklmnopqrstuvwxyz"); + const pageTemplateData1 = { + title: nanoid(6), + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData2 = { + title: nanoid(6), + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + const pageTemplateData3 = { + title: nanoid(6), + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + + beforeEach(() => { + cy.login(); + cy.pbDeleteAllTemplates(); + cy.pbCreatePageTemplate(pageTemplateData1); + cy.pbCreatePageTemplate(pageTemplateData2); + cy.pbCreatePageTemplate(pageTemplateData3); + }); + + it("Should be able to create a page and view all existing templates in it", () => { + cy.visit("/page-builder/pages?folderId=root"); + cy.wait(1000); + cy.findByTestId("new-page-button").click(); + cy.wait(500); + cy.contains("Pick a template for your new page").should("exist"); + cy.contains(pageTemplateData1.title).should("exist"); + cy.contains(pageTemplateData1.description).should("exist"); + cy.contains(pageTemplateData2.title).should("exist"); + cy.contains(pageTemplateData2.description).should("exist"); + cy.contains(pageTemplateData3.title).should("exist"); + cy.contains(pageTemplateData3.description).should("exist"); + + cy.get(".css-1rl9ll7-listStyle .css-5bicyh-listItem").each((item, index) => { + // Click on the current item + cy.wrap(item).click(); + + // Wait for the right panel to load (adjust this timeout if needed) + cy.get(".webiny-split-view__right-panel").should("be.visible", { timeout: 10000 }); + cy.contains( + (pageTemplateData1.title && pageTemplateData1.description) || (pageTemplateData2.title && pageTemplateData2.description) || (pageTemplateData3.title && pageTemplateData3.description) + ).should("exist"); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/admin/pageBuilder/templates/templatesVisible.cy.ts b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/templatesVisible.cy.ts new file mode 100644 index 00000000000..cac7abd2f7d --- /dev/null +++ b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/templatesVisible.cy.ts @@ -0,0 +1,29 @@ +import { customAlphabet } from "nanoid"; + +context("Page Builder - Blocks", () => { + const nanoid = customAlphabet("abcdefghijklmnopqrstuvwxyz"); + const titleString1 = nanoid(6); + const pageTemplateData1 = { + title: titleString1, + slug: nanoid(6), + description: nanoid(6), + tags: [], + layout: "static", + pageCategory: "static" + }; + + beforeEach(() => { + cy.login(); + cy.pbDeleteAllTemplates(); + cy.pbCreatePageTemplate(pageTemplateData1); + }); + + it("Should be able to create a page and view all existing templates in it", () => { + cy.visit("/page-builder/pages?folderId=root"); + cy.findByTestId("new-page-button").click(); + + cy.contains("Pick a template for your new page").should("exist"); + cy.contains(pageTemplateData1.title).should("exist"); + cy.contains(pageTemplateData1.description).should("exist"); + }); +}); diff --git a/cypress-tests/cypress/e2e/admin/pageBuilder/templates/useBlankPageTemplate.cy.ts b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/useBlankPageTemplate.cy.ts new file mode 100644 index 00000000000..b4777c75e13 --- /dev/null +++ b/cypress-tests/cypress/e2e/admin/pageBuilder/templates/useBlankPageTemplate.cy.ts @@ -0,0 +1,21 @@ +import { customAlphabet } from "nanoid"; + +context("Page Builder - Blocks", () => { + const nanoid = customAlphabet("abcdefghijklmnopqrstuvwxyz"); + + beforeEach(() => { + cy.login(); + cy.pbDeleteAllTemplates(); + }); + + it("Should be able to create a page and view all existing templates in it", () => { + cy.visit("/page-builder/pages?folderId=root"); + cy.findByTestId("new-page-button").click(); + + cy.contains("Pick a template for your new page").should("exist"); + + cy.get("button.webiny-ui-button--secondary").eq(1).click(); + cy.findByTestId("pb-content-add-block-button").should("exist"); + cy.contains("to start adding content").should("exist"); + }); +}); diff --git a/cypress-tests/cypress/support/commands.js b/cypress-tests/cypress/support/commands.js index 21fd232af65..66e335cea9f 100644 --- a/cypress-tests/cypress/support/commands.js +++ b/cypress-tests/cypress/support/commands.js @@ -5,6 +5,7 @@ import "./dropFile"; import "./reloadUntil"; import "./pageBuilder/pbListPages"; import "./pageBuilder/pbCreatePage"; +import "./pageBuilder/pbCreatePageTemplate"; import "./pageBuilder/pbCreateBlock"; import "./pageBuilder/pbCreateCategory"; import "./pageBuilder/pbCreateCategoryAndBlocks"; @@ -13,14 +14,16 @@ import "./pageBuilder/pbPublishPage"; import "./pageBuilder/pbDeletePage"; import "./pageBuilder/pbCreateMenu"; import "./pageBuilder/pbDeleteMenu"; +import "./pageBuilder/pbListPageTemplates"; import "./pageBuilder/pbListPageBlocks"; import "./pageBuilder/pbDeleteAllBlocks"; +import "./pageBuilder/pbDeleteAllTemplates"; import "./pageBuilder/pbDeleteAllBlockCategories"; import "./pageBuilder/pbListBlockCategories"; import "./pageBuilder/pbCreateCategory"; import "./pageBuilder/pbDeleteCategory"; +import "./pageBuilder/pbListPageTemplates"; import "./headlessCms/cmsCreateContentModel"; -import "./headlessCms/cmsDeleteAllContentModelGroups"; import "./headlessCms/cmsUpdateContentModel"; import "./headlessCms/cmsDeleteContentModel"; import "./headlessCms/cmsListContentModelGroup"; @@ -41,7 +44,6 @@ import "./fileManager/fmDeleteFile"; import "./fileManager/fmDeleteAllFiles"; import "./fileManager/fmListTags"; import "./formBuilder/fbDeleteForm"; -import "./formBuilder/fbDeleteAllForms"; import "cypress-mailosaur"; import "./aco/acoNavigateToFolder"; diff --git a/cypress-tests/cypress/support/pageBuilder/pbCreateCategoryAndBlocks.ts b/cypress-tests/cypress/support/pageBuilder/pbCreateCategoryAndBlocks.ts index 7f480de3eba..26f9c2057f0 100644 --- a/cypress-tests/cypress/support/pageBuilder/pbCreateCategoryAndBlocks.ts +++ b/cypress-tests/cypress/support/pageBuilder/pbCreateCategoryAndBlocks.ts @@ -1,4 +1,4 @@ -import { gqlClient } from "../utils"; +import { GraphQLClient } from "graphql-request"; interface CreateCategoryAndBlocksParams { blockCategory: Record; @@ -67,12 +67,14 @@ const CRATE_BLOCK_MUTATION = /* GraphQL */ ` Cypress.Commands.add("pbCreateCategoryAndBlocks", ({ blockCategory, blockNames }) => { cy.login().then(user => { - const createCategoryPromise = gqlClient - .request({ - query: CREATE_BLOCK_CATEGORY_MUTATION, - variables: { data: blockCategory }, - authToken: user.idToken.jwtToken - }) + const client = new GraphQLClient(Cypress.env("GRAPHQL_API_URL"), { + headers: { + authorization: `Bearer ${user.idToken.jwtToken}` + } + }); + + const createCategoryPromise = client + .request(CREATE_BLOCK_CATEGORY_MUTATION, { data: blockCategory }) .then(response => response.pageBuilder.blockCategory.data); return createCategoryPromise.then(categoryData => { @@ -81,21 +83,17 @@ Cypress.Commands.add("pbCreateCategoryAndBlocks", ({ blockCategory, blockNames } const createBlocksPromises: Array> = []; blockNames.forEach(blockName => { createBlocksPromises.push( - gqlClient.request({ - query: CRATE_BLOCK_MUTATION, - variables: { - data: { - name: blockName, - blockCategory: categorySlug, - content: { - id: "xyz", - type: "block", - data: {}, - elements: [] - } + client.request(CRATE_BLOCK_MUTATION, { + data: { + name: blockName, + blockCategory: categorySlug, + content: { + id: "xyz", + type: "block", + data: {}, + elements: [] } - }, - authToken: user.idToken.jwtToken + } }) ); }); diff --git a/cypress-tests/cypress/support/pageBuilder/pbCreatePageTemplate.ts b/cypress-tests/cypress/support/pageBuilder/pbCreatePageTemplate.ts index 903552f6478..7069847f960 100644 --- a/cypress-tests/cypress/support/pageBuilder/pbCreatePageTemplate.ts +++ b/cypress-tests/cypress/support/pageBuilder/pbCreatePageTemplate.ts @@ -4,7 +4,7 @@ declare global { // eslint-disable-next-line @typescript-eslint/no-namespace namespace Cypress { interface Chainable { - pbCreatePageTemplate(data: any): Promise; // Update the data type as needed + createPageTemplate(data: any): Promise; // Update the data type as needed } } } diff --git a/cypress-tests/cypress/support/pageBuilder/pbDeleteAllBlockCategories.ts b/cypress-tests/cypress/support/pageBuilder/pbDeleteAllBlockCategories.ts index 3199c385b1f..39f7c9b3ed4 100644 --- a/cypress-tests/cypress/support/pageBuilder/pbDeleteAllBlockCategories.ts +++ b/cypress-tests/cypress/support/pageBuilder/pbDeleteAllBlockCategories.ts @@ -1,4 +1,4 @@ -import { gqlClient } from "../utils"; +import { GraphQLClient } from "graphql-request"; const MUTATION = /* GraphQL */ ` mutation DeleteBlockCategory($slug: String!) { @@ -25,14 +25,20 @@ declare global { Cypress.Commands.add("pbDeleteAllBlockCategories", () => { cy.pbListBlockCategories().then(categories => { cy.login().then(user => { + const client = new GraphQLClient(Cypress.env("GRAPHQL_API_URL"), { + headers: { + authorization: `Bearer ${user.idToken.jwtToken}` + } + }); + return Promise.all( categories.map(category => { - return gqlClient - .request({ - query: MUTATION, - variables: { slug: category.slug }, - authToken: user.idToken.jwtToken - }) + const variables = { + slug: category.slug + }; + + return client + .request(MUTATION, variables) .then(response => response.pageBuilder.deleteBlockCategory); }) ); diff --git a/cypress-tests/cypress/support/pageBuilder/pbDeleteAllBlocks.ts b/cypress-tests/cypress/support/pageBuilder/pbDeleteAllBlocks.ts index 00df675a05e..14b27a4efa9 100644 --- a/cypress-tests/cypress/support/pageBuilder/pbDeleteAllBlocks.ts +++ b/cypress-tests/cypress/support/pageBuilder/pbDeleteAllBlocks.ts @@ -1,4 +1,4 @@ -import { gqlClient } from "../utils"; +import { GraphQLClient } from "graphql-request"; declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -26,6 +26,12 @@ Cypress.Commands.add("pbDeleteAllBlocks", () => { // Use pbListPageBlocks to get an array of page blocks cy.pbListPageBlocks().then(pageBlocks => { cy.login().then(user => { + const client = new GraphQLClient(Cypress.env("GRAPHQL_API_URL"), { + headers: { + authorization: `Bearer ${user.idToken.jwtToken}` + } + }); + // Use Promise.all to map and execute the deletePageBlock mutation for each page block return Promise.all( pageBlocks.map(pageBlock => { @@ -33,12 +39,8 @@ Cypress.Commands.add("pbDeleteAllBlocks", () => { id: pageBlock.id // Assuming the page block object has an 'id' property }; - return gqlClient - .request({ - query: MUTATION, - variables, - authToken: user.idToken.jwtToken - }) + return client + .request(MUTATION, variables) .then(response => response.pageBuilder.deletePageBlock); }) ); diff --git a/cypress-tests/cypress/support/pageBuilder/pbDeleteAllTemplates.ts b/cypress-tests/cypress/support/pageBuilder/pbDeleteAllTemplates.ts new file mode 100644 index 00000000000..a73f81b413b --- /dev/null +++ b/cypress-tests/cypress/support/pageBuilder/pbDeleteAllTemplates.ts @@ -0,0 +1,70 @@ +import { gqlClient } from "../utils"; + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Cypress { + interface Chainable { + // Combined method to list and delete all templates + pbDeleteAllTemplates(): void; + } + } +} + +const LIST_QUERY = /* GraphQL */ ` + query ListPageTemplates { + pageBuilder { + listPageTemplates { + data { + id + } + } + } + } +`; + +const DELETE_MUTATION = /* GraphQL */ ` + mutation DeletePageTemplate($id: ID!) { + pageBuilder { + deletePageTemplate(id: $id) { + error { + code + message + } + } + } + } +`; + +Cypress.Commands.add("pbDeleteAllTemplates", () => { + cy.login().then(user => { + // First, list all page templates + gqlClient + .request({ + query: LIST_QUERY, + authToken: user.idToken.jwtToken + }) + .then(listResponse => { + const templates = listResponse.pageBuilder.listPageTemplates.data; + + // Loop through the templates and delete each one + templates.forEach((template: { id: any; }) => { + gqlClient + .request({ + query: DELETE_MUTATION, + variables: { + id: template.id + }, + authToken: user.idToken.jwtToken + }) + .then(deleteResponse => { + const error = deleteResponse.pageBuilder.deletePageTemplate.error; + if (error) { + // Handle any errors that occur during deletion if needed + // You can use Cypress log or assertion to report the error + cy.log(`Error deleting template with ID: ${template.id}`); + } + }); + }); + }); + }); +}); diff --git a/cypress-tests/cypress/support/pageBuilder/pbListBlockCategories.ts b/cypress-tests/cypress/support/pageBuilder/pbListBlockCategories.ts index 7e2ff8bd767..0bbc43197e6 100644 --- a/cypress-tests/cypress/support/pageBuilder/pbListBlockCategories.ts +++ b/cypress-tests/cypress/support/pageBuilder/pbListBlockCategories.ts @@ -1,4 +1,4 @@ -import { gqlClient } from "../utils"; +import { GraphQLClient } from "graphql-request"; declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -24,11 +24,14 @@ const QUERY = /* GraphQL */ ` Cypress.Commands.add("pbListBlockCategories", () => { cy.login().then(user => { - return gqlClient - .request({ - query: QUERY, - authToken: user.idToken.jwtToken - }) - .then(response => response.pageBuilder.listBlockCategories.data); + const client = new GraphQLClient(Cypress.env("GRAPHQL_API_URL"), { + headers: { + authorization: `Bearer ${user.idToken.jwtToken}` + } + }); + + return client.request(QUERY).then(response => { + return response.pageBuilder.listBlockCategories.data; + }); }); }); diff --git a/cypress-tests/cypress/support/pageBuilder/pbListPageTemplates.ts b/cypress-tests/cypress/support/pageBuilder/pbListPageTemplates.ts index f13beb35cca..80b153715a0 100644 --- a/cypress-tests/cypress/support/pageBuilder/pbListPageTemplates.ts +++ b/cypress-tests/cypress/support/pageBuilder/pbListPageTemplates.ts @@ -4,7 +4,7 @@ declare global { // eslint-disable-next-line @typescript-eslint/no-namespace namespace Cypress { interface Chainable { - pbListPageTemplates(): Promise; // Update the return type as needed + listPageTemplates(): Promise; // Update the return type as needed } } }