Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(e2e): e2e setup for the new content editor #31044

Merged
merged 36 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4982b98
e2e: setup for edit content
nicobytes Jan 2, 2025
c5d9b2c
e2e: apply format
nicobytes Jan 2, 2025
f53eba5
chore(e2e): sync with master
nicobytes Jan 13, 2025
e353da0
chore(e2e): sync with master
nicobytes Jan 13, 2025
640af1a
chore(e2e): sync with master
nicobytes Jan 13, 2025
61744c9
chore(e2e): sync with master
nicobytes Jan 13, 2025
56e0ac8
chore(e2e): sync with master
nicobytes Jan 13, 2025
01b89ce
chore(e2e): sync with master
nicobytes Jan 13, 2025
d5790a4
chore(e2e): sync with master
nicobytes Jan 13, 2025
5771eba
chore(e2e): sync with master
nicobytes Jan 13, 2025
d54c05b
chore(e2e): sync with master
nicobytes Jan 13, 2025
496f837
chore(e2e): sync with master
nicobytes Jan 13, 2025
77ae930
Merge branch 'main' into 31033-end-to-end-e2e-setup-for-the-new-conte…
nicobytes Jan 13, 2025
31219ba
chore(e2e): add test for new content editor
nicobytes Jan 13, 2025
4a960d9
chore(e2e): add after hook to delete content type
nicobytes Jan 13, 2025
74083dd
Merge branch 'main' into 31033-end-to-end-e2e-setup-for-the-new-conte…
nicobytes Jan 13, 2025
f28dff6
Merge branch 'main' into 31033-end-to-end-e2e-setup-for-the-new-conte…
nicobytes Jan 14, 2025
32e2208
chore(e2e): update features flags
nicobytes Jan 14, 2025
2fb70a1
chore(e2e): fix error
nicobytes Jan 14, 2025
12f29ba
Merge branch 'main' into 31033-end-to-end-e2e-setup-for-the-new-conte…
nicobytes Jan 14, 2025
f91343f
chore(e2e): enable basic auth in endpoints
nicobytes Jan 15, 2025
0717689
chore(e2e): sync with master
nicobytes Jan 15, 2025
c980f9c
chore(e2e): sync with master
nicobytes Jan 15, 2025
35010fe
Merge branch 'main' into 31033-end-to-end-e2e-setup-for-the-new-conte…
nicobytes Jan 15, 2025
a3e315a
Merge branch 'main' into 31033-end-to-end-e2e-setup-for-the-new-conte…
nicobytes Jan 16, 2025
7ef4013
chore(e2e): docs
nicobytes Jan 16, 2025
0108942
chore(e2e): sync with master
nicobytes Jan 16, 2025
d506f5a
chore(e2e): avoid click
nicobytes Jan 16, 2025
b1fdf60
chore(e2e): apply format
nicobytes Jan 16, 2025
3d8f019
Merge branch 'main' into 31033-end-to-end-e2e-setup-for-the-new-conte…
nicobytes Jan 16, 2025
275a019
Merge branch 'main' into 31033-end-to-end-e2e-setup-for-the-new-conte…
nicobytes Jan 16, 2025
43ea6cc
chore(e2e): fix error with feature flags
nicobytes Jan 16, 2025
07deec5
Merge branch '31033-end-to-end-e2e-setup-for-the-new-content-editor' …
nicobytes Jan 16, 2025
f9dc0b3
chore(e2e): wait response
nicobytes Jan 16, 2025
7866842
chore(e2e): wait response
nicobytes Jan 16, 2025
7c7ab26
chore(e2e): fix error
nicobytes Jan 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 25 additions & 26 deletions e2e/dotcms-e2e-node/frontend/locators/globalLocators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,48 @@
* Locators for the iframes in the main page.
*/
export const iFramesLocators = {
main_iframe: 'iframe[name="detailFrame"]',
dot_iframe: 'dot-iframe-dialog iframe[name="detailFrame"]',
wysiwygFrame: 'iframe[title="Rich Text Area\\. Press ALT-F9 for menu\\. Press ALT-F10 for toolbar\\. Press ALT-0 for help"]',
dataTestId: '[data-testid="iframe"]',
dot_edit_iframe: 'dot-edit-contentlet iframe[name="detailFrame"]',
}
main_iframe: 'iframe[name="detailFrame"]',
dot_iframe: 'dot-iframe-dialog iframe[name="detailFrame"]',
wysiwygFrame:
'iframe[title="Rich Text Area\\. Press ALT-F9 for menu\\. Press ALT-F10 for toolbar\\. Press ALT-0 for help"]',
dataTestId: '[data-testid="iframe"]',
dot_edit_iframe: 'dot-edit-contentlet iframe[name="detailFrame"]',
};

