From 9d3aca908a44afee08cc47062f97526e1f681958 Mon Sep 17 00:00:00 2001 From: geodem Date: Tue, 18 Nov 2025 06:06:50 +0800 Subject: [PATCH 01/13] modified seeder function to accept fixture path so it can be used to seed any content spec | added actions fixture --- cypress/fixtures/actions.json | 188 +++++++++++++++++++++++++++++++ cypress/index.d.ts | 5 +- cypress/plugins/seeds/content.ts | 9 +- 3 files changed, 195 insertions(+), 7 deletions(-) create mode 100644 cypress/fixtures/actions.json diff --git a/cypress/fixtures/actions.json b/cypress/fixtures/actions.json new file mode 100644 index 000000000..eb2fddb2b --- /dev/null +++ b/cypress/fixtures/actions.json @@ -0,0 +1,188 @@ +{ + "model": { + "label": "E2E: Content - Actions", + "metaTitle": "E2E: Content - Actions", + "type": "pageset", + "listed": true + }, + "fields": [ + { + "label": "Text", + "name": "text", + "datatype": "text", + "required": true, + "settings": { + "list": true, + "defaultValue": "Sample text", + "maxCharLimit": 30, + "minCharLimit": 10 + } + }, + { + "datatype": "textarea", + "name": "textarea", + "label": "textarea", + "required": true, + "sort": 2, + "settings": { + "list": true, + "defaultValue": "test_email@zesty.io", + "regexMatchErrorMessage": "Must be an email (e.g. hello@zesty.io)", + "regexMatchPattern": "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?" + } + }, + { + "datatype": "markdown", + "name": "markdown", + "label": "markdown", + "required": true, + "settings": { + "list": true, + "defaultValue": "markdown" + } + }, + { + "datatype": "wysiwyg_basic", + "name": "wysiwyg_basic", + "label": "wysiwyg basic", + "sort": 3, + "settings": { + "list": true, + "defaultValue": "What is Zesty.io?" + } + }, + { + "label": "Dropdown", + "name": "dropdown", + "datatype": "dropdown", + "settings": { + "list": true, + "options": { + "First Option": 1, + "Second Option": 2 + } + } + }, + { + "label": "Yes/No", + "name": "yes_no", + "datatype": "yes_no", + "settings": { + "list": true, + "options": { + "0": "No", + "1": "Yes" + } + } + }, + { + "label": "Images", + "name": "images", + "datatype": "images", + "settings": { + "list": true, + "limit": 5 + } + }, + { + "label": "One to One", + "name": "one_to_one", + "datatype": "one_to_one", + "relatedModelZUID": "6-a8bae2f4d7-rffln5", + "relatedFieldZUID": "12-d6e4c1d797-sjv628", + "settings": { + "list": true + } + }, + { + "label": "Fontawesome", + "name": "fontawesome", + "datatype": "fontawesome", + "settings": { + "list": true + } + } + ], + "items": [ + { + "web": { + "metaTitle": "E2E: Content - Actions", + "metaLinkText": "E2E: Content - Actions" + }, + "data": { + "text": "Mitchell Wilder", + "textarea": "test_email@zesty.io", + "markdown": "markdown", + "wysiwyg_basic": "Ullamco cupidatat pariatur sit ad non non. Lorem quis officia enim duis eiusmod aliqua aliqua tempor officia voluptate exercitation enim ad consequat. Ipsum velit reprehenderit consequat commodo mollit Lorem tempor laborum amet esse. Magna laborum id laborum labore nostrud. Culpa ullamco officia laborum reprehenderit laborum excepteur voluptate voluptate elit.\r\n", + "yes_no": 0, + "dropdown": 1, + "images": "3-6d7e8b3-4dv81", + "fontawesome": "" + } + }, + { + "web": { + "metaTitle": "Common Item 2", + "metaLinkText": "Common Item 2" + }, + "data": { + "text": "Annabelle Monroe", + "textarea": "673 Locust Street, Klagetoh, Alaska, 1340", + "markdown": "markdown", + "wysiwyg_basic": "Elit tempor laborum consequat nisi. Magna est deserunt duis ipsum anim pariatur fugiat officia ea laborum veniam reprehenderit. Irure dolor proident anim officia veniam nisi. Pariatur consequat nisi id est proident eu sint Lorem anim sit pariatur. Consequat et voluptate amet elit enim in velit laboris. Pariatur aliquip elit esse sunt magna in est esse adipisicing anim. Esse laboris ex cupidatat ut incididunt veniam officia officia elit.\r\n", + "yes_no": 0, + "dropdown": 0, + "images": "3-6e3cefc-neo39,3-7d9231d-i4iv9,3-b138798-i992x,3-6d7e8b3-4dv81", + "fontawesome": "" + } + }, + { + "web": { + "metaTitle": "Common Item 3", + "metaLinkText": "Common Item 3" + }, + "data": { + "text": "Vilma Cantu", + "textarea": "508 Williamsburg Street, Buxton, Utah, 4962", + "markdown": "markdown", + "wysiwyg_basic": "Culpa amet consectetur culpa ipsum duis laboris et consectetur laboris sit amet aute. Esse nulla sint velit quis qui incididunt laboris sit mollit elit sint pariatur. Quis voluptate nulla enim excepteur elit duis quis sit. Ut mollit ut minim esse anim voluptate irure qui elit laborum. Pariatur et sit duis irure consectetur adipisicing sit excepteur aliquip sunt magna. Nisi culpa veniam occaecat eiusmod incididunt veniam eiusmod est ad ea duis qui. Deserunt ea exercitation non pariatur nostrud voluptate consequat aliquip in quis mollit duis eu laboris.\r\n", + "yes_no": 0, + "dropdown": 1, + "images": "3-6e3cefc-neo39", + "fontawesome": "" + } + }, + { + "web": { + "metaTitle": "Common Item 4", + "metaLinkText": "Common Item 4" + }, + "data": { + "text": "Mercedes Noel", + "textarea": "715 Irving Street, Carlos, Maryland, 5696", + "markdown": "markdown", + "wysiwyg_basic": "Occaecat laborum excepteur nulla officia mollit reprehenderit. Officia culpa esse labore do mollit elit. Eiusmod magna do dolor labore ex occaecat fugiat duis labore qui.\r\n", + "yes_no": 0, + "dropdown": 0, + "images": "3-b138798-i992x", + "fontawesome": "" + } + }, + { + "web": { + "metaTitle": "Common Item 5", + "metaLinkText": "Common Item 5" + }, + "data": { + "text": "Chrystal Johns", + "textarea": "403 Euclid Avenue, Imperial, Iowa, 2366", + "markdown": "markdown", + "wysiwyg_basic": "Commodo minim labore ex laborum veniam deserunt consectetur nisi eiusmod consectetur ad do. Aute consectetur proident pariatur aute. Quis ullamco elit veniam labore ullamco dolor pariatur id. Nisi dolore laborum amet quis voluptate nisi.\r\n", + "yes_no": 0, + "dropdown": 1, + "images": "3-7d9231d-i4iv9", + "fontawesome": "" + } + } + ] +} diff --git a/cypress/index.d.ts b/cypress/index.d.ts index 547734a51..b5742414d 100644 --- a/cypress/index.d.ts +++ b/cypress/index.d.ts @@ -57,7 +57,10 @@ declare global { deleteStatusLabels(labels: string[]): Chainable; deleteModel(zuid: string): Chainable; deleteModels(models: string[]): Chainable; - task(event: "seed:content"): Chainable<{ + task( + event: "seed:content", + path: string + ): Chainable<{ model: Partial; fields: Partial; items: Partial; diff --git a/cypress/plugins/seeds/content.ts b/cypress/plugins/seeds/content.ts index 58f830f72..e66f286b4 100644 --- a/cypress/plugins/seeds/content.ts +++ b/cypress/plugins/seeds/content.ts @@ -17,11 +17,8 @@ module.exports = function content(config) { const { formatPathPart } = require("../../../src/utility/formatPathPart"); const { formatName } = require("../../../src/utility/formatName"); const { getSDK } = require("./utils"); - async function seedContent(): Promise { - const jsonString = readFileSync( - join(__dirname, "../../fixtures/content.json"), - "utf8" - ); + async function seedContent(path: string): Promise { + const jsonString = readFileSync(join(__dirname, "../../", path), "utf8"); const json = JSON.parse(jsonString); const sdk = await getSDK(config); @@ -95,6 +92,6 @@ module.exports = function content(config) { // CONTENT TASK MAPPING return { - "seed:content": seedContent, + "seed:content": (path: string) => seedContent(path), }; }; From bce5e18f64a3cab919f82c40ceb0729685d4a723 Mon Sep 17 00:00:00 2001 From: geodem Date: Tue, 18 Nov 2025 06:08:26 +0800 Subject: [PATCH 02/13] added fixture path to seeder task --- cypress/e2e/content/content.spec.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cypress/e2e/content/content.spec.js b/cypress/e2e/content/content.spec.js index 08bc0cbae..8ff4884dd 100644 --- a/cypress/e2e/content/content.spec.js +++ b/cypress/e2e/content/content.spec.js @@ -11,12 +11,14 @@ describe("Content Specs", () => { before(() => { // Seed content - cy.task("seed:content").then(({ model, items }) => { - //Set modelZUID as Cypress env variable for global test access - Cypress.env("modelZUID", model?.ZUID); - //Set itemZUID as Cypress env variable for global test access - Cypress.env("itemZUID", items[0]?.meta?.ZUID); - }); + cy.task("seed:content", "fixtures/content.json").then( + ({ model, items }) => { + //Set modelZUID as Cypress env variable for global test access + Cypress.env("modelZUID", model?.ZUID); + //Set itemZUID as Cypress env variable for global test access + Cypress.env("itemZUID", items[0]?.meta?.ZUID); + } + ); }); describe("editing content", () => { From 775cca5a843a355a3d74fecf03fd8e3c3709b858 Mon Sep 17 00:00:00 2001 From: geodem Date: Tue, 18 Nov 2025 06:11:11 +0800 Subject: [PATCH 03/13] updated: seed data | added all skiped tests | updated element selector tags --- cypress/e2e/content/actions.spec.js | 142 ++++++++++++++++++---------- 1 file changed, 90 insertions(+), 52 deletions(-) diff --git a/cypress/e2e/content/actions.spec.js b/cypress/e2e/content/actions.spec.js index 900c12407..4a58acf1d 100644 --- a/cypress/e2e/content/actions.spec.js +++ b/cypress/e2e/content/actions.spec.js @@ -6,9 +6,27 @@ const SUFFIX = "---TEST"; const TEST_DATA = { newItem: `new_item${SUFFIX}`, }; +const HOMEPAGE = { + modelZUID: "6-a1a600-k0b6f0", + itemZUID: "7-a1be38-1b42ht", +}; describe("Actions in content editor", () => { before(() => { + cy.task("seed:content", "fixtures/actions.json").then( + ({ model, fields, items }) => { + //Set modelZUID as Cypress env variable for global test access + Cypress.env("modelZUID", model?.ZUID); + //Set itemZUID as Cypress env variable for global test access + Cypress.env("itemZUID", items[0]?.meta?.ZUID); + + // Delete fontawesome field to test deactivated fields scenario + const fontAwesomeField = fields?.find( + (field) => field.datatype === "fontawesome" + ); + deleteFields(Cypress.env("modelZUID"), [fontAwesomeField?.ZUID]); + } + ); cleanTestData(); }); @@ -16,10 +34,15 @@ describe("Actions in content editor", () => { it("Must not save when missing required Field", () => { cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-556370-8sh47g/7-82a5c7ffb0-07vj1c"); + cy.visit( + `/content/${Cypress.env("modelZUID")}/${Cypress.env("itemZUID")}` + ); }); - cy.get("#12-13d590-9v2nr2 input", TIMEOUT).clear().should("have.value", ""); + cy.get(`[data-cy="field:markdown"] textarea`, TIMEOUT) + .clear() + .should("have.value", "") + .wait(500); cy.get("#SaveItemButton", TIMEOUT).trigger("click"); cy.get("[data-cy=toast]", TIMEOUT).contains( @@ -30,10 +53,15 @@ describe("Actions in content editor", () => { it("Must not save when exceeding or lacking characters", () => { cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-a4f5f1beaa-zc5l6v/7-ce9ca8cfb0-cc1mnz"); + cy.visit( + `/content/${Cypress.env("modelZUID")}/${Cypress.env("itemZUID")}` + ); }); - cy.get("#12-e6a5cfe3f6-k94nbg input", TIMEOUT).clear().type("aa"); + cy.get(`[data-cy="field:text"] input`, TIMEOUT) + .clear() + .type("aa") + .wait(500); cy.get("#SaveItemButton", TIMEOUT).trigger("click"); cy.getBySelector("FieldErrorsList").should("exist"); cy.getBySelector("FieldErrorsList") @@ -41,9 +69,10 @@ describe("Actions in content editor", () => { .find("li") .first() .contains("Requires 8 more characters."); - cy.get("#12-e6a5cfe3f6-k94nbg input") + cy.get(`[data-cy="field:text"] input`) .clear() - .type("Lorem ipsum dolor sit amet, consect"); + .type("Lorem ipsum dolor sit amet, consect") + .wait(500); cy.get("#SaveItemButton", TIMEOUT).trigger("click"); cy.getBySelector("FieldErrorsList").should("exist"); cy.getBySelector("FieldErrorsList") @@ -51,22 +80,25 @@ describe("Actions in content editor", () => { .find("li") .first() .contains("Exceeding by 5 characters."); - cy.get("#12-e6a5cfe3f6-k94nbg input", TIMEOUT) + cy.get(`[data-cy="field:text"] input`, TIMEOUT) .clear({ force: true }) - .wait(500) - .type("Lorem ipsum"); + .type("Lorem ipsum") + .wait(500); cy.get("#SaveItemButton", TIMEOUT).click(); cy.get("[data-cy=toast]", TIMEOUT).contains( - "Item Saved: New Schema All Fields" + "Item Saved: E2E: Content - Actions", + { matchCase: false } ); }); it("Must not save when regex is not matched", () => { cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-a4f5f1beaa-zc5l6v/7-ce9ca8cfb0-cc1mnz"); + cy.visit( + `/content/${Cypress.env("modelZUID")}/${Cypress.env("itemZUID")}` + ); }); - cy.get("#12-b6d09d92d0-7911ld textarea", TIMEOUT) + cy.get(`[data-cy="field:textarea"] textarea:eq(0)`, TIMEOUT) .first() .clear() .type("aa"); @@ -77,14 +109,15 @@ describe("Actions in content editor", () => { .find("li") .first() .contains("Must be an email (e.g. hello@zesty.io)"); - cy.get("#12-b6d09d92d0-7911ld textarea") + cy.get(`[data-cy="field:textarea"] textarea:eq(0)`) .first() .clear() - .wait(500) - .type("hello@zesty.io"); - cy.get("#SaveItemButton", TIMEOUT).click(); + .type("hello@zesty.io") + .wait(500); + cy.get("#SaveItemButton", TIMEOUT).trigger("click"); cy.get("[data-cy=toast]", TIMEOUT).contains( - "Item Saved: New Schema All Fields" + "Item Saved: E2E: Content - Actions", + { matchCase: false } ); }); @@ -93,32 +126,28 @@ describe("Actions in content editor", () => { * */ it("Save when missing required deactivated field", () => { cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-0c960c-d1n0kx/7-c882ba84ce-c4smnp"); + cy.visit( + `/content/${Cypress.env("modelZUID")}/${Cypress.env("itemZUID")}` + ); }); // Test deactivated field is not in DOM - cy.get("#12-f8efe4e0f5-xj7pj6 input").should("not.exist"); + cy.get(`[data-cy="field:fontawesome"] input`).should("not.exist"); // Make an edit to enable save button - cy.get("#12-849844-t8v5l6 input", TIMEOUT) + cy.get(`[data-cy="field:text"] input`, TIMEOUT) .clear() - .wait(500) - .type(TEST_DATA?.newItem); + .type(TEST_DATA?.newItem) + .wait(500); - // save to api - cy.waitOn( - "/v1/content/models/6-0c960c-d1n0kx/items/7-c882ba84ce-c4smnp", - () => { - cy.get("#SaveItemButton").click({ force: true, ...TIMEOUT }); - } - ); + cy.get("#SaveItemButton", TIMEOUT).trigger("click"); cy.get("[data-cy=toast]", TIMEOUT).contains("Item Saved"); }); it("Saves homepage item metadata", () => { cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-a1a600-k0b6f0/7-a1be38-1b42ht/meta"); + cy.visit(`/content/${HOMEPAGE.modelZUID}/${HOMEPAGE.itemZUID}/meta`); }); cy.get("textarea", TIMEOUT) @@ -128,7 +157,7 @@ describe("Actions in content editor", () => { .should("have.value", "This is an item meta description"); cy.waitOn( - "/v1/content/models/6-a1a600-k0b6f0/items/7-a1be38-1b42ht", + `/v1/content/models/${HOMEPAGE.modelZUID}/items/${HOMEPAGE.itemZUID}`, () => { cy.get("#SaveItemButton", TIMEOUT).trigger("click"); } @@ -150,9 +179,9 @@ describe("Actions in content editor", () => { it("Unpublishes an item", () => { cy.getBySelector("ContentPublishedIndicator").should("exist"); - cy.getBySelector("PublishMenuButton", TIMEOUT).click(); - cy.getBySelector("UnpublishContentButton", TIMEOUT).click(); - cy.getBySelector("ConfirmUnpublishButton").click(); + cy.getBySelector("PublishMenuButton", TIMEOUT).should("exist").click(); + cy.getBySelector("UnpublishContentButton", TIMEOUT).should("exist").click(); + cy.getBySelector("ConfirmUnpublishButton").should("exist").click(); cy.intercept("GET", "**/publishings").as("publish"); cy.wait("@publish"); @@ -162,25 +191,25 @@ describe("Actions in content editor", () => { it("Schedules a Publish for an item", () => { cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-a1a600-k0b6f0/7-a1be38-1b42ht/meta"); + cy.visit(`/content/${HOMEPAGE.modelZUID}/${HOMEPAGE.itemZUID}/meta`); }); cy.getBySelector("PublishMenuButton", TIMEOUT).click(); - cy.getBySelector("PublishScheduleButton").click(); - cy.getBySelector("SchedulePublishButton").click(); + cy.getBySelector("PublishScheduleButton").should("exist").click(); + cy.getBySelector("SchedulePublishButton").should("exist").click(); cy.getBySelector("ContentScheduledIndicator").should("exist"); }); it("Unschedules a Publish for an item", () => { - cy.getBySelector("PublishMenuButton", TIMEOUT).click(); - cy.getBySelector("PublishScheduleButton").click(); - cy.getBySelector("UnschedulePublishButton").click(); + cy.getBySelector("PublishMenuButton", TIMEOUT).should("exist").click(); + cy.getBySelector("PublishScheduleButton").should("exist").click(); + cy.getBySelector("UnschedulePublishButton").should("exist").click(); cy.getBySelector("ContentScheduledIndicator").should("not.exist"); }); it("Only allows future dates to be scheduled for publish", () => { cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-a1a600-k0b6f0/7-a1be38-1b42ht/meta"); + cy.visit(`/content/${HOMEPAGE.modelZUID}/${HOMEPAGE.itemZUID}/meta`); }); cy.getBySelector("PublishMenuButton", TIMEOUT).click(); @@ -200,7 +229,7 @@ describe("Actions in content editor", () => { it("Fills in default values for a new item", () => { cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-a1a600-k0b6f0/new"); + cy.visit(`/content/${HOMEPAGE.modelZUID}/new`); }); cy.get("#12-0c3934-8dz720 input", TIMEOUT).should( @@ -217,7 +246,7 @@ describe("Actions in content editor", () => { it("Fills in default values for a new item", () => { cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-a1a600-k0b6f0/new"); + cy.visit(`/content/${HOMEPAGE.modelZUID}/new`); }); cy.get("#12-0c3934-8dz720 input", TIMEOUT).should( @@ -233,7 +262,7 @@ describe("Actions in content editor", () => { it("Creates a new item", () => { cleanTestData(); cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-a1a600-k0b6f0/new"); + cy.visit(`/content/${HOMEPAGE.modelZUID}/new`); }); cy.get("input[name=title]", TIMEOUT) @@ -256,7 +285,7 @@ describe("Actions in content editor", () => { it("Displays a new item in the list", () => { cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-a1a600-k0b6f0"); + cy.visit(`/content/${HOMEPAGE.modelZUID}`); }); cy.contains(TEST_DATA?.newItem, { timeout: 50_000 }).should("exist"); @@ -269,7 +298,7 @@ describe("Actions in content editor", () => { cy.getBySelector("DeleteContentItemConfirmButton").click(); cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-a1a600-k0b6f0"); + cy.visit(`/content/${HOMEPAGE.modelZUID}`); }); cy.contains(TEST_DATA?.newItem).should("not.exist"); @@ -300,15 +329,15 @@ describe("Actions in content editor", () => { // // cy.contains("The item has been purged from the CDN cache", { timeout: 5000 }).should("exist"); // }); - it.only("Creates a new content item using AI-generated data", () => { + it("Creates a new content item using AI-generated data", () => { cy.waitOn("/v1/content/models*", () => { cy.waitOn("/v1/content/models/*/fields?showDeleted=true", () => { - cy.visit("/content/6-a1a600-k0b6f0/new"); + cy.visit(`/content/${HOMEPAGE.modelZUID}/new`); }); }); // Generate AI content for single line text - cy.get("#12-0c3934-8dz720", { timeout: 30_000 }) + cy.get(`[data-cy="field:title"]`, { timeout: 30_000 }) .find("[data-cy='AIOpen']") .click(); cy.getBySelector("AITopicField").type("biking"); @@ -318,7 +347,7 @@ describe("Actions in content editor", () => { cy.get("[data-cy='AIApprove']", { timeout: 50_000 }).click(); // Generate AI content for wysiwyg - cy.get("#12-717920-6z46t7", { timeout: 30_000 }) + cy.get(`[data-cy="field:content"]`, { timeout: 30_000 }) .find("[data-cy='AIOpen']") .click(); cy.getBySelector("AITopicField").type("biking"); @@ -358,16 +387,25 @@ describe("Actions in content editor", () => { function cleanTestData() { cy.apiRequest({ - url: `${API_ENDPOINTS.devInstance}/content/models/6-a1a600-k0b6f0/items?limit=5000&page=1&lang=en-US`, + url: `${API_ENDPOINTS.devInstance}/content/models/${HOMEPAGE.modelZUID}/items?limit=5000&page=1&lang=en-US`, }).then((response) => { const zuids = response?.data ?.filter((resData) => resData?.data?.title?.includes(SUFFIX)) ?.map((item) => item?.meta?.ZUID); cy.apiRequest({ - url: `${API_ENDPOINTS.devInstance}/content/models/6-a1a600-k0b6f0/items/batch`, + url: `${API_ENDPOINTS.devInstance}/content/models/${HOMEPAGE.modelZUID}/items/batch`, method: "DELETE", body: JSON.stringify(zuids), }); }); } + +function deleteFields(modelZUID, fieldZUIDs) { + fieldZUIDs.forEach((fieldZUID) => { + cy.apiRequest({ + url: `${API_ENDPOINTS.devInstance}/content/models/${modelZUID}/fields/${fieldZUID}`, + method: "DELETE", + }); + }); +} From 55327819f4c3b9e3755870cc6baf3c599c2e56e5 Mon Sep 17 00:00:00 2001 From: geodem Date: Fri, 21 Nov 2025 01:46:38 +0800 Subject: [PATCH 04/13] added logic in internal link component to check if item has meta data since the test fails if data is not yet fetch --- cypress/e2e/content/actions.spec.js | 14 ++++++++------ .../app/components/Editor/Field/InternalLink.tsx | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cypress/e2e/content/actions.spec.js b/cypress/e2e/content/actions.spec.js index 4a58acf1d..3738c5fbe 100644 --- a/cypress/e2e/content/actions.spec.js +++ b/cypress/e2e/content/actions.spec.js @@ -232,14 +232,14 @@ describe("Actions in content editor", () => { cy.visit(`/content/${HOMEPAGE.modelZUID}/new`); }); - cy.get("#12-0c3934-8dz720 input", TIMEOUT).should( + cy.get('[data-cy="field:title"] input', TIMEOUT).should( "have.value", "default single line text field" ); - cy.get("#12-d39a38-85sqdt", TIMEOUT).contains( + cy.get('[data-cy="field:image"]', TIMEOUT).contains( "zesty-io-logo-horizontal-dark.png" ); - cy.get("#12-bcd1dcc5f4-2rpm9p", TIMEOUT).contains( + cy.get('[data-cy="field:habibi"]', TIMEOUT).contains( "5 Tricks to Teach Your Pitbull: Fun & Easy Tips for You & Your Dog!" ); }); @@ -249,12 +249,14 @@ describe("Actions in content editor", () => { cy.visit(`/content/${HOMEPAGE.modelZUID}/new`); }); - cy.get("#12-0c3934-8dz720 input", TIMEOUT).should( + cy.get('[data-cy="field:title"] input', TIMEOUT).should( "have.value", "default single line text field" ); - cy.get("#12-d39a38-85sqdt").contains("zesty-io-logo-horizontal-dark.png"); - cy.get("#12-bcd1dcc5f4-2rpm9p").contains( + cy.get('[data-cy="field:image"]', TIMEOUT).contains( + "zesty-io-logo-horizontal-dark.png" + ); + cy.get('[data-cy="field:habibi"]', TIMEOUT).contains( "5 Tricks to Teach Your Pitbull: Fun & Easy Tips for You & Your Dog!" ); }); diff --git a/src/apps/content-editor/src/app/components/Editor/Field/InternalLink.tsx b/src/apps/content-editor/src/app/components/Editor/Field/InternalLink.tsx index c7df8af19..d87a6e6d0 100644 --- a/src/apps/content-editor/src/app/components/Editor/Field/InternalLink.tsx +++ b/src/apps/content-editor/src/app/components/Editor/Field/InternalLink.tsx @@ -30,7 +30,8 @@ export const InternalLink = ({ useEffect(() => { // Resolve the itemZUID in case it isn't in the store cache if ( - !internalLinkRelatedItem && + // make sure that internalLinkRelatedItem from store has meta data + !internalLinkRelatedItem?.meta?.ZUID && zuid.isValid(value) && zuid.matches(value, zuid.prefix["SITE_CONTENT_ITEM"]) ) { From b3ac70d02aa140a90072b61a1f38c18fe6acb90d Mon Sep 17 00:00:00 2001 From: geodem Date: Wed, 26 Nov 2025 23:54:59 +0800 Subject: [PATCH 05/13] fixed failing tests | removed unnecessary timeouts --- cypress.config.js | 9 + cypress/e2e/content/actions.spec.js | 354 ++++++++---------- cypress/fixtures/actions.json | 74 ++-- cypress/plugins/seeds/content.ts | 10 +- .../components/Editor/Field/InternalLink.tsx | 3 +- 5 files changed, 225 insertions(+), 225 deletions(-) diff --git a/cypress.config.js b/cypress.config.js index e370fd52f..2db7ed8cf 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -7,6 +7,7 @@ module.exports = defineConfig({ viewportHeight: 1080, video: false, defaultCommandTimeout: 15000, + experimentalInteractiveRunEvents: true, env: { API_AUTH: "https://auth.api.dev.zesty.io", COOKIE_NAME: "DEV_APP_SID", @@ -26,6 +27,14 @@ module.exports = defineConfig({ return launchOptions; }); + // Get spec info to be used for the model's label/name + on("before:spec", (spec) => { + const pathPart = spec.relative.split("/"); + config.env.SPEC = { + folder: pathPart[pathPart?.length - 2], + file: pathPart[pathPart?.length - 1].replace(/\.spec\.js/g, ""), + }; + }); return require("./cypress/plugins/index.js")(on, config); }, diff --git a/cypress/e2e/content/actions.spec.js b/cypress/e2e/content/actions.spec.js index 3738c5fbe..a90027d34 100644 --- a/cypress/e2e/content/actions.spec.js +++ b/cypress/e2e/content/actions.spec.js @@ -1,17 +1,14 @@ import { API_ENDPOINTS } from "../../support/api"; -const TIMEOUT = { timeout: 15_000 }; - -const SUFFIX = "---TEST"; +const timestamp = Date.now(); const TEST_DATA = { - newItem: `new_item${SUFFIX}`, -}; -const HOMEPAGE = { - modelZUID: "6-a1a600-k0b6f0", - itemZUID: "7-a1be38-1b42ht", + new: `New Item:${timestamp}`, + ai: `AI Generated:${timestamp}`, }; describe("Actions in content editor", () => { + let CONTENT_ITEMS = null; + let FIELDS = null; before(() => { cy.task("seed:content", "fixtures/actions.json").then( ({ model, fields, items }) => { @@ -19,50 +16,42 @@ describe("Actions in content editor", () => { Cypress.env("modelZUID", model?.ZUID); //Set itemZUID as Cypress env variable for global test access Cypress.env("itemZUID", items[0]?.meta?.ZUID); - + CONTENT_ITEMS = items; + FIELDS = fields; // Delete fontawesome field to test deactivated fields scenario - const fontAwesomeField = fields?.find( - (field) => field.datatype === "fontawesome" - ); - deleteFields(Cypress.env("modelZUID"), [fontAwesomeField?.ZUID]); + const fontAwesomeField = + Array.isArray(fields) && + fields?.find((field) => field.datatype === "fontawesome"); + cy.apiRequest({ + url: `${API_ENDPOINTS.devInstance}/content/models/${model?.ZUID}/fields/${fontAwesomeField?.ZUID}`, + method: "DELETE", + }); } ); - cleanTestData(); }); - const timestamp = Date.now(); - it("Must not save when missing required Field", () => { - cy.waitOn("/v1/content/models*", () => { + cy.waitOn("/v1/content/models**", () => { cy.visit( `/content/${Cypress.env("modelZUID")}/${Cypress.env("itemZUID")}` ); }); - cy.get(`[data-cy="field:markdown"] textarea`, TIMEOUT) + cy.get(`[data-cy="field:markdown"] textarea`) .clear() .should("have.value", "") .wait(500); - cy.get("#SaveItemButton", TIMEOUT).trigger("click"); + cy.get("#SaveItemButton").trigger("click"); - cy.get("[data-cy=toast]", TIMEOUT).contains( - "Missing Data in Required Fields", - TIMEOUT - ); + cy.get("[data-cy=toast]").contains("Missing Data in Required Fields", { + matchCase: false, + }); + cy.get(`[data-cy="field:markdown"] textarea`).clear().type("markdown"); }); it("Must not save when exceeding or lacking characters", () => { - cy.waitOn("/v1/content/models*", () => { - cy.visit( - `/content/${Cypress.env("modelZUID")}/${Cypress.env("itemZUID")}` - ); - }); - - cy.get(`[data-cy="field:text"] input`, TIMEOUT) - .clear() - .type("aa") - .wait(500); - cy.get("#SaveItemButton", TIMEOUT).trigger("click"); + cy.get(`[data-cy="field:text"] input`).clear().type("aa").wait(500); + cy.get("#SaveItemButton").trigger("click"); cy.getBySelector("FieldErrorsList").should("exist"); cy.getBySelector("FieldErrorsList") .find("ol") @@ -73,36 +62,30 @@ describe("Actions in content editor", () => { .clear() .type("Lorem ipsum dolor sit amet, consect") .wait(500); - cy.get("#SaveItemButton", TIMEOUT).trigger("click"); + cy.get("#SaveItemButton").trigger("click"); cy.getBySelector("FieldErrorsList").should("exist"); cy.getBySelector("FieldErrorsList") .find("ol") .find("li") .first() .contains("Exceeding by 5 characters."); - cy.get(`[data-cy="field:text"] input`, TIMEOUT) - .clear({ force: true }) + cy.get(`[data-cy="field:text"] input`) + .clear() .type("Lorem ipsum") .wait(500); - cy.get("#SaveItemButton", TIMEOUT).click(); - cy.get("[data-cy=toast]", TIMEOUT).contains( - "Item Saved: E2E: Content - Actions", - { matchCase: false } + cy.get("#SaveItemButton").click(); + cy.get("[data-cy=toast]").contains( + `Item Saved: ${CONTENT_ITEMS?.[0].web.metaTitle}`, + { + matchCase: false, + } ); + cy.get(`[data-cy="field:text"] input`).clear().type("Mitchell Wilder"); }); it("Must not save when regex is not matched", () => { - cy.waitOn("/v1/content/models*", () => { - cy.visit( - `/content/${Cypress.env("modelZUID")}/${Cypress.env("itemZUID")}` - ); - }); - - cy.get(`[data-cy="field:textarea"] textarea:eq(0)`, TIMEOUT) - .first() - .clear() - .type("aa"); - cy.get("#SaveItemButton", TIMEOUT).trigger("click"); + cy.get(`[data-cy="field:textarea"] textarea:eq(0)`).clear().type("aa"); + cy.get("#SaveItemButton").trigger("click"); cy.getBySelector("FieldErrorsList").should("exist"); cy.getBySelector("FieldErrorsList") .find("ol") @@ -114,108 +97,137 @@ describe("Actions in content editor", () => { .clear() .type("hello@zesty.io") .wait(500); - cy.get("#SaveItemButton", TIMEOUT).trigger("click"); - cy.get("[data-cy=toast]", TIMEOUT).contains( - "Item Saved: E2E: Content - Actions", - { matchCase: false } + cy.get("#SaveItemButton").trigger("click"); + cy.get("[data-cy=toast]").contains( + `Item Saved: ${CONTENT_ITEMS?.[0].web.metaTitle}`, + { + matchCase: false, + } ); + cy.get(`[data-cy="field:textarea"] textarea:eq(0)`) + .clear() + .type("test_email@zesty.io"); }); /** * NOTE: this depends upon `toggle` field on the schema being marked as being required and deactivated. Because it's deactivated it doesn't render in the content editor and the expectation is the content item should save. there fore there is nothing to do and confirm that this item saves successfully. Adding this notes because nothing really happens inside this test but it's important this test remains. * */ it("Save when missing required deactivated field", () => { - cy.waitOn("/v1/content/models*", () => { - cy.visit( - `/content/${Cypress.env("modelZUID")}/${Cypress.env("itemZUID")}` - ); - }); - // Test deactivated field is not in DOM cy.get(`[data-cy="field:fontawesome"] input`).should("not.exist"); // Make an edit to enable save button - cy.get(`[data-cy="field:text"] input`, TIMEOUT) + cy.get(`[data-cy="field:text"] input`) .clear() - .type(TEST_DATA?.newItem) + .type(TEST_DATA?.new) .wait(500); - cy.get("#SaveItemButton", TIMEOUT).trigger("click"); + cy.get("#SaveItemButton").trigger("click"); - cy.get("[data-cy=toast]", TIMEOUT).contains("Item Saved"); + cy.get("[data-cy=toast]").contains("Item Saved"); }); - it("Saves homepage item metadata", () => { - cy.waitOn("/v1/content/models*", () => { - cy.visit(`/content/${HOMEPAGE.modelZUID}/${HOMEPAGE.itemZUID}/meta`); + it("Saves page item metadata", () => { + cy.waitOn("/v1/content/models**", () => { + cy.visit( + `/content/${Cypress.env("modelZUID")}/${Cypress.env("itemZUID")}/meta` + ); }); - cy.get("textarea", TIMEOUT) + cy.get("textarea") .first() .wait(500) - .type("{selectall}{backspace}This is an item meta description", TIMEOUT) + .type("{selectall}{backspace}This is an item meta description") .should("have.value", "This is an item meta description"); + cy.get('[data-cy="itemRoute"] input').type("/"); + cy.get(".MuiAutocomplete-listbox li:eq(0)").click(); + cy.waitOn( - `/v1/content/models/${HOMEPAGE.modelZUID}/items/${HOMEPAGE.itemZUID}`, + `/v1/content/models/${Cypress.env("modelZUID")}/items/${Cypress.env( + "itemZUID" + )}`, () => { - cy.get("#SaveItemButton", TIMEOUT).trigger("click"); + cy.get("#SaveItemButton").trigger("click"); } ); - cy.get("[data-cy=toast]", TIMEOUT).contains("Item Saved"); + cy.get("[data-cy=toast]").contains("Item Saved"); }); it("Publishes an item", () => { - cy.getBySelector("PublishButton", TIMEOUT).click(); - cy.getBySelector("ConfirmPublishModal").should("exist"); - cy.getBySelector("ConfirmPublishButton").click(); + cy.waitOn("/v1/content/models**", () => { + cy.visit( + `/content/${Cypress.env("modelZUID")}/${CONTENT_ITEMS?.[4]?.meta?.ZUID}` + ); + }); + cy.intercept("**/publishings").as("publish"); + cy.getBySelector("PublishButton") + .should("exist") + .should("be.enabled") + .click(); + cy.getBySelector("ConfirmPublishModal").should("exist").wait(500); + cy.getBySelector("ConfirmPublishButton").should("exist").click(); - cy.intercept("GET", "**/publishings").as("publish"); cy.wait("@publish"); - cy.getBySelector("ContentPublishedIndicator").should("exist"); }); it("Unpublishes an item", () => { - cy.getBySelector("ContentPublishedIndicator").should("exist"); - cy.getBySelector("PublishMenuButton", TIMEOUT).should("exist").click(); - cy.getBySelector("UnpublishContentButton", TIMEOUT).should("exist").click(); + cy.intercept("**/publishings/**").as("publish"); + cy.getBySelector("PublishMenuButton") + .should("exist") + .should("be.enabled") + .click(); + cy.getBySelector("UnpublishContentButton").should("exist").click(); + cy.get(".MuiDialog-root").should("exist").wait(500); cy.getBySelector("ConfirmUnpublishButton").should("exist").click(); - cy.intercept("GET", "**/publishings").as("publish"); cy.wait("@publish"); - - cy.getBySelector("PublishButton", TIMEOUT).should("exist"); + cy.getBySelector("PublishButton").should("exist"); }); it("Schedules a Publish for an item", () => { - cy.waitOn("/v1/content/models*", () => { - cy.visit(`/content/${HOMEPAGE.modelZUID}/${HOMEPAGE.itemZUID}/meta`); + cy.waitOn("/v1/content/models**", () => { + cy.visit( + `/content/${Cypress.env("modelZUID")}/${CONTENT_ITEMS?.[4]?.meta?.ZUID}` + ); }); - - cy.getBySelector("PublishMenuButton", TIMEOUT).click(); + cy.intercept("**/publishings").as("publish"); + cy.getBySelector("PublishMenuButton") + .should("exist") + .should("be.enabled") + .click(); cy.getBySelector("PublishScheduleButton").should("exist").click(); + cy.getBySelector("SchedulePublishModal").should("exist").wait(500); cy.getBySelector("SchedulePublishButton").should("exist").click(); + cy.wait("@publish"); cy.getBySelector("ContentScheduledIndicator").should("exist"); }); it("Unschedules a Publish for an item", () => { - cy.getBySelector("PublishMenuButton", TIMEOUT).should("exist").click(); + cy.intercept("**/publishings**").as("publish"); + cy.getBySelector("PublishMenuButton") + .should("exist") + .should("be.enabled") + .click(); cy.getBySelector("PublishScheduleButton").should("exist").click(); + cy.getBySelector("SchedulePublishModal").should("exist").wait(500); cy.getBySelector("UnschedulePublishButton").should("exist").click(); + cy.wait("@publish"); cy.getBySelector("ContentScheduledIndicator").should("not.exist"); }); it("Only allows future dates to be scheduled for publish", () => { - cy.waitOn("/v1/content/models*", () => { - cy.visit(`/content/${HOMEPAGE.modelZUID}/${HOMEPAGE.itemZUID}/meta`); - }); - - cy.getBySelector("PublishMenuButton", TIMEOUT).click(); - cy.getBySelector("PublishScheduleButton").click(); + cy.getBySelector("PublishMenuButton") + .should("exist") + .should("be.enabled") + .click(); + cy.getBySelector("PublishScheduleButton").should("exist").click(); + cy.getBySelector("PublishScheduleModal").should("exist").wait(500); cy.getBySelector("PublishScheduleModal") .find("[data-cy='datePickerInputField']") + .should("exist") .click(); cy.get( @@ -224,100 +236,84 @@ describe("Actions in content editor", () => { cy.get( '.MuiPickersArrowSwitcher-root button[aria-label="Next month"]' ).should("not.be.disabled"); - cy.getBySelector("CancelSchedulePublishButton").click(); + cy.getBySelector("CancelSchedulePublishButton").should("exist").click(); }); it("Fills in default values for a new item", () => { - cy.waitOn("/v1/content/models*", () => { - cy.visit(`/content/${HOMEPAGE.modelZUID}/new`); + cy.waitOn("/v1/content/models**", () => { + cy.visit(`/content/${Cypress.env("modelZUID")}/new`); }); - cy.get('[data-cy="field:title"] input', TIMEOUT).should( + cy.get('[data-cy="field:text"] input').should( "have.value", - "default single line text field" - ); - cy.get('[data-cy="field:image"]', TIMEOUT).contains( - "zesty-io-logo-horizontal-dark.png" + FIELDS.find((field) => field.name === "text").settings.defaultValue ); - cy.get('[data-cy="field:habibi"]', TIMEOUT).contains( - "5 Tricks to Teach Your Pitbull: Fun & Easy Tips for You & Your Dog!" - ); - }); - - it("Fills in default values for a new item", () => { - cy.waitOn("/v1/content/models*", () => { - cy.visit(`/content/${HOMEPAGE.modelZUID}/new`); - }); - - cy.get('[data-cy="field:title"] input', TIMEOUT).should( + cy.get('[data-cy="field:textarea"] textarea:eq(0)').should( "have.value", - "default single line text field" - ); - cy.get('[data-cy="field:image"]', TIMEOUT).contains( - "zesty-io-logo-horizontal-dark.png" + FIELDS.find((field) => field.name === "textarea").settings.defaultValue ); - cy.get('[data-cy="field:habibi"]', TIMEOUT).contains( - "5 Tricks to Teach Your Pitbull: Fun & Easy Tips for You & Your Dog!" + cy.get('[data-cy="field:markdown"] textarea').should( + "have.value", + FIELDS.find((field) => field.name === "markdown").settings.defaultValue ); }); it("Creates a new item", () => { - cleanTestData(); - cy.waitOn("/v1/content/models*", () => { - cy.visit(`/content/${HOMEPAGE.modelZUID}/new`); + cy.waitOn("/v1/content/models**", () => { + cy.visit(`/content/${Cypress.env("modelZUID")}/new`); }); - cy.get("input[name=title]", TIMEOUT) - .wait(500) - .type(TEST_DATA?.newItem, TIMEOUT); + cy.get('[data-cy="field:text"] input').clear(); + cy.get('[data-cy="field:text"] input').should("have.value", ""); + cy.get('[data-cy="field:text"] input').type(TEST_DATA?.new); cy.getBySelector("ManualMetaFlow").click(); cy.getBySelector("metaDescription") .find("textarea") .first() .wait(500) - .type(TEST_DATA?.newItem); - cy.getBySelector("CreateItemSaveButton", TIMEOUT).click(); + .type(TEST_DATA?.new); + cy.getBySelector("CreateItemSaveButton").click(); - cy.contains("Created Item", TIMEOUT).should("exist"); + cy.get("[data-cy=toast]") + .contains(`Created Item: ${TEST_DATA?.new}`, { matchCase: false }) + .should("exist"); }); it("Saved item becomes publishable", () => { - cy.get("#PublishButton", TIMEOUT).should("exist"); + cy.get("#PublishButton").should("exist"); }); it("Displays a new item in the list", () => { - cy.waitOn("/v1/content/models*", () => { - cy.visit(`/content/${HOMEPAGE.modelZUID}`); + cy.waitOn("/v1/content/models**", () => { + cy.visit(`/content/${Cypress.env("modelZUID")}`); }); - cy.contains(TEST_DATA?.newItem, { timeout: 50_000 }).should("exist"); + cy.contains(TEST_DATA?.new).should("exist"); }); it("Deletes an item", () => { - cy.contains(TEST_DATA?.newItem, TIMEOUT).click(); - cy.getBySelector("ContentItemMoreButton", TIMEOUT).click(); + cy.contains(TEST_DATA?.new).should("exist").click(); + cy.getBySelector("ContentItemMoreButton").click(); cy.getBySelector("DeleteContentItem").click(); - cy.getBySelector("DeleteContentItemConfirmButton").click(); + cy.getBySelector("DeleteContentItemConfirmButton").should("exist").click(); - cy.waitOn("/v1/content/models*", () => { - cy.visit(`/content/${HOMEPAGE.modelZUID}`); + cy.waitOn("/v1/content/models**", () => { + cy.visit(`/content/${Cypress.env("modelZUID")}`); }); - cy.contains(TEST_DATA?.newItem).should("not.exist"); + cy.contains(TEST_DATA?.new).should("not.exist"); }); // TODO: Workflow request doesn't work it.skip("Makes a workflow request", () => { - cy.get("#MainNavigation", TIMEOUT).contains("Homepage").click(); - cy.get("#WorkflowRequestButton").click(); + cy.get("#MainNavigation").contains("Homepage").click(); + cy.get("#WorkflowRequestButton").should("exist").click(); cy.contains("Grant Test").click(); - cy.get("#WorkflowRequestSendButton").click(); + cy.get("#WorkflowRequestSendButton").should("exist").click(); // these waits are due to a delay // dealing with these specific endpoints // the local environment is slow - cy.contains("Successfully sent workflow request", { timeout: 5000 }).should( - "exist" - ); + cy.contains("Successfully sent workflow request").should("exist"); }); // it("Refreshes the CDN cache", () => { @@ -332,31 +328,38 @@ describe("Actions in content editor", () => { // }); it("Creates a new content item using AI-generated data", () => { - cy.waitOn("/v1/content/models*", () => { + cy.waitOn("/v1/content/models**", () => { cy.waitOn("/v1/content/models/*/fields?showDeleted=true", () => { - cy.visit(`/content/${HOMEPAGE.modelZUID}/new`); + cy.visit(`/content/${Cypress.env("modelZUID")}/new`); }); }); - // Generate AI content for single line text - cy.get(`[data-cy="field:title"]`, { timeout: 30_000 }) - .find("[data-cy='AIOpen']") - .click(); + // Increase timeout to account for longer AI generation times. + const aiDataGenerationTimeout = { timeout: 60_000 }; + + cy.get('[data-cy="field:text"] input').clear(); + cy.get('[data-cy="field:text"] input').should("have.value", ""); + cy.get('[data-cy="field:text"] input').type(TEST_DATA?.ai); + + // Generate AI content for markdown + cy.get(`[data-cy="field:markdown"]`).find("[data-cy='AIOpen']").click(); cy.getBySelector("AITopicField").type("biking"); cy.getBySelector("AIAudienceField").type("young adults"); cy.getBySelector("AIGenerate").click(); - cy.get("[data-cy='AIApprove']", { timeout: 50_000 }).click(); + cy.get("[data-cy='AIApprove']", aiDataGenerationTimeout).click(); - // Generate AI content for wysiwyg - cy.get(`[data-cy="field:content"]`, { timeout: 30_000 }) + // Generate AI content for wysiwyg_basic + cy.get(`[data-cy="field:wysiwyg_basic"]`) .find("[data-cy='AIOpen']") .click(); cy.getBySelector("AITopicField").type("biking"); cy.getBySelector("AIAudienceField").type("young adults"); - cy.get("[data-cy='AIGenerate']", { timeout: 30_000 }).click(); + cy.getBySelector("AIGenerate").click(); - cy.get("[data-cy='AIApprove']", { timeout: 50_000 }).click(); + cy.get("[data-cy='AIApprove']", aiDataGenerationTimeout) + .should("exist") + .click(); // Select AI-assisted metadata generation flow cy.getBySelector("ManualMetaFlow").click(); @@ -364,11 +367,11 @@ describe("Actions in content editor", () => { // Generate AI content for meta title cy.getBySelector("metaTitle").find("input").clear(); cy.getBySelector("metaTitle").find("[data-cy='AIOpen']").click(); - cy.get("[data-cy='AIGenerate']", { timeout: 30_000 }).click(); + cy.get("[data-cy='AIGenerate']").click(); - cy.get("[data-cy='AISuggestion1']", { timeout: 30_000 }).click(); + cy.get("[data-cy='AISuggestion1']", aiDataGenerationTimeout).click(); - cy.get("[data-cy='AIApprove']", { timeout: 50_000 }).click(); + cy.get("[data-cy='AIApprove']", aiDataGenerationTimeout).click(); // Generate AI content for meta description cy.getBySelector("metaDescription") @@ -377,37 +380,12 @@ describe("Actions in content editor", () => { cy.getBySelector("metaDescription").find("[data-cy='AIOpen']").click(); cy.getBySelector("AIGenerate").click(); - cy.get("[data-cy='AISuggestion1']", { timeout: 50_000 }).click(); + cy.get("[data-cy='AISuggestion1']", aiDataGenerationTimeout).click(); - cy.get("[data-cy='AIApprove']", { timeout: 50_000 }).click(); + cy.get("[data-cy='AIApprove']", aiDataGenerationTimeout).click(); cy.getBySelector("CreateItemSaveButton").click(); - cy.contains("Created Item", { timeout: 15000 }).should("exist"); + cy.contains("Created Item", aiDataGenerationTimeout).should("exist"); }); }); - -function cleanTestData() { - cy.apiRequest({ - url: `${API_ENDPOINTS.devInstance}/content/models/${HOMEPAGE.modelZUID}/items?limit=5000&page=1&lang=en-US`, - }).then((response) => { - const zuids = response?.data - ?.filter((resData) => resData?.data?.title?.includes(SUFFIX)) - ?.map((item) => item?.meta?.ZUID); - - cy.apiRequest({ - url: `${API_ENDPOINTS.devInstance}/content/models/${HOMEPAGE.modelZUID}/items/batch`, - method: "DELETE", - body: JSON.stringify(zuids), - }); - }); -} - -function deleteFields(modelZUID, fieldZUIDs) { - fieldZUIDs.forEach((fieldZUID) => { - cy.apiRequest({ - url: `${API_ENDPOINTS.devInstance}/content/models/${modelZUID}/fields/${fieldZUID}`, - method: "DELETE", - }); - }); -} diff --git a/cypress/fixtures/actions.json b/cypress/fixtures/actions.json index eb2fddb2b..2c85491db 100644 --- a/cypress/fixtures/actions.json +++ b/cypress/fixtures/actions.json @@ -1,7 +1,6 @@ { "model": { - "label": "E2E: Content - Actions", - "metaTitle": "E2E: Content - Actions", + "label": "Content - Actions", "type": "pageset", "listed": true }, @@ -11,6 +10,7 @@ "name": "text", "datatype": "text", "required": true, + "sort": 0, "settings": { "list": true, "defaultValue": "Sample text", @@ -23,7 +23,7 @@ "name": "textarea", "label": "textarea", "required": true, - "sort": 2, + "sort": 1, "settings": { "list": true, "defaultValue": "test_email@zesty.io", @@ -36,6 +36,7 @@ "name": "markdown", "label": "markdown", "required": true, + "sort": 2, "settings": { "list": true, "defaultValue": "markdown" @@ -55,11 +56,13 @@ "label": "Dropdown", "name": "dropdown", "datatype": "dropdown", + "sort": 4, "settings": { "list": true, + "defaultValue": "Option 1", "options": { - "First Option": 1, - "Second Option": 2 + "Option 1": "Option 1", + "Option 2": "Option 2" } } }, @@ -67,8 +70,10 @@ "label": "Yes/No", "name": "yes_no", "datatype": "yes_no", + "sort": 5, "settings": { "list": true, + "defaultValue": 1, "options": { "0": "No", "1": "Yes" @@ -79,8 +84,10 @@ "label": "Images", "name": "images", "datatype": "images", + "sort": 6, "settings": { "list": true, + "defaultValue": "3-7d9231d-i4iv9", "limit": 5 } }, @@ -88,16 +95,19 @@ "label": "One to One", "name": "one_to_one", "datatype": "one_to_one", + "sort": 7, "relatedModelZUID": "6-a8bae2f4d7-rffln5", "relatedFieldZUID": "12-d6e4c1d797-sjv628", "settings": { - "list": true + "list": true, + "defaultValue": "7-feb2e9c4e2-4hm7c2" } }, { "label": "Fontawesome", "name": "fontawesome", "datatype": "fontawesome", + "sort": 8, "settings": { "list": true } @@ -106,82 +116,82 @@ "items": [ { "web": { - "metaTitle": "E2E: Content - Actions", - "metaLinkText": "E2E: Content - Actions" + "metaTitle": "Action Item 1", + "metaLinkText": "Action Item 1" }, "data": { "text": "Mitchell Wilder", "textarea": "test_email@zesty.io", "markdown": "markdown", - "wysiwyg_basic": "Ullamco cupidatat pariatur sit ad non non. Lorem quis officia enim duis eiusmod aliqua aliqua tempor officia voluptate exercitation enim ad consequat. Ipsum velit reprehenderit consequat commodo mollit Lorem tempor laborum amet esse. Magna laborum id laborum labore nostrud. Culpa ullamco officia laborum reprehenderit laborum excepteur voluptate voluptate elit.\r\n", + "wysiwyg_basic": "

Ullamco cupidatat pariatur sit ad non non.

", "yes_no": 0, - "dropdown": 1, + "dropdown": "Option 1", "images": "3-6d7e8b3-4dv81", - "fontawesome": "" + "fontawesome": "fontawesome" } }, { "web": { - "metaTitle": "Common Item 2", - "metaLinkText": "Common Item 2" + "metaTitle": "Action Item 2", + "metaLinkText": "Action Item 2" }, "data": { "text": "Annabelle Monroe", "textarea": "673 Locust Street, Klagetoh, Alaska, 1340", "markdown": "markdown", - "wysiwyg_basic": "Elit tempor laborum consequat nisi. Magna est deserunt duis ipsum anim pariatur fugiat officia ea laborum veniam reprehenderit. Irure dolor proident anim officia veniam nisi. Pariatur consequat nisi id est proident eu sint Lorem anim sit pariatur. Consequat et voluptate amet elit enim in velit laboris. Pariatur aliquip elit esse sunt magna in est esse adipisicing anim. Esse laboris ex cupidatat ut incididunt veniam officia officia elit.\r\n", + "wysiwyg_basic": "

Elit tempor laborum consequat nisi.

", "yes_no": 0, - "dropdown": 0, + "dropdown": "Option 1", "images": "3-6e3cefc-neo39,3-7d9231d-i4iv9,3-b138798-i992x,3-6d7e8b3-4dv81", - "fontawesome": "" + "fontawesome": "fontawesome" } }, { "web": { - "metaTitle": "Common Item 3", - "metaLinkText": "Common Item 3" + "metaTitle": "Action Item 3", + "metaLinkText": "Action Item 3" }, "data": { "text": "Vilma Cantu", "textarea": "508 Williamsburg Street, Buxton, Utah, 4962", "markdown": "markdown", - "wysiwyg_basic": "Culpa amet consectetur culpa ipsum duis laboris et consectetur laboris sit amet aute. Esse nulla sint velit quis qui incididunt laboris sit mollit elit sint pariatur. Quis voluptate nulla enim excepteur elit duis quis sit. Ut mollit ut minim esse anim voluptate irure qui elit laborum. Pariatur et sit duis irure consectetur adipisicing sit excepteur aliquip sunt magna. Nisi culpa veniam occaecat eiusmod incididunt veniam eiusmod est ad ea duis qui. Deserunt ea exercitation non pariatur nostrud voluptate consequat aliquip in quis mollit duis eu laboris.\r\n", + "wysiwyg_basic": "

Culpa amet consectetur culpa ipsum duis laboris et consectetur laboris sit amet aute.

", "yes_no": 0, - "dropdown": 1, + "dropdown": "Option 1", "images": "3-6e3cefc-neo39", - "fontawesome": "" + "fontawesome": "fontawesome" } }, { "web": { - "metaTitle": "Common Item 4", - "metaLinkText": "Common Item 4" + "metaTitle": "Action Item 4", + "metaLinkText": "Action Item 4" }, "data": { "text": "Mercedes Noel", "textarea": "715 Irving Street, Carlos, Maryland, 5696", "markdown": "markdown", - "wysiwyg_basic": "Occaecat laborum excepteur nulla officia mollit reprehenderit. Officia culpa esse labore do mollit elit. Eiusmod magna do dolor labore ex occaecat fugiat duis labore qui.\r\n", + "wysiwyg_basic": "

Occaecat laborum excepteur nulla officia mollit reprehenderit. Officia culpa esse labore do mollit elit.

", "yes_no": 0, - "dropdown": 0, + "dropdown": "Option 1", "images": "3-b138798-i992x", - "fontawesome": "" + "fontawesome": "fontawesome" } }, { "web": { - "metaTitle": "Common Item 5", - "metaLinkText": "Common Item 5" + "metaTitle": "Action Item 5", + "metaLinkText": "Action Item 5" }, "data": { "text": "Chrystal Johns", - "textarea": "403 Euclid Avenue, Imperial, Iowa, 2366", + "textarea": "test_email@zesty.io", "markdown": "markdown", - "wysiwyg_basic": "Commodo minim labore ex laborum veniam deserunt consectetur nisi eiusmod consectetur ad do. Aute consectetur proident pariatur aute. Quis ullamco elit veniam labore ullamco dolor pariatur id. Nisi dolore laborum amet quis voluptate nisi.\r\n", + "wysiwyg_basic": "", "yes_no": 0, - "dropdown": 1, + "dropdown": "Option 1", "images": "3-7d9231d-i4iv9", - "fontawesome": "" + "fontawesome": "fontawesome" } } ] diff --git a/cypress/plugins/seeds/content.ts b/cypress/plugins/seeds/content.ts index e66f286b4..02475302e 100644 --- a/cypress/plugins/seeds/content.ts +++ b/cypress/plugins/seeds/content.ts @@ -17,17 +17,20 @@ module.exports = function content(config) { const { formatPathPart } = require("../../../src/utility/formatPathPart"); const { formatName } = require("../../../src/utility/formatName"); const { getSDK } = require("./utils"); + async function seedContent(path: string): Promise { const jsonString = readFileSync(join(__dirname, "../../", path), "utf8"); const json = JSON.parse(jsonString); const sdk = await getSDK(config); - const timeStamp = Date.now(); + const { COMMIT_ID, SPEC } = config?.env; + const SPEC_ID = `${COMMIT_ID} | ${Date.now()}`; // 1) Create Schema // Append commit id for spec tracking // append timestamp to prevent naming conflicts - const modelLabel = `${json.model.label} | ${config.env.COMMIT_ID} | ${timeStamp}`; + const specLabel = !SPEC?.file ? "" : `${SPEC?.folder}-${SPEC?.file}`; + const modelLabel = `E2E: ${specLabel || json?.model?.label} | ${SPEC_ID}`; const modelPayload = { ...json.model, label: modelLabel, @@ -52,12 +55,13 @@ module.exports = function content(config) { json.items.map((item, index) => { // Append commit id to item labels for spec tracking // append timestamp to prevent naming conflicts - const itemLabel = `${item.web.metaTitle}-${config.env.COMMIT_ID}-${timeStamp}`; + const itemLabel = `${item?.web?.metaTitle} | ${SPEC_ID}`; const payload = { ...item, meta: { ...item.meta, sort: item.meta?.sort ?? index, + contentModelZUID: model?.ZUID, }, web: { ...item.web, diff --git a/src/apps/content-editor/src/app/components/Editor/Field/InternalLink.tsx b/src/apps/content-editor/src/app/components/Editor/Field/InternalLink.tsx index d87a6e6d0..c7df8af19 100644 --- a/src/apps/content-editor/src/app/components/Editor/Field/InternalLink.tsx +++ b/src/apps/content-editor/src/app/components/Editor/Field/InternalLink.tsx @@ -30,8 +30,7 @@ export const InternalLink = ({ useEffect(() => { // Resolve the itemZUID in case it isn't in the store cache if ( - // make sure that internalLinkRelatedItem from store has meta data - !internalLinkRelatedItem?.meta?.ZUID && + !internalLinkRelatedItem && zuid.isValid(value) && zuid.matches(value, zuid.prefix["SITE_CONTENT_ITEM"]) ) { From a038c36ffeddd1293fa2c50db96fbcb2a8460298 Mon Sep 17 00:00:00 2001 From: geodem Date: Tue, 16 Dec 2025 20:42:21 +0800 Subject: [PATCH 06/13] removed before:spec and experimentalInteractiveRunEvents --- cypress.config.js | 9 --------- cypress/fixtures/content.json | 8 ++++---- cypress/plugins/seeds/content.ts | 8 +++----- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/cypress.config.js b/cypress.config.js index 2db7ed8cf..e370fd52f 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -7,7 +7,6 @@ module.exports = defineConfig({ viewportHeight: 1080, video: false, defaultCommandTimeout: 15000, - experimentalInteractiveRunEvents: true, env: { API_AUTH: "https://auth.api.dev.zesty.io", COOKIE_NAME: "DEV_APP_SID", @@ -27,14 +26,6 @@ module.exports = defineConfig({ return launchOptions; }); - // Get spec info to be used for the model's label/name - on("before:spec", (spec) => { - const pathPart = spec.relative.split("/"); - config.env.SPEC = { - folder: pathPart[pathPart?.length - 2], - file: pathPart[pathPart?.length - 1].replace(/\.spec\.js/g, ""), - }; - }); return require("./cypress/plugins/index.js")(on, config); }, diff --git a/cypress/fixtures/content.json b/cypress/fixtures/content.json index 4610d64fb..f54914326 100644 --- a/cypress/fixtures/content.json +++ b/cypress/fixtures/content.json @@ -1,7 +1,7 @@ { "model": { - "label": "E2E: Content", - "metaTitle": "E2E: Content", + "label": "Content", + "metaTitle": "Content", "type": "pageset", "listed": true }, @@ -358,8 +358,8 @@ "sort": 0 }, "web": { - "metaLinkText": "E2E: Content", - "metaTitle": "E2E: Content" + "metaLinkText": "Content", + "metaTitle": "Content" }, "data": { "internal_link": "7-a2c992ecb6-v6j9zt", diff --git a/cypress/plugins/seeds/content.ts b/cypress/plugins/seeds/content.ts index 02475302e..d34bd028c 100644 --- a/cypress/plugins/seeds/content.ts +++ b/cypress/plugins/seeds/content.ts @@ -23,14 +23,12 @@ module.exports = function content(config) { const json = JSON.parse(jsonString); const sdk = await getSDK(config); - const { COMMIT_ID, SPEC } = config?.env; - const SPEC_ID = `${COMMIT_ID} | ${Date.now()}`; + const timeStamp = Date.now(); // 1) Create Schema // Append commit id for spec tracking // append timestamp to prevent naming conflicts - const specLabel = !SPEC?.file ? "" : `${SPEC?.folder}-${SPEC?.file}`; - const modelLabel = `E2E: ${specLabel || json?.model?.label} | ${SPEC_ID}`; + const modelLabel = `E2E: ${json.model.label} | ${config.env.COMMIT_ID} | ${timeStamp}`; const modelPayload = { ...json.model, label: modelLabel, @@ -55,7 +53,7 @@ module.exports = function content(config) { json.items.map((item, index) => { // Append commit id to item labels for spec tracking // append timestamp to prevent naming conflicts - const itemLabel = `${item?.web?.metaTitle} | ${SPEC_ID}`; + const itemLabel = `E2E: ${item.web.metaTitle} | ${config.env.COMMIT_ID} | ${timeStamp}`; const payload = { ...item, meta: { From 4b70212ea875fb95af5af58e801720dda85b7356 Mon Sep 17 00:00:00 2001 From: geodem Date: Wed, 17 Dec 2025 15:58:01 +0800 Subject: [PATCH 07/13] added cypress element tags --- .../components/ItemEditHeader/ItemEditHeaderActions.tsx | 1 + .../components/ItemEditHeader/UnpublishDialog.tsx | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/ItemEditHeaderActions.tsx b/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/ItemEditHeaderActions.tsx index b4710ba80..e9a3e1a68 100644 --- a/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/ItemEditHeaderActions.tsx +++ b/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/ItemEditHeaderActions.tsx @@ -903,6 +903,7 @@ const PublishingMenu = ({ }>(); return ( onClose()} anchorOrigin={{ vertical: "bottom", diff --git a/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/UnpublishDialog.tsx b/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/UnpublishDialog.tsx index c1355a632..51b862841 100644 --- a/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/UnpublishDialog.tsx +++ b/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/UnpublishDialog.tsx @@ -22,7 +22,13 @@ export const UnpublishDialog = ({ loading, }: UnpublishDialogProps) => { return ( - + Date: Wed, 17 Dec 2025 15:59:01 +0800 Subject: [PATCH 08/13] updated element selector | fixed failing tests --- cypress/e2e/content/actions.spec.js | 302 ++++++++++++++++++++-------- 1 file changed, 213 insertions(+), 89 deletions(-) diff --git a/cypress/e2e/content/actions.spec.js b/cypress/e2e/content/actions.spec.js index a90027d34..f810b3970 100644 --- a/cypress/e2e/content/actions.spec.js +++ b/cypress/e2e/content/actions.spec.js @@ -1,10 +1,9 @@ -import { API_ENDPOINTS } from "../../support/api"; - const timestamp = Date.now(); const TEST_DATA = { new: `New Item:${timestamp}`, ai: `AI Generated:${timestamp}`, }; +const requestTimeout = 15000; describe("Actions in content editor", () => { let CONTENT_ITEMS = null; @@ -23,7 +22,9 @@ describe("Actions in content editor", () => { Array.isArray(fields) && fields?.find((field) => field.datatype === "fontawesome"); cy.apiRequest({ - url: `${API_ENDPOINTS.devInstance}/content/models/${model?.ZUID}/fields/${fontAwesomeField?.ZUID}`, + url: `${Cypress.env("API_INSTANCE_URL")}/content/models/${ + model?.ZUID + }/fields/${fontAwesomeField?.ZUID}`, method: "DELETE", }); } @@ -37,20 +38,32 @@ describe("Actions in content editor", () => { ); }); - cy.get(`[data-cy="field:markdown"] textarea`) + cy.get(`[data-cy="field:markdown"]`) + .find("textarea") + .click() .clear() - .should("have.value", "") - .wait(500); + .wait(500) + .should("have.value", ""); + cy.get("#SaveItemButton").trigger("click"); cy.get("[data-cy=toast]").contains("Missing Data in Required Fields", { matchCase: false, }); - cy.get(`[data-cy="field:markdown"] textarea`).clear().type("markdown"); + cy.get(`[data-cy="field:markdown"]`) + .find("textarea") + .click() + .clear() + .type("markdown"); }); it("Must not save when exceeding or lacking characters", () => { - cy.get(`[data-cy="field:text"] input`).clear().type("aa").wait(500); + cy.get(`[data-cy="field:text"]`) + .find("input") + .click() + .clear() + .type("aa") + .wait(500); cy.get("#SaveItemButton").trigger("click"); cy.getBySelector("FieldErrorsList").should("exist"); cy.getBySelector("FieldErrorsList") @@ -58,7 +71,9 @@ describe("Actions in content editor", () => { .find("li") .first() .contains("Requires 8 more characters."); - cy.get(`[data-cy="field:text"] input`) + cy.get(`[data-cy="field:text"]`) + .find("input") + .click() .clear() .type("Lorem ipsum dolor sit amet, consect") .wait(500); @@ -69,7 +84,9 @@ describe("Actions in content editor", () => { .find("li") .first() .contains("Exceeding by 5 characters."); - cy.get(`[data-cy="field:text"] input`) + cy.get(`[data-cy="field:text"]`) + .find("input") + .click() .clear() .type("Lorem ipsum") .wait(500); @@ -80,11 +97,20 @@ describe("Actions in content editor", () => { matchCase: false, } ); - cy.get(`[data-cy="field:text"] input`).clear().type("Mitchell Wilder"); + cy.get(`[data-cy="field:text"]`) + .find("input") + .click() + .clear() + .type("Mitchell Wilder"); }); it("Must not save when regex is not matched", () => { - cy.get(`[data-cy="field:textarea"] textarea:eq(0)`).clear().type("aa"); + cy.get(`[data-cy="field:textarea"]`) + .find("textarea") + .first() + .click() + .clear() + .type("aa"); cy.get("#SaveItemButton").trigger("click"); cy.getBySelector("FieldErrorsList").should("exist"); cy.getBySelector("FieldErrorsList") @@ -92,8 +118,10 @@ describe("Actions in content editor", () => { .find("li") .first() .contains("Must be an email (e.g. hello@zesty.io)"); - cy.get(`[data-cy="field:textarea"] textarea:eq(0)`) + cy.get(`[data-cy="field:textarea"]`) + .find("textarea") .first() + .click() .clear() .type("hello@zesty.io") .wait(500); @@ -104,7 +132,10 @@ describe("Actions in content editor", () => { matchCase: false, } ); - cy.get(`[data-cy="field:textarea"] textarea:eq(0)`) + cy.get(`[data-cy="field:textarea"]`) + .find("textarea") + .first() + .click() .clear() .type("test_email@zesty.io"); }); @@ -117,10 +148,11 @@ describe("Actions in content editor", () => { cy.get(`[data-cy="field:fontawesome"] input`).should("not.exist"); // Make an edit to enable save button - cy.get(`[data-cy="field:text"] input`) + cy.get(`[data-cy="field:text"]`) + .find("input") + .click() .clear() - .type(TEST_DATA?.new) - .wait(500); + .type(TEST_DATA?.new); cy.get("#SaveItemButton").trigger("click"); @@ -136,11 +168,11 @@ describe("Actions in content editor", () => { cy.get("textarea") .first() - .wait(500) + .click() .type("{selectall}{backspace}This is an item meta description") .should("have.value", "This is an item meta description"); - cy.get('[data-cy="itemRoute"] input').type("/"); + cy.get('[data-cy="itemRoute"]').find("input").type("/"); cy.get(".MuiAutocomplete-listbox li:eq(0)").click(); cy.waitOn( @@ -156,87 +188,142 @@ describe("Actions in content editor", () => { }); it("Publishes an item", () => { - cy.waitOn("/v1/content/models**", () => { - cy.visit( - `/content/${Cypress.env("modelZUID")}/${CONTENT_ITEMS?.[4]?.meta?.ZUID}` - ); - }); - cy.intercept("**/publishings").as("publish"); - cy.getBySelector("PublishButton") + const { models, search, items, publishings, publishItem } = awaitRequests(); + cy.visit( + `/content/${Cypress.env("modelZUID")}/${CONTENT_ITEMS?.[4]?.meta?.ZUID}` + ); + + // Apply extended timeout for potentially slow-loading item and publishing data + cy.wait([models, search, items, publishings], { requestTimeout }); + + cy.getBySelector("PublishButton").should("exist").should("be.enabled"); + cy.getBySelector("PublishButton").click(); + + cy.getBySelector("ConfirmPublishModal") .should("exist") - .should("be.enabled") - .click(); - cy.getBySelector("ConfirmPublishModal").should("exist").wait(500); - cy.getBySelector("ConfirmPublishButton").should("exist").click(); + .within(() => { + cy.getBySelector("ConfirmPublishButton").should("exist"); + cy.getBySelector("ConfirmPublishButton").click(); + }); + + cy.wait(publishItem); + cy.wait(publishings); - cy.wait("@publish"); cy.getBySelector("ContentPublishedIndicator").should("exist"); }); it("Unpublishes an item", () => { - cy.intercept("**/publishings/**").as("publish"); - cy.getBySelector("PublishMenuButton") + cy.get(`[data-cy="field:text"]`).find("input").click(); + const { deletePublishedItem, publishings } = awaitRequests(); + cy.getBySelector("PublishMenuButton").should("exist").should("be.enabled"); + cy.getBySelector("PublishMenuButton").click(); + + cy.getBySelector("publishingMenu") .should("exist") - .should("be.enabled") - .click(); - cy.getBySelector("UnpublishContentButton").should("exist").click(); - cy.get(".MuiDialog-root").should("exist").wait(500); - cy.getBySelector("ConfirmUnpublishButton").should("exist").click(); + .within(() => { + cy.getBySelector("UnpublishContentButton").should("exist"); + cy.getBySelector("UnpublishContentButton").click(); + }); + + cy.getBySelector("unpublishDialog") + .should("exist") + .within(() => { + cy.getBySelector("ConfirmUnpublishButton").should("exist"); + cy.getBySelector("ConfirmUnpublishButton").click(); + }); + + cy.wait(deletePublishedItem); + cy.wait(publishings); - cy.wait("@publish"); - cy.getBySelector("PublishButton").should("exist"); + cy.getBySelector("PublishButton").should("exist").should("be.enabled"); }); it("Schedules a Publish for an item", () => { - cy.waitOn("/v1/content/models**", () => { - cy.visit( - `/content/${Cypress.env("modelZUID")}/${CONTENT_ITEMS?.[4]?.meta?.ZUID}` - ); - }); - cy.intercept("**/publishings").as("publish"); - cy.getBySelector("PublishMenuButton") + const { models, search, items, publishings, publishItem } = awaitRequests(); + cy.visit( + `/content/${Cypress.env("modelZUID")}/${CONTENT_ITEMS?.[4]?.meta?.ZUID}` + ); + + // Apply extended timeout for potentially slow-loading item and publishing data + cy.wait([models, search, items, publishings], { requestTimeout }); + + cy.getBySelector("PublishMenuButton").should("exist").should("be.enabled"); + cy.getBySelector("PublishMenuButton").click(); + + cy.getBySelector("publishingMenu") .should("exist") - .should("be.enabled") - .click(); - cy.getBySelector("PublishScheduleButton").should("exist").click(); - cy.getBySelector("SchedulePublishModal").should("exist").wait(500); - cy.getBySelector("SchedulePublishButton").should("exist").click(); - cy.wait("@publish"); + .within(() => { + cy.getBySelector("PublishScheduleButton").should("exist"); + cy.getBySelector("PublishScheduleButton").click(); + }); + + cy.getBySelector("SchedulePublishModal") + .should("exist") + .within(() => { + cy.getBySelector("SchedulePublishButton").should("exist"); + cy.getBySelector("SchedulePublishButton").click(); + }); + + cy.wait(publishItem); + cy.wait(publishings); + cy.getBySelector("ContentScheduledIndicator").should("exist"); }); it("Unschedules a Publish for an item", () => { - cy.intercept("**/publishings**").as("publish"); - cy.getBySelector("PublishMenuButton") + const { deletePublishedItem, publishings } = awaitRequests(); + + cy.getBySelector("PublishMenuButton").should("exist").should("be.enabled"); + cy.getBySelector("PublishMenuButton").click(); + + cy.getBySelector("publishingMenu") .should("exist") - .should("be.enabled") - .click(); - cy.getBySelector("PublishScheduleButton").should("exist").click(); - cy.getBySelector("SchedulePublishModal").should("exist").wait(500); - cy.getBySelector("UnschedulePublishButton").should("exist").click(); - cy.wait("@publish"); + .within(() => { + cy.getBySelector("PublishScheduleButton").should("exist"); + cy.getBySelector("PublishScheduleButton").click(); + }); + + cy.getBySelector("SchedulePublishModal") + .should("exist") + .within(() => { + cy.getBySelector("UnschedulePublishButton").should("exist"); + cy.getBySelector("UnschedulePublishButton").click(); + }); + + cy.wait(deletePublishedItem); + cy.wait(publishings); + cy.getBySelector("ContentScheduledIndicator").should("not.exist"); }); it("Only allows future dates to be scheduled for publish", () => { - cy.getBySelector("PublishMenuButton") + cy.getBySelector("PublishMenuButton").should("exist").should("be.enabled"); + cy.getBySelector("PublishMenuButton").click(); + + cy.getBySelector("publishingMenu") .should("exist") - .should("be.enabled") - .click(); - cy.getBySelector("PublishScheduleButton").should("exist").click(); - cy.getBySelector("PublishScheduleModal").should("exist").wait(500); + .within(() => { + cy.getBySelector("PublishScheduleButton").should("exist"); + cy.getBySelector("PublishScheduleButton").click(); + }); + cy.getBySelector("PublishScheduleModal") - .find("[data-cy='datePickerInputField']") .should("exist") - .click(); + .within(() => { + cy.getBySelector("datePickerInputField").should("exist"); + cy.getBySelector("datePickerInputField").click(); + }); cy.get( '.MuiPickersArrowSwitcher-root button[aria-label="Previous month"]' ).should("be.disabled"); + cy.get( '.MuiPickersArrowSwitcher-root button[aria-label="Next month"]' ).should("not.be.disabled"); - cy.getBySelector("CancelSchedulePublishButton").should("exist").click(); + + cy.getBySelector("CancelSchedulePublishButton").should("exist"); + cy.getBySelector("CancelSchedulePublishButton").click(); }); it("Fills in default values for a new item", () => { @@ -244,18 +331,25 @@ describe("Actions in content editor", () => { cy.visit(`/content/${Cypress.env("modelZUID")}/new`); }); - cy.get('[data-cy="field:text"] input').should( - "have.value", - FIELDS.find((field) => field.name === "text").settings.defaultValue - ); - cy.get('[data-cy="field:textarea"] textarea:eq(0)').should( - "have.value", - FIELDS.find((field) => field.name === "textarea").settings.defaultValue - ); - cy.get('[data-cy="field:markdown"] textarea').should( - "have.value", - FIELDS.find((field) => field.name === "markdown").settings.defaultValue - ); + cy.get('[data-cy="field:text"]') + .find("input") + .should( + "have.value", + FIELDS.find((field) => field.name === "text").settings.defaultValue + ); + cy.get('[data-cy="field:textarea"]') + .find("textarea") + .first() + .should( + "have.value", + FIELDS.find((field) => field.name === "textarea").settings.defaultValue + ); + cy.get('[data-cy="field:markdown"]') + .find("textarea") + .should( + "have.value", + FIELDS.find((field) => field.name === "markdown").settings.defaultValue + ); }); it("Creates a new item", () => { @@ -263,14 +357,17 @@ describe("Actions in content editor", () => { cy.visit(`/content/${Cypress.env("modelZUID")}/new`); }); - cy.get('[data-cy="field:text"] input').clear(); - cy.get('[data-cy="field:text"] input').should("have.value", ""); - cy.get('[data-cy="field:text"] input').type(TEST_DATA?.new); + cy.get('[data-cy="field:text"]') + .find("input") + .click() + .clear() + .type(TEST_DATA?.new); + cy.getBySelector("ManualMetaFlow").click(); cy.getBySelector("metaDescription") .find("textarea") .first() - .wait(500) + .click() .type(TEST_DATA?.new); cy.getBySelector("CreateItemSaveButton").click(); @@ -337,9 +434,11 @@ describe("Actions in content editor", () => { // Increase timeout to account for longer AI generation times. const aiDataGenerationTimeout = { timeout: 60_000 }; - cy.get('[data-cy="field:text"] input').clear(); - cy.get('[data-cy="field:text"] input').should("have.value", ""); - cy.get('[data-cy="field:text"] input').type(TEST_DATA?.ai); + cy.get('[data-cy="field:text"]') + .find("input") + .click() + .clear() + .type(TEST_DATA?.ai); // Generate AI content for markdown cy.get(`[data-cy="field:markdown"]`).find("[data-cy='AIOpen']").click(); @@ -365,7 +464,7 @@ describe("Actions in content editor", () => { cy.getBySelector("ManualMetaFlow").click(); // Generate AI content for meta title - cy.getBySelector("metaTitle").find("input").clear(); + cy.getBySelector("metaTitle").find("input").click().clear(); cy.getBySelector("metaTitle").find("[data-cy='AIOpen']").click(); cy.get("[data-cy='AIGenerate']").click(); @@ -376,6 +475,7 @@ describe("Actions in content editor", () => { // Generate AI content for meta description cy.getBySelector("metaDescription") .find("textarea[name='metaDescription']") + .click() .clear(); cy.getBySelector("metaDescription").find("[data-cy='AIOpen']").click(); cy.getBySelector("AIGenerate").click(); @@ -389,3 +489,27 @@ describe("Actions in content editor", () => { cy.contains("Created Item", aiDataGenerationTimeout).should("exist"); }); }); + +function awaitRequests() { + cy.intercept("GET", "/v1/content/models**").as("models"); + cy.intercept("GET", "/v1/search/items*").as("search"); + cy.intercept("GET", "/v1/content/models/*/items/*/publishings*").as( + "publishings" + ); + cy.intercept("GET", "/v1/content/models/*/items*").as("items"); + cy.intercept("POST", "/v1/content/models/*/items/*/publishings").as( + "publishItem" + ); + cy.intercept("DELETE", "/v1/content/models/*/items/*/publishings/*").as( + "deletePublishedItem" + ); + + return { + models: "@models", + search: "@search", + publishings: "@publishings", + items: "@items", + publishItem: "@publishItem", + deletePublishedItem: "@deletePublishedItem", + }; +} From bfac784afe6093b7f33951de215122d98a96086f Mon Sep 17 00:00:00 2001 From: geodem Date: Wed, 17 Dec 2025 22:16:35 +0800 Subject: [PATCH 09/13] added cypress element selector 'data-cy' for better dom control --- .../src/app/views/ItemEdit/Meta/settings/ItemParent.tsx | 7 +++++++ .../components/ItemEditHeader/ItemEditHeaderActions.tsx | 1 + 2 files changed, 8 insertions(+) diff --git a/src/apps/content-editor/src/app/views/ItemEdit/Meta/settings/ItemParent.tsx b/src/apps/content-editor/src/app/views/ItemEdit/Meta/settings/ItemParent.tsx index 4eea48edf..e4625922c 100644 --- a/src/apps/content-editor/src/app/views/ItemEdit/Meta/settings/ItemParent.tsx +++ b/src/apps/content-editor/src/app/views/ItemEdit/Meta/settings/ItemParent.tsx @@ -265,6 +265,13 @@ export const ItemParent = ({ onChange }: ItemParentProps) => { {value.text} )} + slotProps={{ + listbox: { + ...({ + "data-cy": "itemRouteListBox", + } as React.HTMLAttributes), + }, + }} getOptionLabel={(option) => option.text} onInputChange={(_, filterTerm) => { if (filterTerm !== "/") { diff --git a/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/ItemEditHeaderActions.tsx b/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/ItemEditHeaderActions.tsx index e9a3e1a68..98c350da1 100644 --- a/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/ItemEditHeaderActions.tsx +++ b/src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/ItemEditHeaderActions.tsx @@ -519,6 +519,7 @@ export const ItemEditHeaderActions = ({ loading={saving} disabled={!canUpdate} id="SaveItemButton" + data-cy="SaveItemButton" > Save From c2cb7ffa4b154a95a8da46ad10f9a337e6eb0daa Mon Sep 17 00:00:00 2001 From: geodem Date: Wed, 17 Dec 2025 22:17:50 +0800 Subject: [PATCH 10/13] utilized custom selector for data-cy attribute | added assertions --- cypress/e2e/content/actions.spec.js | 146 ++++++++++++++++------------ 1 file changed, 86 insertions(+), 60 deletions(-) diff --git a/cypress/e2e/content/actions.spec.js b/cypress/e2e/content/actions.spec.js index f810b3970..2541f97f1 100644 --- a/cypress/e2e/content/actions.spec.js +++ b/cypress/e2e/content/actions.spec.js @@ -38,19 +38,22 @@ describe("Actions in content editor", () => { ); }); - cy.get(`[data-cy="field:markdown"]`) + cy.getBySelector(`"field:markdown"`) .find("textarea") .click() .clear() .wait(500) .should("have.value", ""); - cy.get("#SaveItemButton").trigger("click"); + cy.getBySelector("SaveItemButton") + .should("exist") + .should("be.enabled") + .trigger("click"); - cy.get("[data-cy=toast]").contains("Missing Data in Required Fields", { + cy.getBySelector("toast").contains("Missing Data in Required Fields", { matchCase: false, }); - cy.get(`[data-cy="field:markdown"]`) + cy.getBySelector(`"field:markdown"`) .find("textarea") .click() .clear() @@ -58,46 +61,55 @@ describe("Actions in content editor", () => { }); it("Must not save when exceeding or lacking characters", () => { - cy.get(`[data-cy="field:text"]`) + cy.getBySelector(`"field:text"`) .find("input") .click() .clear() .type("aa") .wait(500); - cy.get("#SaveItemButton").trigger("click"); + cy.getBySelector("SaveItemButton") + .should("exist") + .should("be.enabled") + .trigger("click"); cy.getBySelector("FieldErrorsList").should("exist"); cy.getBySelector("FieldErrorsList") .find("ol") .find("li") .first() .contains("Requires 8 more characters."); - cy.get(`[data-cy="field:text"]`) + cy.getBySelector(`"field:text"`) .find("input") .click() .clear() .type("Lorem ipsum dolor sit amet, consect") .wait(500); - cy.get("#SaveItemButton").trigger("click"); + cy.getBySelector("SaveItemButton") + .should("exist") + .should("be.enabled") + .trigger("click"); cy.getBySelector("FieldErrorsList").should("exist"); cy.getBySelector("FieldErrorsList") .find("ol") .find("li") .first() .contains("Exceeding by 5 characters."); - cy.get(`[data-cy="field:text"]`) + cy.getBySelector(`"field:text"`) .find("input") .click() .clear() .type("Lorem ipsum") .wait(500); - cy.get("#SaveItemButton").click(); - cy.get("[data-cy=toast]").contains( + cy.getBySelector("SaveItemButton") + .should("exist") + .should("be.enabled") + .trigger("click"); + cy.getBySelector("toast").contains( `Item Saved: ${CONTENT_ITEMS?.[0].web.metaTitle}`, { matchCase: false, } ); - cy.get(`[data-cy="field:text"]`) + cy.getBySelector(`"field:text"`) .find("input") .click() .clear() @@ -105,34 +117,40 @@ describe("Actions in content editor", () => { }); it("Must not save when regex is not matched", () => { - cy.get(`[data-cy="field:textarea"]`) + cy.getBySelector(`"field:textarea"`) .find("textarea") .first() .click() .clear() .type("aa"); - cy.get("#SaveItemButton").trigger("click"); + cy.getBySelector("SaveItemButton") + .should("exist") + .should("be.enabled") + .trigger("click"); cy.getBySelector("FieldErrorsList").should("exist"); cy.getBySelector("FieldErrorsList") .find("ol") .find("li") .first() .contains("Must be an email (e.g. hello@zesty.io)"); - cy.get(`[data-cy="field:textarea"]`) + cy.getBySelector(`"field:textarea"`) .find("textarea") .first() .click() .clear() .type("hello@zesty.io") .wait(500); - cy.get("#SaveItemButton").trigger("click"); - cy.get("[data-cy=toast]").contains( + cy.getBySelector("SaveItemButton") + .should("exist") + .should("be.enabled") + .trigger("click"); + cy.getBySelector("toast").contains( `Item Saved: ${CONTENT_ITEMS?.[0].web.metaTitle}`, { matchCase: false, } ); - cy.get(`[data-cy="field:textarea"]`) + cy.getBySelector(`"field:textarea"`) .find("textarea") .first() .click() @@ -148,15 +166,18 @@ describe("Actions in content editor", () => { cy.get(`[data-cy="field:fontawesome"] input`).should("not.exist"); // Make an edit to enable save button - cy.get(`[data-cy="field:text"]`) + cy.getBySelector(`"field:text"`) .find("input") .click() .clear() .type(TEST_DATA?.new); - cy.get("#SaveItemButton").trigger("click"); + cy.getBySelector("SaveItemButton") + .should("exist") + .should("be.enabled") + .trigger("click"); - cy.get("[data-cy=toast]").contains("Item Saved"); + cy.getBySelector("toast").contains("Item Saved"); }); it("Saves page item metadata", () => { @@ -166,44 +187,49 @@ describe("Actions in content editor", () => { ); }); - cy.get("textarea") + cy.getBySelector("metaDescription") + .find("textarea") .first() .click() + .clear() .type("{selectall}{backspace}This is an item meta description") .should("have.value", "This is an item meta description"); - cy.get('[data-cy="itemRoute"]').find("input").type("/"); - cy.get(".MuiAutocomplete-listbox li:eq(0)").click(); + cy.getBySelector("itemRoute").find("input").click().clear().type("/"); + cy.getBySelector("itemRouteListBox").find("li").first().click(); cy.waitOn( `/v1/content/models/${Cypress.env("modelZUID")}/items/${Cypress.env( "itemZUID" )}`, () => { - cy.get("#SaveItemButton").trigger("click"); + cy.getBySelector("SaveItemButton") + .should("exist") + .should("be.enabled") + .trigger("click"); } ); - cy.get("[data-cy=toast]").contains("Item Saved"); + cy.getBySelector("toast").contains("Item Saved"); }); it("Publishes an item", () => { - const { models, search, items, publishings, publishItem } = awaitRequests(); + const { items, publishItem, publishings } = awaitRequests(); cy.visit( `/content/${Cypress.env("modelZUID")}/${CONTENT_ITEMS?.[4]?.meta?.ZUID}` ); // Apply extended timeout for potentially slow-loading item and publishing data - cy.wait([models, search, items, publishings], { requestTimeout }); + cy.wait([items, publishings], { requestTimeout }); cy.getBySelector("PublishButton").should("exist").should("be.enabled"); - cy.getBySelector("PublishButton").click(); + cy.getBySelector("PublishButton").trigger("click"); cy.getBySelector("ConfirmPublishModal") .should("exist") .within(() => { cy.getBySelector("ConfirmPublishButton").should("exist"); - cy.getBySelector("ConfirmPublishButton").click(); + cy.getBySelector("ConfirmPublishButton").trigger("click"); }); cy.wait(publishItem); @@ -213,23 +239,27 @@ describe("Actions in content editor", () => { }); it("Unpublishes an item", () => { - cy.get(`[data-cy="field:text"]`).find("input").click(); - const { deletePublishedItem, publishings } = awaitRequests(); + const { items, deletePublishedItem, publishings } = awaitRequests(); + cy.visit( + `/content/${Cypress.env("modelZUID")}/${CONTENT_ITEMS?.[4]?.meta?.ZUID}` + ); + cy.wait([items, publishings], { requestTimeout }); + cy.getBySelector("PublishMenuButton").should("exist").should("be.enabled"); - cy.getBySelector("PublishMenuButton").click(); + cy.getBySelector("PublishMenuButton").trigger("click"); cy.getBySelector("publishingMenu") .should("exist") .within(() => { cy.getBySelector("UnpublishContentButton").should("exist"); - cy.getBySelector("UnpublishContentButton").click(); + cy.getBySelector("UnpublishContentButton").trigger("click"); }); cy.getBySelector("unpublishDialog") .should("exist") .within(() => { cy.getBySelector("ConfirmUnpublishButton").should("exist"); - cy.getBySelector("ConfirmUnpublishButton").click(); + cy.getBySelector("ConfirmUnpublishButton").trigger("click"); }); cy.wait(deletePublishedItem); @@ -239,29 +269,29 @@ describe("Actions in content editor", () => { }); it("Schedules a Publish for an item", () => { - const { models, search, items, publishings, publishItem } = awaitRequests(); + const { items, publishItem, publishings } = awaitRequests(); cy.visit( `/content/${Cypress.env("modelZUID")}/${CONTENT_ITEMS?.[4]?.meta?.ZUID}` ); // Apply extended timeout for potentially slow-loading item and publishing data - cy.wait([models, search, items, publishings], { requestTimeout }); + cy.wait([items, publishings], { requestTimeout }); cy.getBySelector("PublishMenuButton").should("exist").should("be.enabled"); - cy.getBySelector("PublishMenuButton").click(); + cy.getBySelector("PublishMenuButton").trigger("click"); cy.getBySelector("publishingMenu") .should("exist") .within(() => { cy.getBySelector("PublishScheduleButton").should("exist"); - cy.getBySelector("PublishScheduleButton").click(); + cy.getBySelector("PublishScheduleButton").trigger("click"); }); cy.getBySelector("SchedulePublishModal") .should("exist") .within(() => { cy.getBySelector("SchedulePublishButton").should("exist"); - cy.getBySelector("SchedulePublishButton").click(); + cy.getBySelector("SchedulePublishButton").trigger("click"); }); cy.wait(publishItem); @@ -274,20 +304,20 @@ describe("Actions in content editor", () => { const { deletePublishedItem, publishings } = awaitRequests(); cy.getBySelector("PublishMenuButton").should("exist").should("be.enabled"); - cy.getBySelector("PublishMenuButton").click(); + cy.getBySelector("PublishMenuButton").trigger("click"); cy.getBySelector("publishingMenu") .should("exist") .within(() => { cy.getBySelector("PublishScheduleButton").should("exist"); - cy.getBySelector("PublishScheduleButton").click(); + cy.getBySelector("PublishScheduleButton").trigger("click"); }); cy.getBySelector("SchedulePublishModal") .should("exist") .within(() => { cy.getBySelector("UnschedulePublishButton").should("exist"); - cy.getBySelector("UnschedulePublishButton").click(); + cy.getBySelector("UnschedulePublishButton").trigger("click"); }); cy.wait(deletePublishedItem); @@ -298,20 +328,20 @@ describe("Actions in content editor", () => { it("Only allows future dates to be scheduled for publish", () => { cy.getBySelector("PublishMenuButton").should("exist").should("be.enabled"); - cy.getBySelector("PublishMenuButton").click(); + cy.getBySelector("PublishMenuButton").trigger("click"); cy.getBySelector("publishingMenu") .should("exist") .within(() => { cy.getBySelector("PublishScheduleButton").should("exist"); - cy.getBySelector("PublishScheduleButton").click(); + cy.getBySelector("PublishScheduleButton").trigger("click"); }); cy.getBySelector("PublishScheduleModal") .should("exist") .within(() => { cy.getBySelector("datePickerInputField").should("exist"); - cy.getBySelector("datePickerInputField").click(); + cy.getBySelector("datePickerInputField").trigger("click"); }); cy.get( @@ -323,7 +353,7 @@ describe("Actions in content editor", () => { ).should("not.be.disabled"); cy.getBySelector("CancelSchedulePublishButton").should("exist"); - cy.getBySelector("CancelSchedulePublishButton").click(); + cy.getBySelector("CancelSchedulePublishButton").trigger("click"); }); it("Fills in default values for a new item", () => { @@ -331,20 +361,20 @@ describe("Actions in content editor", () => { cy.visit(`/content/${Cypress.env("modelZUID")}/new`); }); - cy.get('[data-cy="field:text"]') + cy.getBySelector(`"field:text"`) .find("input") .should( "have.value", FIELDS.find((field) => field.name === "text").settings.defaultValue ); - cy.get('[data-cy="field:textarea"]') + cy.getBySelector(`"field:textarea"`) .find("textarea") .first() .should( "have.value", FIELDS.find((field) => field.name === "textarea").settings.defaultValue ); - cy.get('[data-cy="field:markdown"]') + cy.getBySelector(`"field:markdown"`) .find("textarea") .should( "have.value", @@ -357,7 +387,7 @@ describe("Actions in content editor", () => { cy.visit(`/content/${Cypress.env("modelZUID")}/new`); }); - cy.get('[data-cy="field:text"]') + cy.getBySelector(`"field:text"`) .find("input") .click() .clear() @@ -371,7 +401,7 @@ describe("Actions in content editor", () => { .type(TEST_DATA?.new); cy.getBySelector("CreateItemSaveButton").click(); - cy.get("[data-cy=toast]") + cy.getBySelector("toast") .contains(`Created Item: ${TEST_DATA?.new}`, { matchCase: false }) .should("exist"); }); @@ -434,14 +464,14 @@ describe("Actions in content editor", () => { // Increase timeout to account for longer AI generation times. const aiDataGenerationTimeout = { timeout: 60_000 }; - cy.get('[data-cy="field:text"]') + cy.getBySelector(`"field:text"`) .find("input") .click() .clear() .type(TEST_DATA?.ai); // Generate AI content for markdown - cy.get(`[data-cy="field:markdown"]`).find("[data-cy='AIOpen']").click(); + cy.getBySelector(`"field:markdown"`).find("[data-cy='AIOpen']").click(); cy.getBySelector("AITopicField").type("biking"); cy.getBySelector("AIAudienceField").type("young adults"); cy.getBySelector("AIGenerate").click(); @@ -449,7 +479,7 @@ describe("Actions in content editor", () => { cy.get("[data-cy='AIApprove']", aiDataGenerationTimeout).click(); // Generate AI content for wysiwyg_basic - cy.get(`[data-cy="field:wysiwyg_basic"]`) + cy.getBySelector(`"field:wysiwyg_basic"`) .find("[data-cy='AIOpen']") .click(); cy.getBySelector("AITopicField").type("biking"); @@ -491,9 +521,7 @@ describe("Actions in content editor", () => { }); function awaitRequests() { - cy.intercept("GET", "/v1/content/models**").as("models"); - cy.intercept("GET", "/v1/search/items*").as("search"); - cy.intercept("GET", "/v1/content/models/*/items/*/publishings*").as( + cy.intercept("GET", "/v1/content/models/*/items/*/publishings").as( "publishings" ); cy.intercept("GET", "/v1/content/models/*/items*").as("items"); @@ -505,8 +533,6 @@ function awaitRequests() { ); return { - models: "@models", - search: "@search", publishings: "@publishings", items: "@items", publishItem: "@publishItem", From 9e3f4e29ac8f4e06126d15ba25074515569b4995 Mon Sep 17 00:00:00 2001 From: geodem Date: Fri, 5 Dec 2025 01:12:24 +0800 Subject: [PATCH 11/13] added data seeding | updated element selectors --- cypress/e2e/content/item-list-table.spec.js | 31 +++++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/cypress/e2e/content/item-list-table.spec.js b/cypress/e2e/content/item-list-table.spec.js index f219d5110..6dc6a0d75 100644 --- a/cypress/e2e/content/item-list-table.spec.js +++ b/cypress/e2e/content/item-list-table.spec.js @@ -1,25 +1,38 @@ const NOW = Date.now(); describe("Content item list table", () => { + let MODEL = null; + let FIELDS = null; + let ITEMS = null; + before(() => { + cy.task("seed:content", "fixtures/actions.json").then( + ({ model, fields, items }) => { + Cypress.env("modelZUID", model?.ZUID); + Cypress.env("itemZUID", items[0]?.meta?.ZUID); + MODEL = model; + ITEMS = items; + FIELDS = fields; + } + ); + }); + it("Resolves internal link zuids", () => { cy.waitOn("/search/items*", () => { - cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-a1a600-k0b6f0"); + cy.waitOn("/v1/content/models**", () => { + cy.visit(`/content/${Cypress.env("modelZUID")}`); }); }); - cy.getBySelector("SingleRelationshipCell", { timeout: 30000 }) + // cy.getBySelector("SingleRelationshipCell") + cy.get(".MuiDataGrid-row .MuiDataGrid-cell:eq(2)") .first() - .contains( - "5 Tricks to Teach Your Pitbull: Fun & Easy Tips for You & Your Dog!", - { timeout: 15_000 } - ); + .contains(ITEMS?.[0]?.web?.metaTitle, { matchCase: false }); }); it("properly removes deleted content items from cache even after page reload", () => { cy.waitOn("/search/items*", () => { cy.waitOn("/v1/content/models*", () => { - cy.visit("/content/6-a1a600-k0b6f0/new"); + cy.visit(`/content/${Cypress.env("modelZUID")}/new`); }); }); @@ -37,7 +50,7 @@ describe("Content item list table", () => { cy.contains("Created Item").should("exist"); - cy.visit("/content/6-a1a600-k0b6f0"); + cy.visit(`/content/${Cypress.env("modelZUID")}`); cy.get(".MuiDataGrid-cellCheckbox").first().click(); cy.getBySelector("MultiPageTableDelete").click(); From 6d4e273e94e0f929858bccd72c2041b2d63fe571 Mon Sep 17 00:00:00 2001 From: geodem Date: Wed, 17 Dec 2025 06:57:14 +0800 Subject: [PATCH 12/13] added cypress element tags 'data-cy' --- .../content-editor/src/app/views/ItemList/ItemListFilters.tsx | 1 + .../content-editor/src/app/views/ItemList/ItemListTable.tsx | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/apps/content-editor/src/app/views/ItemList/ItemListFilters.tsx b/src/apps/content-editor/src/app/views/ItemList/ItemListFilters.tsx index e48ce8bf9..6bd4bcfed 100644 --- a/src/apps/content-editor/src/app/views/ItemList/ItemListFilters.tsx +++ b/src/apps/content-editor/src/app/views/ItemList/ItemListFilters.tsx @@ -247,6 +247,7 @@ export const ItemListFilters = () => { key={field.ZUID} onClick={() => handleUpdateSortOrder(field.name)} selected={activeSortOrder === field.name} + data-cy={`sort:${field.name}`} > {field.label} diff --git a/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx b/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx index 09d715838..8077868a3 100644 --- a/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx +++ b/src/apps/content-editor/src/app/views/ItemList/ItemListTable.tsx @@ -382,6 +382,7 @@ export const ItemListTable = memo( {({ width, height }: Size) => ( { // if included in staged changes, highlight the row From ff4410135ddf2054e98fe460774e818bdbb22bfa Mon Sep 17 00:00:00 2001 From: geodem Date: Wed, 17 Dec 2025 06:58:43 +0800 Subject: [PATCH 13/13] implemented content seeding | updated element selector --- cypress/e2e/content/item-list-table.spec.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cypress/e2e/content/item-list-table.spec.js b/cypress/e2e/content/item-list-table.spec.js index 6dc6a0d75..409ca8189 100644 --- a/cypress/e2e/content/item-list-table.spec.js +++ b/cypress/e2e/content/item-list-table.spec.js @@ -1,17 +1,13 @@ const NOW = Date.now(); describe("Content item list table", () => { - let MODEL = null; - let FIELDS = null; let ITEMS = null; before(() => { cy.task("seed:content", "fixtures/actions.json").then( - ({ model, fields, items }) => { + ({ model, items }) => { Cypress.env("modelZUID", model?.ZUID); Cypress.env("itemZUID", items[0]?.meta?.ZUID); - MODEL = model; ITEMS = items; - FIELDS = fields; } ); }); @@ -23,10 +19,13 @@ describe("Content item list table", () => { }); }); - // cy.getBySelector("SingleRelationshipCell") - cy.get(".MuiDataGrid-row .MuiDataGrid-cell:eq(2)") + cy.getBySelector("sortByFilter_default").click(); + cy.getBySelector(`"sort:text"`).click(); + cy.getBySelector("listItemTable") + .find('[data-cy="itemListRow"]') .first() - .contains(ITEMS?.[0]?.web?.metaTitle, { matchCase: false }); + .find('[data-field="text"]') + .contains(ITEMS?.[1]?.data?.text); }); it("properly removes deleted content items from cache even after page reload", () => { @@ -38,8 +37,10 @@ describe("Content item list table", () => { cy.intercept("/search/items*").as("searchItems"); cy.intercept("/v1/content/models*").as("contentModels"); - - cy.get("input[name=title]").clear().type(`Delete me ${NOW}`); + cy.getBySelector(`"field:text"`) + .find("input") + .clear() + .type(`Delete me ${NOW}`); cy.getBySelector("ManualMetaFlow").click(); cy.getBySelector("metaDescription") .find("textarea")