* test(UI): use non-admin user for tests (cherry picked from commitversion-14f07bc3b369
) # Conflicts: # .github/workflows/ui-tests.yml * test: fix tests using admin account (cherry picked from commita6b341bd59
) # Conflicts: # cypress/integration/dashboard_links.js * test: add test role to user (cherry picked from commit0896873c8a
) * chore: conflicts * test: fix broken test Not sure why there's test on doctype "created by". Makes no sense. Co-authored-by: Ankush Menat <ankush@frappe.io>
@@ -137,7 +137,10 @@ jobs: | |||||
- name: Site Setup | - name: Site Setup | ||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }} | if: ${{ steps.check-build.outputs.build == 'strawberry' }} | ||||
run: cd ~/frappe-bench/ && bench --site test_site execute frappe.utils.install.complete_setup_wizard | |||||
run: | | |||||
cd ~/frappe-bench/ | |||||
bench --site test_site execute frappe.utils.install.complete_setup_wizard | |||||
bench --site test_site execute frappe.tests.ui_test_helpers.create_test_user | |||||
- name: UI Tests | - name: UI Tests | ||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }} | if: ${{ steps.check-build.outputs.build == 'strawberry' }} | ||||
@@ -3,6 +3,7 @@ const { defineConfig } = require("cypress"); | |||||
module.exports = defineConfig({ | module.exports = defineConfig({ | ||||
projectId: "92odwv", | projectId: "92odwv", | ||||
adminPassword: "admin", | adminPassword: "admin", | ||||
testUser: "frappe@example.com", | |||||
defaultCommandTimeout: 20000, | defaultCommandTimeout: 20000, | ||||
pageLoadTimeout: 15000, | pageLoadTimeout: 15000, | ||||
video: true, | video: true, | ||||
@@ -177,14 +177,14 @@ context("Control Link", () => { | |||||
cy.intercept("POST", "/api/method/frappe.client.validate_link").as("validate_link"); | cy.intercept("POST", "/api/method/frappe.client.validate_link").as("validate_link"); | ||||
cy.get(".frappe-control[data-fieldname=assigned_by] input").focus().as("input"); | cy.get(".frappe-control[data-fieldname=assigned_by] input").focus().as("input"); | ||||
cy.get("@input").type("Administrator", { delay: 100 }).blur(); | |||||
cy.get("@input").type(cy.config("testUser"), { delay: 100 }).blur(); | |||||
cy.wait("@validate_link"); | cy.wait("@validate_link"); | ||||
cy.get(".frappe-control[data-fieldname=assigned_by_full_name] .control-value").should( | cy.get(".frappe-control[data-fieldname=assigned_by_full_name] .control-value").should( | ||||
"contain", | "contain", | ||||
"Administrator" | |||||
"Frappe" | |||||
); | ); | ||||
cy.window().its("cur_frm.doc.assigned_by").should("eq", "Administrator"); | |||||
cy.window().its("cur_frm.doc.assigned_by").should("eq", cy.config("testUser")); | |||||
// invalid input | // invalid input | ||||
cy.get("@input").clear().type("invalid input", { delay: 100 }).blur(); | cy.get("@input").clear().type("invalid input", { delay: 100 }).blur(); | ||||
@@ -198,10 +198,10 @@ context("Control Link", () => { | |||||
// set valid value again | // set valid value again | ||||
cy.get("@input").clear().focus(); | cy.get("@input").clear().focus(); | ||||
cy.wait("@search_link"); | cy.wait("@search_link"); | ||||
cy.get("@input").type("Administrator", { delay: 100 }).blur(); | |||||
cy.get("@input").type(cy.config("testUser"), { delay: 100 }).blur(); | |||||
cy.wait("@validate_link"); | cy.wait("@validate_link"); | ||||
cy.window().its("cur_frm.doc.assigned_by").should("eq", "Administrator"); | |||||
cy.window().its("cur_frm.doc.assigned_by").should("eq", cy.config("testUser")); | |||||
// clear input | // clear input | ||||
cy.get("@input").clear().blur(); | cy.get("@input").clear().blur(); | ||||
@@ -8,7 +8,7 @@ const child_table_doctype_name = child_table_doctype.name; | |||||
context("Dashboard links", () => { | context("Dashboard links", () => { | ||||
before(() => { | before(() => { | ||||
cy.visit("/login"); | cy.visit("/login"); | ||||
cy.login(); | |||||
cy.login("Administrator"); | |||||
cy.insert_doc("DocType", child_table_doctype, true); | cy.insert_doc("DocType", child_table_doctype, true); | ||||
cy.insert_doc("DocType", child_table_doctype_1, true); | cy.insert_doc("DocType", child_table_doctype_1, true); | ||||
cy.insert_doc("DocType", doctype_with_child_table, true); | cy.insert_doc("DocType", doctype_with_child_table, true); | ||||
@@ -27,8 +27,7 @@ context("Dashboard links", () => { | |||||
cy.visit("/app/contact"); | cy.visit("/app/contact"); | ||||
cy.clear_filters(); | cy.clear_filters(); | ||||
cy.visit("/app/user"); | |||||
cy.get(".list-row-col > .level-item > .ellipsis").eq(0).click({ force: true }); | |||||
cy.visit(`/app/user/${cy.config("testUser")}`); | |||||
//To check if initially the dashboard contains only the "Contact" link and there is no counter | //To check if initially the dashboard contains only the "Contact" link and there is no counter | ||||
cy.get('[data-doctype="Contact"]').should("contain", "Contact"); | cy.get('[data-doctype="Contact"]').should("contain", "Contact"); | ||||
@@ -40,11 +39,10 @@ context("Dashboard links", () => { | |||||
cy.findByRole("button", { name: "Add Contact" }).click(); | cy.findByRole("button", { name: "Add Contact" }).click(); | ||||
cy.get('[data-doctype="Contact"][data-fieldname="first_name"]').type("Admin"); | cy.get('[data-doctype="Contact"][data-fieldname="first_name"]').type("Admin"); | ||||
cy.findByRole("button", { name: "Save" }).click(); | cy.findByRole("button", { name: "Save" }).click(); | ||||
cy.visit("/app/user"); | |||||
cy.get(".list-row-col > .level-item > .ellipsis").eq(0).click({ force: true }); | |||||
cy.visit(`/app/user/${cy.config("testUser")}`); | |||||
//To check if the counter for contact doc is "1" after adding the contact | |||||
cy.get('[data-doctype="Contact"] > .count').should("contain", "1"); | |||||
//To check if the counter for contact doc is "2" after adding additional contact | |||||
cy.get('[data-doctype="Contact"] > .count').should("contain", "2"); | |||||
cy.get('[data-doctype="Contact"]').contains("Contact").click(); | cy.get('[data-doctype="Contact"]').contains("Contact").click(); | ||||
//Deleting the newly created contact | //Deleting the newly created contact | ||||
@@ -62,8 +60,7 @@ context("Dashboard links", () => { | |||||
}); | }); | ||||
it("Report link in dashboard", () => { | it("Report link in dashboard", () => { | ||||
cy.visit("/app/user"); | |||||
cy.visit("/app/user/Administrator"); | |||||
cy.visit(`/app/user/${cy.config("testUser")}`); | |||||
cy.get('[data-doctype="Contact"]').should("contain", "Contact"); | cy.get('[data-doctype="Contact"]').should("contain", "Contact"); | ||||
cy.findByText("Connections"); | cy.findByText("Connections"); | ||||
cy.window() | cy.window() | ||||
@@ -32,7 +32,7 @@ context("Login", () => { | |||||
it("logs in using correct credentials", () => { | it("logs in using correct credentials", () => { | ||||
cy.get("#login_email").type("Administrator"); | cy.get("#login_email").type("Administrator"); | ||||
cy.get("#login_password").type(Cypress.config("adminPassword")); | |||||
cy.get("#login_password").type(Cypress.env("adminPassword")); | |||||
cy.findByRole("button", { name: "Login" }).click(); | cy.findByRole("button", { name: "Login" }).click(); | ||||
cy.location("pathname").should("eq", "/app"); | cy.location("pathname").should("eq", "/app"); | ||||
@@ -56,7 +56,7 @@ context("Login", () => { | |||||
); | ); | ||||
cy.get("#login_email").type("Administrator"); | cy.get("#login_email").type("Administrator"); | ||||
cy.get("#login_password").type(Cypress.config("adminPassword")); | |||||
cy.get("#login_password").type(Cypress.env("adminPassword")); | |||||
cy.findByRole("button", { name: "Login" }).click(); | cy.findByRole("button", { name: "Login" }).click(); | ||||
@@ -38,11 +38,6 @@ context("Sidebar", () => { | |||||
//To check if no filter is available in "Assigned To" dropdown | //To check if no filter is available in "Assigned To" dropdown | ||||
cy.get(".empty-state").should("contain", "No filters found"); | cy.get(".empty-state").should("contain", "No filters found"); | ||||
cy.click_sidebar_button("Created By"); | |||||
//To check if "Created By" dropdown contains filter | |||||
cy.get(".group-by-item > .dropdown-item").should("contain", "Me"); | |||||
//Assigning a doctype to a user | //Assigning a doctype to a user | ||||
cy.visit("/app/doctype/ToDo"); | cy.visit("/app/doctype/ToDo"); | ||||
cy.get(".form-assignments > .flex > .text-muted").click(); | cy.get(".form-assignments > .flex > .text-muted").click(); | ||||
@@ -72,7 +67,7 @@ context("Sidebar", () => { | |||||
cy.get(".condition").should("have.value", "like"); | cy.get(".condition").should("have.value", "like"); | ||||
cy.get(".filter-field > .form-group > .input-with-feedback").should( | cy.get(".filter-field > .form-group > .input-with-feedback").should( | ||||
"have.value", | "have.value", | ||||
"%Administrator%" | |||||
`%${cy.config("testUser")}%` | |||||
); | ); | ||||
cy.click_filter_button(); | cy.click_filter_button(); | ||||
@@ -13,7 +13,9 @@ context("Table MultiSelect", () => { | |||||
cy.fill_field("assign_condition", 'status=="Open"', "Code"); | cy.fill_field("assign_condition", 'status=="Open"', "Code"); | ||||
cy.get('input[data-fieldname="users"]').focus().as("input"); | cy.get('input[data-fieldname="users"]').focus().as("input"); | ||||
cy.get('input[data-fieldname="users"] + ul').should("be.visible"); | cy.get('input[data-fieldname="users"] + ul').should("be.visible"); | ||||
cy.get("@input").type("test{enter}", { delay: 100 }); | |||||
cy.get("@input").type("test@erpnext", { delay: 100 }); | |||||
cy.wait(500); | |||||
cy.get("@input").type("{enter}"); | |||||
cy.get( | cy.get( | ||||
'.frappe-control[data-fieldname="users"] .form-control .tb-selected-value .btn-link-to-form' | '.frappe-control[data-fieldname="users"] .form-control .tb-selected-value .btn-link-to-form' | ||||
).as("selected-value"); | ).as("selected-value"); | ||||
@@ -72,14 +72,14 @@ context("Timeline", () => { | |||||
cy.click_listview_row_item(0); | cy.click_listview_row_item(0); | ||||
//To check if the submission of the documemt is visible in the timeline content | //To check if the submission of the documemt is visible in the timeline content | ||||
cy.get(".timeline-content").should("contain", "Administrator submitted this document"); | |||||
cy.get(".timeline-content").should("contain", "Frappe submitted this document"); | |||||
cy.get('[id="page-Custom Submittable DocType"] .page-actions') | cy.get('[id="page-Custom Submittable DocType"] .page-actions') | ||||
.findByRole("button", { name: "Cancel" }) | .findByRole("button", { name: "Cancel" }) | ||||
.click(); | .click(); | ||||
cy.get_open_dialog().findByRole("button", { name: "Yes" }).click(); | cy.get_open_dialog().findByRole("button", { name: "Yes" }).click(); | ||||
//To check if the cancellation of the documemt is visible in the timeline content | //To check if the cancellation of the documemt is visible in the timeline content | ||||
cy.get(".timeline-content").should("contain", "Administrator cancelled this document"); | |||||
cy.get(".timeline-content").should("contain", "Frappe cancelled this document"); | |||||
//Deleting the document | //Deleting the document | ||||
cy.visit("/app/custom-submittable-doctype"); | cy.visit("/app/custom-submittable-doctype"); | ||||
@@ -1,6 +1,13 @@ | |||||
context("Web Form", () => { | context("Web Form", () => { | ||||
before(() => { | before(() => { | ||||
cy.login(); | |||||
cy.login("Administrator"); | |||||
cy.visit("/app/"); | |||||
return cy | |||||
.window() | |||||
.its("frappe") | |||||
.then((frappe) => { | |||||
return frappe.xcall("frappe.tests.ui_test_helpers.clear_notes"); | |||||
}); | |||||
}); | }); | ||||
it("Create Web Form", () => { | it("Create Web Form", () => { | ||||
@@ -42,7 +49,7 @@ context("Web Form", () => { | |||||
}); | }); | ||||
it("Login Required", () => { | it("Login Required", () => { | ||||
cy.login(); | |||||
cy.login("Administrator"); | |||||
cy.visit("/app/web-form/note"); | cy.visit("/app/web-form/note"); | ||||
cy.findByRole("tab", { name: "Settings" }).click(); | cy.findByRole("tab", { name: "Settings" }).click(); | ||||
@@ -51,7 +58,6 @@ context("Web Form", () => { | |||||
cy.save(); | cy.save(); | ||||
cy.visit("/note"); | cy.visit("/note"); | ||||
cy.url().should("include", "/note/Note%201"); | |||||
cy.call("logout"); | cy.call("logout"); | ||||
@@ -62,7 +68,7 @@ context("Web Form", () => { | |||||
}); | }); | ||||
it("Show List", () => { | it("Show List", () => { | ||||
cy.login(); | |||||
cy.login("Administrator"); | |||||
cy.visit("/app/web-form/note"); | cy.visit("/app/web-form/note"); | ||||
cy.findByRole("tab", { name: "Settings" }).click(); | cy.findByRole("tab", { name: "Settings" }).click(); | ||||
@@ -156,7 +162,7 @@ context("Web Form", () => { | |||||
}); | }); | ||||
it("Read Only", () => { | it("Read Only", () => { | ||||
cy.login(); | |||||
cy.login("Administrator"); | |||||
cy.visit("/note"); | cy.visit("/note"); | ||||
cy.url().should("include", "/note/list"); | cy.url().should("include", "/note/list"); | ||||
@@ -29,7 +29,7 @@ import "cypress-real-events/support"; | |||||
Cypress.Commands.add("login", (email, password) => { | Cypress.Commands.add("login", (email, password) => { | ||||
if (!email) { | if (!email) { | ||||
email = "Administrator"; | |||||
email = Cypress.config("testUser") || "Administrator"; | |||||
} | } | ||||
if (!password) { | if (!password) { | ||||
password = Cypress.env("adminPassword"); | password = Cypress.env("adminPassword"); | ||||
@@ -2,6 +2,8 @@ import frappe | |||||
from frappe import _ | from frappe import _ | ||||
from frappe.utils import add_to_date, now | from frappe.utils import add_to_date, now | ||||
UI_TEST_USER = "frappe@example.com" | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def create_if_not_exists(doc): | def create_if_not_exists(doc): | ||||
@@ -54,6 +56,15 @@ def create_todo_records(): | |||||
).insert() | ).insert() | ||||
@frappe.whitelist() | |||||
def clear_notes(): | |||||
if not frappe.local.dev_server: | |||||
frappe.throw(_("Not allowed"), frappe.PermissionError) | |||||
for note in frappe.get_all("Note", pluck="name"): | |||||
frappe.delete_doc("Note", note, force=True) | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def create_communication_record(): | def create_communication_record(): | ||||
doc = frappe.get_doc( | doc = frappe.get_doc( | ||||
@@ -396,3 +407,27 @@ def create_blog_post(): | |||||
).insert(ignore_if_duplicate=True) | ).insert(ignore_if_duplicate=True) | ||||
return doc | return doc | ||||
def create_test_user(): | |||||
if frappe.db.exists("User", UI_TEST_USER): | |||||
return | |||||
user = frappe.new_doc("User") | |||||
user.email = UI_TEST_USER | |||||
user.first_name = "Frappe" | |||||
user.new_password = frappe.local.conf.admin_password | |||||
user.send_welcome_email = 0 | |||||
user.time_zone = "Asia/Kolkata" | |||||
user.flags.ignore_password_policy = True | |||||
user.insert(ignore_if_duplicate=True) | |||||
user.reload() | |||||
blocked_roles = {"Administrator", "Guest", "All"} | |||||
all_roles = set(frappe.get_all("Role", pluck="name")) | |||||
for role in all_roles - blocked_roles: | |||||
user.append("roles", {"role": role}) | |||||
user.save() |
@@ -2,7 +2,6 @@ | |||||
# License: MIT. See LICENSE | # License: MIT. See LICENSE | ||||
import frappe | import frappe | ||||
from frappe.model.workflow import ( | from frappe.model.workflow import ( | ||||
WorkflowPermissionError, | |||||
WorkflowTransitionError, | WorkflowTransitionError, | ||||
apply_workflow, | apply_workflow, | ||||
get_common_transition_actions, | get_common_transition_actions, | ||||
@@ -191,11 +190,18 @@ class TestWorkflow(FrappeTestCase): | |||||
def create_todo_workflow(): | def create_todo_workflow(): | ||||
from frappe.tests.ui_test_helpers import UI_TEST_USER | |||||
if frappe.db.exists("Workflow", "Test ToDo"): | if frappe.db.exists("Workflow", "Test ToDo"): | ||||
frappe.delete_doc("Workflow", "Test ToDo") | frappe.delete_doc("Workflow", "Test ToDo") | ||||
if not frappe.db.exists("Role", "Test Approver"): | |||||
frappe.get_doc(dict(doctype="Role", role_name="Test Approver")).insert(ignore_if_duplicate=True) | |||||
TEST_ROLE = "Test Approver" | |||||
if not frappe.db.exists("Role", TEST_ROLE): | |||||
frappe.get_doc(dict(doctype="Role", role_name=TEST_ROLE)).insert(ignore_if_duplicate=True) | |||||
if frappe.db.exists("User", UI_TEST_USER): | |||||
frappe.get_doc("User", UI_TEST_USER).add_roles(TEST_ROLE) | |||||
workflow = frappe.new_doc("Workflow") | workflow = frappe.new_doc("Workflow") | ||||
workflow.workflow_name = "Test ToDo" | workflow.workflow_name = "Test ToDo" | ||||
workflow.document_type = "ToDo" | workflow.document_type = "ToDo" | ||||
@@ -205,16 +211,16 @@ def create_todo_workflow(): | |||||
workflow.append("states", dict(state="Pending", allow_edit="All")) | workflow.append("states", dict(state="Pending", allow_edit="All")) | ||||
workflow.append( | workflow.append( | ||||
"states", | "states", | ||||
dict(state="Approved", allow_edit="Test Approver", update_field="status", update_value="Closed"), | |||||
dict(state="Approved", allow_edit=TEST_ROLE, update_field="status", update_value="Closed"), | |||||
) | ) | ||||
workflow.append("states", dict(state="Rejected", allow_edit="Test Approver")) | |||||
workflow.append("states", dict(state="Rejected", allow_edit=TEST_ROLE)) | |||||
workflow.append( | workflow.append( | ||||
"transitions", | "transitions", | ||||
dict( | dict( | ||||
state="Pending", | state="Pending", | ||||
action="Approve", | action="Approve", | ||||
next_state="Approved", | next_state="Approved", | ||||
allowed="Test Approver", | |||||
allowed=TEST_ROLE, | |||||
allow_self_approval=1, | allow_self_approval=1, | ||||
), | ), | ||||
) | ) | ||||
@@ -224,7 +230,7 @@ def create_todo_workflow(): | |||||
state="Pending", | state="Pending", | ||||
action="Reject", | action="Reject", | ||||
next_state="Rejected", | next_state="Rejected", | ||||
allowed="Test Approver", | |||||
allowed=TEST_ROLE, | |||||
allow_self_approval=1, | allow_self_approval=1, | ||||
), | ), | ||||
) | ) | ||||