/**
* Locators for the login functionality.
*/
export const loginLocators = {
userNameInput: 'input[id="inputtext"]',
passwordInput: 'input[id="password"]',
loginBtn: 'submitButton'
}
userNameInput: 'input[id="inputtext"]',
passwordInput: 'input[id="password"]',
loginBtn: "submitButton",
};

/**
* Locators for the Add Content functionality.
*/
export const addContent = {
addBtn: '#dijit_form_DropDownButton_0',
addNewContentSubMenu: 'Add New Content',
addNewMenuLabel: '▼'
}
addBtn: "#dijit_form_DropDownButton_0",
addNewContentSubMenu: "Add New Content",
addNewMenuLabel: "▼",
};

/**
* Locators for the Rich Text functionality.
*/
export const contentGeneric = {
locator: "articleContent (Generic)",
label: "Content (Generic)"
}
locator: "articleContent (Generic)",
label: "Content (Generic)",
};

export const fileAsset = {
locator: "attach_fileFile Asset",
label: "File Asset"
}
locator: "attach_fileFile Asset",
label: "File Asset",
};

export const pageAsset = {
locator: "descriptionPage",
label: "Page"
}

export {
} from './navigation/menuLocators';
locator: "descriptionPage",
label: "Page",
};

export {} from "./navigation/menuLocators";
64 changes: 34 additions & 30 deletions e2e/dotcms-e2e-node/frontend/locators/navigation/menuLocators.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,48 @@
import {Page, Locator} from '@playwright/test';
import { Page, Locator } from "@playwright/test";

export class GroupEntriesLocators {
readonly SITE: Locator;
readonly CONTENT: Locator;
readonly SCHEMA: Locator;

constructor(page: Page) {
this.SITE = page.getByText('Site', {exact: true});
this.CONTENT = page.getByRole('complementary').getByText('Content', {exact: true});
this.SCHEMA = page.getByText('Schema');

}
readonly SITE: Locator;
readonly CONTENT: Locator;
readonly CONTENT_MODEL: Locator;
readonly SCHEMA: Locator;

constructor(page: Page) {
this.SITE = page.getByText("Site", { exact: true });
this.CONTENT = page
.getByRole("complementary")
.getByText("Content", { exact: true });

this.CONTENT_MODEL = page.getByText("Content Model");
this.SCHEMA = page.getByText("Schema");
}
}

/**
* Locators for the tools in the menu
*/
export class ToolEntriesLocators {
readonly SEARCH_ALL: Locator;
readonly CONTENT_TYPES: Locator;
readonly CATEGORIES: Locator;


constructor(page: Page) {
this.SEARCH_ALL = page.getByRole('link', {name: 'Search All'});
this.CONTENT_TYPES = page.getByRole('link', {name: 'Content Types'});
this.CATEGORIES = page.getByRole('link', { name: 'Categories' });
}
readonly SEARCH_ALL: Locator;
readonly CONTENT_TYPES: Locator;
readonly CATEGORIES: Locator;

constructor(page: Page) {
this.SEARCH_ALL = page.getByRole("link", { name: "Search All" });
this.CONTENT_TYPES = page.getByRole("link", { name: "Content Types" });
this.CATEGORIES = page.getByRole("link", { name: "Categories" });
}
}

/**
* Locators for the menu entries
*/
export class MenuEntriesLocators {
readonly EXPAND: Locator;
readonly COLLAPSE: Locator;

constructor(page: Page) {
this.EXPAND = page.getByRole('button', { name: '' });
this.COLLAPSE = page.locator('button[ng-reflect-ng-class="[object Object]"]').first();

}
}
readonly EXPAND: Locator;
readonly COLLAPSE: Locator;

constructor(page: Page) {
this.EXPAND = page.getByRole("button", { name: "" });
this.COLLAPSE = page
.locator('button[ng-reflect-ng-class="[object Object]"]')
.first();
}
}
4 changes: 3 additions & 1 deletion e2e/dotcms-e2e-node/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"license": "MIT",
"devDependencies": {
"@eslint/js": "^9.17.0",
"@faker-js/faker": "9.3.0",
"@playwright/test": "^1.48.2",
"@types/node": "^22.5.4",
"@typescript-eslint/eslint-plugin": "^8.19.0",
Expand All @@ -22,10 +23,11 @@
},
"scripts": {
"show-report": "if [[ \"$CURRENT_ENV\" != \"ci\" ]]; then yarn playwright show-report; fi",
"start": "PLAYWRIGHT_JUNIT_SUITE_ID=nodee2etestsuite PLAYWRIGHT_JUNIT_SUITE_NAME='E2E Node Test Suite' PLAYWRIGHT_JUNIT_OUTPUT_FILE='../target/failsafe-reports/TEST-e2e-node-results.xml' yarn playwright test ${PLAYWRIGHT_SPECIFIC} ${PLAYWRIGHT_DEBUG}; yarn run show-report",
"start": "PLAYWRIGHT_JUNIT_SUITE_ID=nodee2etestsuite PLAYWRIGHT_JUNIT_SUITE_NAME='E2E Node Test Suite' PLAYWRIGHT_JUNIT_OUTPUT_FILE='../target/failsafe-reports/TEST-e2e-node-results.xml' yarn playwright test ${PLAYWRIGHT_SPECIFIC} ${PLAYWRIGHT_DEBUG} --ui; yarn run show-report",
"start-local": "CURRENT_ENV=local yarn run start",
"start-dev": "CURRENT_ENV=dev yarn run start",
"start-ci": "CURRENT_ENV=ci yarn run start",
"codegen": "yarn playwright codegen",
"post-testing": "PLAYWRIGHT_JUNIT_OUTPUT_FILE='../target/failsafe-reports/TEST-e2e-node-results.xml' node index.js",
"format": "prettier --write .",
"lint": "eslint .",
Expand Down
19 changes: 19 additions & 0 deletions e2e/dotcms-e2e-node/frontend/pages/contentTypeForm.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Page } from "@playwright/test";

export class ContentTypeFormPage {
constructor(private page: Page) {}

async fillNewContentType() {
await this.addTitleField();
}

async addTitleField() {
const dropZone = this.page.locator('div[dragula="fields-bag"]');
await this.page
.locator("li")
.getByText("Text", { exact: true })
.dragTo(dropZone);
await this.page.locator("input#name").fill("Text Field");
await this.page.getByTestId("dotDialogAcceptAction").click();
}
}
88 changes: 88 additions & 0 deletions e2e/dotcms-e2e-node/frontend/pages/listingContentTypes.pages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Page, expect } from "@playwright/test";
import { MenuEntriesLocators } from "../locators/navigation/menuLocators";
import {
GroupEntriesLocators,
ToolEntriesLocators,
} from "../locators/navigation/menuLocators";
import { dotCMSUtils } from "../utils/dotCMSUtils";

export class ListingContentTypesPage {
private cmsUtils: dotCMSUtils;
private menuLocators: MenuEntriesLocators;
private groupsLocators: GroupEntriesLocators;
private toolsLocators: ToolEntriesLocators;

constructor(private page: Page) {
this.cmsUtils = new dotCMSUtils();
this.menuLocators = new MenuEntriesLocators(this.page);
this.groupsLocators = new GroupEntriesLocators(this.page);
this.toolsLocators = new ToolEntriesLocators(this.page);
}

async goToUrl() {
await this.page.goto("/dotAdmin/#/content-types-angular");
}

async goTo() {
// Get the username and password from the environment variables
const username = process.env.USERNAME as string;
const password = process.env.PASSWORD as string;

// Login to dotCMS
await this.cmsUtils.login(this.page, username, password);
await this.cmsUtils.navigate(
this.menuLocators.EXPAND,
this.groupsLocators.CONTENT_MODEL,
this.toolsLocators.CONTENT_TYPES,
);

// Validate the portlet title
const breadcrumbLocator = this.page.locator("p-breadcrumb");
await expect(breadcrumbLocator).toContainText("Content Types");
}

async addNewContentType(name: string) {
await this.page.getByRole("button", { name: "" }).click();
await this.page.getByLabel("Content").locator("a").click();
await this.page
.locator('[data-test-id="content-type__new-content-banner"] div')
.nth(2)
.click();

await this.page.getByLabel("Content Name").fill(name);
await this.page.getByTestId("dotDialogAcceptAction").click();
}

async goToAddNewContentType(contentType: string) {
const capitalized =
contentType.charAt(0).toUpperCase() + contentType.slice(1);

await this.page
.getByTestId(`row-${capitalized}`)
.getByRole("link", { name: "View (0)" })
.click();
await this.page
.locator('iframe[name="detailFrame"]')
.contentFrame()
.locator("#dijit_form_DropDownButton_0")
.click();
await this.page
.locator('iframe[name="detailFrame"]')
.contentFrame()
.getByLabel("▼")
.getByText("Add New Content")
.click();
}

async deleteContentType(contentType: string) {
const capitalized =
contentType.charAt(0).toUpperCase() + contentType.slice(1);

await this.page
.getByTestId(`row-${capitalized}`)
.getByTestId("dot-menu-button")
.click();
await this.page.getByLabel("Delete").locator("a").click();
await this.page.getByRole("button", { name: "Delete" }).click();
}
}
24 changes: 24 additions & 0 deletions e2e/dotcms-e2e-node/frontend/pages/listngContent.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Page } from "@playwright/test";

export class ListingContentPage {
constructor(private page: Page) {}
#addBtn = this.page.locator("span[widgetid='dijit_form_DropDownButton_0']");
#addNewContent = this.page.locator(
".dijitPopup tr[aria-label='Add New Content']",
);

async goTo(filter?: string) {
const urlPath = new URL("/c/content", this.page.url());

if (filter) {
urlPath.searchParams.set("filter", filter);
}

await this.page.goto(urlPath.toString());
}

async clickAddNewContent() {
await this.#addBtn.click();
await this.#addNewContent.click();
}
}
14 changes: 14 additions & 0 deletions e2e/dotcms-e2e-node/frontend/pages/newEditContentForm.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Page } from "@playwright/test";

export class NewEditContentFormPage {
constructor(private page: Page) {}

async fillTextField(text: string) {
await this.page.getByTestId("textField").fill(text);
}

async save() {
await this.page.getByRole("button", { name: "Save" }).click();
await this.page.waitForResponse("**/api/v1/workflow/actions/**");
}
}
10 changes: 10 additions & 0 deletions e2e/dotcms-e2e-node/frontend/pages/textField.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Page } from "@playwright/test";

export class TextFieldPage {
constructor(private page: Page) {}

async fill(variableName: string, value: string) {
const input = this.page.locator(`input#${variableName}`);
await input.fill(value);
}
}
Loading
Loading