Bläddra i källkod

Merge pull request #18046 from frappe/version-14-hotfix

chore: weekly version-14 release
version-14
Deepesh Garg 2 år sedan
committed by GitHub
förälder
incheckning
182a94e828
Ingen känd nyckel hittad för denna signaturen i databasen GPG-nyckel ID: 4AEE18F83AFDEB23
41 ändrade filer med 413 tillägg och 286 borttagningar
  1. +34
    -7
      .github/helper/roulette.py
  2. +1
    -6
      .github/workflows/server-mariadb-tests.yml
  3. +1
    -6
      .github/workflows/server-postgres-tests.yml
  4. +5
    -8
      .github/workflows/ui-tests.yml
  5. +23
    -0
      cypress.config.js
  6. +0
    -15
      cypress.json
  7. +1
    -0
      cypress/integration/control_float.js
  8. +5
    -9
      cypress/integration/form.js
  9. +0
    -13
      cypress/integration/kanban.js
  10. +6
    -3
      cypress/integration/list_view.js
  11. +2
    -0
      cypress/integration/list_view_settings.js
  12. +0
    -93
      cypress/integration/timeline_email.js
  13. +2
    -1
      cypress/integration/workspace_blocks.js
  14. +26
    -3
      cypress/support/commands.js
  15. +0
    -0
      cypress/support/e2e.js
  16. +4
    -4
      esbuild/utils.js
  17. +1
    -1
      frappe/app.py
  18. +7
    -2
      frappe/auth.py
  19. +1
    -1
      frappe/commands/utils.py
  20. +6
    -5
      frappe/core/doctype/comment/comment.py
  21. +1
    -0
      frappe/core/doctype/log_settings/log_settings.py
  22. +9
    -1
      frappe/core/doctype/system_settings/system_settings.json
  23. +16
    -0
      frappe/core/doctype/system_settings/system_settings.py
  24. +96
    -44
      frappe/core/workspace/build/build.json
  25. +26
    -47
      frappe/core/workspace/settings/settings.json
  26. +14
    -3
      frappe/custom/workspace/customization/customization.json
  27. +1
    -0
      frappe/desk/doctype/workspace/workspace.py
  28. +1
    -0
      frappe/patches.txt
  29. +9
    -0
      frappe/patches/v14_0/event_streaming_deprecation_warning.py
  30. +2
    -2
      frappe/patches/v14_0/update_multistep_webforms.py
  31. +4
    -0
      frappe/public/js/frappe/views/treeview.js
  32. +1
    -0
      frappe/public/js/frappe/views/workspace/workspace.js
  33. +2
    -2
      frappe/public/scss/desk/report.scss
  34. +9
    -0
      frappe/tests/test_auth.py
  35. +64
    -0
      frappe/tests/test_perf.py
  36. +1
    -2
      frappe/tests/ui_test_helpers.py
  37. +18
    -0
      frappe/tests/utils.py
  38. +2
    -4
      frappe/utils/install.py
  39. +1
    -0
      frappe/utils/safe_exec.py
  40. +8
    -3
      frappe/www/login.html
  41. +3
    -1
      frappe/www/login.py

+ 34
- 7
.github/helper/roulette.py Visa fil

@@ -17,39 +17,57 @@ def fetch_pr_data(pr_number, repo, endpoint=""):

req = urllib.request.Request(api_url)
res = urllib.request.urlopen(req)
return json.loads(res.read().decode('utf8'))
return json.loads(res.read().decode("utf8"))


def get_files_list(pr_number, repo="frappe/frappe"):
return [change["filename"] for change in fetch_pr_data(pr_number, repo, "files")]


def get_output(command, shell=True):
print(command)
command = shlex.split(command)
return subprocess.check_output(command, shell=shell, encoding="utf8").strip()


def has_skip_ci_label(pr_number, repo="frappe/frappe"):
return has_label(pr_number, "Skip CI", repo)


def has_run_server_tests_label(pr_number, repo="frappe/frappe"):
return has_label(pr_number, "Run Server Tests", repo)


def has_run_ui_tests_label(pr_number, repo="frappe/frappe"):
return has_label(pr_number, "Run UI Tests", repo)


def has_label(pr_number, label, repo="frappe/frappe"):
return any([fetched_label["name"] for fetched_label in fetch_pr_data(pr_number, repo)["labels"] if fetched_label["name"] == label])
return any(
[
fetched_label["name"]
for fetched_label in fetch_pr_data(pr_number, repo)["labels"]
if fetched_label["name"] == label
]
)


def is_py(file):
return file.endswith("py")


def is_ci(file):
return ".github" in file


def is_frontend_code(file):
return file.lower().endswith((".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue", ".html"))
return file.lower().endswith(
(".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue", ".html")
)


def is_docs(file):
regex = re.compile(r'\.(md|png|jpg|jpeg|csv|svg)$|^.github|LICENSE')
regex = re.compile(r"\.(md|png|jpg|jpeg|csv|svg)$|^.github|LICENSE")
return bool(regex.search(file))


@@ -78,8 +96,13 @@ if __name__ == "__main__":
only_py_changed = updated_py_file_count == len(files_list)

if has_skip_ci_label(pr_number, repo):
print("Found `Skip CI` label on pr, stopping build process.")
sys.exit(0)
if build_type == "ui" and has_run_ui_tests_label(pr_number, repo):
print("Running UI tests only.")
elif build_type == "server" and has_run_server_tests_label(pr_number, repo):
print("Running server tests only.")
else:
print("Found `Skip CI` label on pr, stopping build process.")
sys.exit(0)

elif ci_files_changed:
print("CI related files were updated, running all build processes.")
@@ -88,7 +111,11 @@ if __name__ == "__main__":
print("Only docs were updated, stopping build process.")
sys.exit(0)

elif only_frontend_code_changed and build_type == "server" and not has_run_server_tests_label(pr_number, repo):
elif (
only_frontend_code_changed
and build_type == "server"
and not has_run_server_tests_label(pr_number, repo)
):
print("Only Frontend code was updated; Stopping Python build process.")
sys.exit(0)



+ 1
- 6
.github/workflows/server-mariadb-tests.yml Visa fil

@@ -22,8 +22,6 @@ jobs:

strategy:
fail-fast: false
matrix:
container: [1, 2]

services:
mariadb:
@@ -122,7 +120,4 @@ jobs:

- name: Run Tests
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator
env:
CI_BUILD_ID: ${{ github.run_id }}
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests

+ 1
- 6
.github/workflows/server-postgres-tests.yml Visa fil

@@ -21,8 +21,6 @@ jobs:

strategy:
fail-fast: false
matrix:
container: [1, 2]

services:
postgres:
@@ -125,7 +123,4 @@ jobs:

- name: Run Tests
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator
env:
CI_BUILD_ID: ${{ github.run_id }}
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests

+ 5
- 8
.github/workflows/ui-tests.yml Visa fil

@@ -21,7 +21,7 @@ jobs:
strategy:
fail-fast: false
matrix:
containers: [1, 2, 3]
containers: [1, 2]

name: UI Tests (Cypress)

@@ -105,19 +105,16 @@ jobs:
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
key: ${{ runner.os }}-yarn-ui-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
${{ runner.os }}-yarn-ui-

- name: Cache cypress binary
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/cache@v3
with:
path: ~/.cache
key: ${{ runner.os }}-cypress-
restore-keys: |
${{ runner.os }}-cypress-
${{ runner.os }}-
path: ~/.cache/Cypress
key: ${{ runner.os }}-cypress

- name: Install Dependencies
if: ${{ steps.check-build.outputs.build == 'strawberry' }}


+ 23
- 0
cypress.config.js Visa fil

@@ -0,0 +1,23 @@
const { defineConfig } = require("cypress");

module.exports = defineConfig({
projectId: "92odwv",
adminPassword: "admin",
defaultCommandTimeout: 20000,
pageLoadTimeout: 15000,
video: true,
videoUploadOnPasses: false,
retries: {
runMode: 2,
openMode: 2,
},
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require("./cypress/plugins/index.js")(on, config);
},
baseUrl: "http://test_site_ui:8000",
specPattern: ["./cypress/integration/*.js", "**/ui_test_*.js"],
},
});

+ 0
- 15
cypress.json Visa fil

@@ -1,15 +0,0 @@
{
"baseUrl": "http://test_site_ui:8000",
"projectId": "92odwv",
"adminPassword": "admin",
"defaultCommandTimeout": 20000,
"pageLoadTimeout": 15000,
"video": true,
"videoUploadOnPasses": false,
"retries": {
"runMode": 2,
"openMode": 2
},
"integrationFolder": ".",
"testFiles": ["cypress/integration/*.js", "**/ui_test_*.js"]
}

+ 1
- 0
cypress/integration/control_float.js Visa fil

@@ -29,6 +29,7 @@ context("Control Float", () => {
});
x.values.forEach((d) => {
cy.get_field("float_number", "Float").clear();
cy.wait(200);
cy.fill_field("float_number", d.input, "Float").blur();
cy.get_field("float_number", "Float").should("have.value", d.blur_expected);



+ 5
- 9
cypress/integration/form.js Visa fil

@@ -132,9 +132,6 @@ context("Form", () => {
jump_to_field("Username");
type_value("admin42");

jump_to_field("Birth Date");
type_value("12-31-01");

jump_to_field("Send Welcome Email");
cy.focused().uncheck();

@@ -155,16 +152,15 @@ context("Form", () => {
undo();
undo();
undo();
undo();
redo();
redo();
redo();
redo();
redo();

cy.get_field("username").should("have.value", "admin24");
cy.get_field("email").should("have.value", "admin@example.com");
cy.get_field("birth_date").should("have.value", "12-31-2001"); // parsed value
cy.get_field("send_welcome_email").should("not.be.checked");
cy.compare_document({
username: "admin24",
email: "admin@example.com",
send_welcome_email: 0,
});
});
});

+ 0
- 13
cypress/integration/kanban.js Visa fil

@@ -96,17 +96,4 @@ context("Kanban Board", () => {
.first()
.should("not.contain", "ID:");
});

// it('Drag todo', () => {
// cy.intercept({
// method: 'POST',
// url: 'api/method/frappe.desk.doctype.kanban_board.kanban_board.update_order_for_single_card'
// }).as('drag-completed');

// cy.get('.kanban-card-body')
// .contains('Test Kanban ToDo').first()
// .drag('[data-column-value="Closed"] .kanban-cards', { force: true });

// cy.wait('@drag-completed');
// });
});

+ 6
- 3
cypress/integration/list_view.js Visa fil

@@ -10,10 +10,13 @@ context("List View", () => {
});
});

it("Keep checkbox checked after Refresh", () => {
it("Keep checkbox checked after Refresh", { scrollBehavior: false }, () => {
cy.go_to_list("ToDo");
cy.clear_filters();
cy.get(".list-row-container .list-row-checkbox").click({ multiple: true, force: true });
cy.get(".list-row-container .list-row-checkbox").click({
multiple: true,
force: true,
});
cy.get(".actions-btn-group button").contains("Actions").should("be.visible");
cy.intercept("/api/method/frappe.desk.reportview.get").as("list-refresh");
cy.wait(3000); // wait before you hit another refresh
@@ -22,7 +25,7 @@ context("List View", () => {
cy.get(".list-row-container .list-row-checkbox:checked").should("be.visible");
});

it('enables "Actions" button', () => {
it('enables "Actions" button', { scrollBehavior: false }, () => {
const actions = [
"Approve",
"Reject",


+ 2
- 0
cypress/integration/list_view_settings.js Visa fil

@@ -5,12 +5,14 @@ context("List View Settings", () => {
});
it("Default settings", () => {
cy.visit("/app/List/DocType/List");
cy.clear_filters();
cy.get(".list-count").should("contain", "20 of");
cy.get(".list-stats").should("contain", "Tags");
});
it("disable count and sidebar stats then verify", () => {
cy.wait(300);
cy.visit("/app/List/DocType/List");
cy.clear_filters();
cy.wait(300);
cy.get(".list-count").should("contain", "20 of");
cy.get(".menu-btn-group button").click();


+ 0
- 93
cypress/integration/timeline_email.js Visa fil

@@ -1,93 +0,0 @@
context("Timeline Email", () => {
before(() => {
cy.visit("/login");
cy.login();
cy.visit("/app/todo");
});

it("Adding new ToDo", () => {
cy.click_listview_primary_button("Add ToDo");
cy.get(".custom-actions:visible > .btn").contains("Edit Full Form").click({ delay: 500 });
cy.fill_field("description", "Test ToDo", "Text Editor");
cy.wait(500);
cy.get(".primary-action").contains("Save").click({ force: true });
cy.wait(700);
});

it("Adding email and verifying timeline content for email attachment", () => {
cy.visit("/app/todo");
cy.click_listview_row_item_with_text("Test ToDo");

//Creating a new email
cy.get(".timeline-actions > .timeline-item > .action-buttons > .action-btn").click();
cy.fill_field("recipients", "test@example.com", "MultiSelect");
cy.get(
'.modal.show > .modal-dialog > .modal-content > .modal-body > :nth-child(1) > .form-layout > .form-page > :nth-child(3) > .section-body > .form-column > form > [data-fieldtype="Text Editor"] > .form-group > .control-input-wrapper > .control-input > .ql-container > .ql-editor'
).type("Test Mail");

//Adding attachment to the email
cy.get(".add-more-attachments > .btn").click();
cy.get(".mt-2 > .btn > .mt-1").eq(2).click();
cy.get(".input-group > .form-control").type(
"https://wallpaperplay.com/walls/full/8/2/b/72402.jpg"
);
cy.get(".btn-primary").contains("Upload").click();

//Sending the email
cy.click_modal_primary_button("Send", { delay: 500 });

//To check if the sent mail content is shown in the timeline content
cy.get('[data-doctype="Communication"] > .timeline-content').should(
"contain",
"Test Mail"
);

//To check if the attachment of email is shown in the timeline content
cy.get(".timeline-content").should("contain", "Added 72402.jpg");

//Deleting the sent email
cy.get('[title="Open Communication"] > .icon').first().click({ force: true });
cy.get(
"#page-Communication > .page-head > .container > .row > .col > .standard-actions > .menu-btn-group > .btn"
).click();
cy.get(
"#page-Communication > .page-head > .container > .row > .col > .standard-actions > .menu-btn-group > .dropdown-menu > li > .grey-link"
)
.eq(9)
.click();
cy.get(
".modal.show > .modal-dialog > .modal-content > .modal-footer > .standard-actions > .btn-primary"
).click();
});

it("Deleting attachment and ToDo", () => {
cy.visit("/app/todo");
cy.click_listview_row_item_with_text("Test ToDo");

//Removing the added attachment
cy.get(".attachment-row > .data-pill > .remove-btn > .icon").click();
cy.wait(500);
cy.get(".modal-footer:visible > .standard-actions > .btn-primary").contains("Yes").click();

//To check if the removed attachment is shown in the timeline content
cy.get(".timeline-content").should("contain", "Removed 72402.jpg");
cy.wait(500);

//To check if the discard button functionality in email is working correctly
cy.get(".timeline-actions > .timeline-item > .action-buttons > .action-btn").click();
cy.fill_field("recipients", "test@example.com", "MultiSelect");
cy.get(".modal-footer > .standard-actions > .btn-secondary").contains("Discard").click();
cy.wait(500);
cy.get(".timeline-actions > .timeline-item > .action-buttons > .action-btn").click();
cy.wait(500);
cy.get_field("recipients", "MultiSelect").should("have.text", "");
cy.get(".modal-header:visible > .modal-actions > .btn-modal-close > .icon").click();

//Deleting the added ToDo
cy.get(".menu-btn-group:visible > .btn").click();
cy.get(".menu-btn-group:visible > .dropdown-menu > li > .dropdown-item")
.contains("Delete")
.click();
cy.get(".modal-footer:visible > .standard-actions > .btn-primary").click();
});
});

+ 2
- 1
cypress/integration/workspace_blocks.js Visa fil

@@ -68,9 +68,10 @@ context("Workspace Blocks", () => {

cy.intercept({
method: "GET",
url: "api/method/frappe.desk.form.load.getdoctype",
url: "api/method/frappe.desk.form.load.getdoctype?**",
}).as("get_doctype");

cy.visit("/app/tools");
cy.get(".codex-editor__redactor .ce-block");
cy.get(".standard-actions .btn-secondary[data-label=Edit]").click();



+ 26
- 3
cypress/support/commands.js Visa fil

@@ -281,12 +281,12 @@ Cypress.Commands.add("get_open_dialog", () => {
});

Cypress.Commands.add("save", () => {
cy.intercept("/api").as("api");
cy.intercept("/api/method/frappe.desk.form.save.savedocs").as("save_call");
cy.get(`button[data-label="Save"]:visible`).click({ scrollBehavior: false, force: true });
cy.wait("@api");
cy.wait("@save_call");
});
Cypress.Commands.add("hide_dialog", () => {
cy.wait(300);
cy.wait(500);
cy.get_open_dialog().focus().find(".btn-modal-close").click();
cy.get(".modal:visible").should("not.exist");
});
@@ -459,3 +459,26 @@ Cypress.Commands.add("select_listview_row_checkbox", (row_no) => {
Cypress.Commands.add("click_form_section", (section_name) => {
cy.get(".section-head").contains(section_name).click();
});

const compare_document = (expected, actual) => {
for (const prop in expected) {
if (expected[prop] instanceof Array) {
// recursively compare child documents.
expected[prop].forEach((item, idx) => {
compare_document(item, actual[prop][idx]);
});
} else {
assert.equal(expected[prop], actual[prop], `${prop} should be equal.`);
}
}
};

Cypress.Commands.add("compare_document", (expected_document) => {
cy.window()
.its("cur_frm")
.then((frm) => {
// Don't remove this, cypress can't magically wait for events it has no control over.
cy.wait(1000);
compare_document(expected_document, frm.doc);
});
});

cypress/support/index.js → cypress/support/e2e.js Visa fil


+ 4
- 4
esbuild/utils.js Visa fil

@@ -111,15 +111,15 @@ function get_redis_subscriber(kind) {
let retry_strategy;
let { get_redis_subscriber: get_redis, get_conf } = require("../node_utils");

if (process.env.CI == 1 || get_conf().developer_mode == 1) {
if (process.env.CI == 1 || get_conf().developer_mode == 0) {
retry_strategy = () => {};
} else {
retry_strategy = function (options) {
// abort after 10 connection attempts
if (options.attempt > 10) {
// abort after 5 x 3 connection attempts ~= 3 seconds
if (options.attempt > 4) {
return undefined;
}
return Math.min(options.attempt * 100, 2000);
return options.attempt * 100;
};
}
return get_redis(kind, { retry_strategy });


+ 1
- 1
frappe/app.py Visa fil

@@ -46,7 +46,7 @@ class RequestContext:

@local_manager.middleware
@Request.application
def application(request):
def application(request: Request):
response = None

try:


+ 7
- 2
frappe/auth.py Visa fil

@@ -140,6 +140,9 @@ class LoginManager:
self.set_user_info()

def login(self):
if frappe.get_system_settings("disable_user_pass_login"):
frappe.throw(_("Login with username and password is not allowed."), frappe.AuthenticationError)

# clear cache
frappe.clear_cache(user=frappe.form_dict.get("usr"))
user, pwd = get_cached_user_pass()
@@ -226,14 +229,16 @@ class LoginManager:

def clear_active_sessions(self):
"""Clear other sessions of the current user if `deny_multiple_sessions` is not set"""
if frappe.session.user == "Guest":
return

if not (
cint(frappe.conf.get("deny_multiple_sessions"))
or cint(frappe.db.get_system_setting("deny_multiple_sessions"))
):
return

if frappe.session.user != "Guest":
clear_sessions(frappe.session.user, keep_current=True)
clear_sessions(frappe.session.user, keep_current=True)

def authenticate(self, user: str = None, pwd: str = None):
from frappe.core.doctype.user.user import User


+ 1
- 1
frappe/commands/utils.py Visa fil

@@ -879,7 +879,7 @@ def run_ui_tests(
click.secho("Installing Cypress...", fg="yellow")
packages = " ".join(
[
"cypress@^6",
"cypress@^10",
"cypress-file-upload@^5",
"@4tw/cypress-drag-drop@^2",
"cypress-real-events",


+ 6
- 5
frappe/core/doctype/comment/comment.py Visa fil

@@ -161,12 +161,13 @@ def update_comments_in_parent(reference_doctype, reference_name, _comments):

else:
raise ImplicitCommitError

else:
if not frappe.flags.in_patch:
reference_doc = frappe.get_doc(reference_doctype, reference_name)
if getattr(reference_doc, "route", None):
clear_cache(reference_doc.route)
if frappe.flags.in_patch:
return

# Clear route cache
if route := frappe.get_cached_value(reference_doctype, reference_name, "route"):
clear_cache(route)


def update_comments_in_parent_after_request():


+ 1
- 0
frappe/core/doctype/log_settings/log_settings.py Visa fil

@@ -16,6 +16,7 @@ DEFAULT_LOGTYPES_RETENTION = {
"Email Queue": 30,
"Error Snapshot": 30,
"Scheduled Job Log": 90,
"Route History": 90,
}




+ 9
- 1
frappe/core/doctype/system_settings/system_settings.json Visa fil

@@ -39,6 +39,7 @@
"deny_multiple_sessions",
"allow_login_using_mobile_number",
"allow_login_using_user_name",
"disable_user_pass_login",
"allow_error_traceback",
"strip_exif_metadata_from_uploaded_images",
"allow_older_web_view_links",
@@ -525,12 +526,19 @@
"fieldname": "email_retry_limit",
"fieldtype": "Int",
"label": "Email Retry Limit"
},
{
"default": "0",
"description": "Make sure to configure a Social Login Key before disabling to prevent lockout",
"fieldname": "disable_user_pass_login",
"fieldtype": "Check",
"label": "Disable Username/Password Login"
}
],
"icon": "fa fa-cog",
"issingle": 1,
"links": [],
"modified": "2022-06-21 13:55:04.796152",
"modified": "2022-09-06 03:16:59.090906",
"modified_by": "Administrator",
"module": "Core",
"name": "System Settings",


+ 16
- 0
frappe/core/doctype/system_settings/system_settings.py Visa fil

@@ -43,6 +43,22 @@ class SystemSettings(Document):
):
frappe.flags.update_last_reset_password_date = True

self.validate_user_pass_login()

def validate_user_pass_login(self):
if not self.disable_user_pass_login:
return

social_login_enabled = frappe.db.exists("Social Login Key", {"enable_social_login": 1})
ldap_enabled = frappe.db.get_single_value("LDAP Settings", "enabled")

if not (social_login_enabled or ldap_enabled):
frappe.throw(
_(
"Please enable atleast one Social Login Key or LDAP before disabling username/password based login."
)
)

def on_update(self):
self.set_defaults()



+ 96
- 44
frappe/core/workspace/build/build.json Visa fil

@@ -1,6 +1,6 @@
{
"charts": [],
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"DocType\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Workspace\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Report\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Elements</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Modules\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Models\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Views\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Scripting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Packages\",\"col\":4}}]",
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"DocType\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Workspace\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Report\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Elements</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Modules\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Models\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Views\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Scripting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Packages\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"System Logs\",\"col\":4}}]",
"creation": "2021-01-02 10:51:16.579957",
"docstatus": 0,
"doctype": "Workspace",
@@ -13,7 +13,7 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Modules",
"label": "Models",
"link_count": 0,
"link_type": "DocType",
"onboard": 0,
@@ -23,9 +23,9 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Module Def",
"label": "DocType",
"link_count": 0,
"link_to": "Module Def",
"link_to": "DocType",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
@@ -34,9 +34,9 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Workspace",
"label": "Workflow",
"link_count": 0,
"link_to": "Workspace",
"link_to": "Workflow",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
@@ -45,9 +45,19 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Module Onboarding",
"label": "Scripting",
"link_count": 0,
"link_to": "Module Onboarding",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
"type": "Card Break"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Server Script",
"link_count": 0,
"link_to": "Server Script",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
@@ -56,9 +66,9 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Block Module",
"label": "Client Script",
"link_count": 0,
"link_to": "Block Module",
"link_to": "Client Script",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
@@ -67,30 +77,56 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Models",
"label": "Scheduled Job Type",
"link_count": 0,
"link_to": "Scheduled Job Type",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Packages",
"link_count": 2,
"onboard": 0,
"type": "Card Break"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "DocType",
"label": "Package",
"link_count": 0,
"link_to": "DocType",
"link_to": "Package",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Workflow",
"label": "Package Import",
"link_count": 0,
"link_to": "Workflow",
"link_to": "Package Import",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Modules",
"link_count": 3,
"onboard": 0,
"type": "Card Break"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Module Def",
"link_count": 0,
"link_to": "Module Def",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
@@ -99,11 +135,30 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Views",
"label": "Module Onboarding",
"link_count": 0,
"link_to": "Module Onboarding",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Module Profile",
"link_count": 0,
"link_to": "Module Profile",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Views",
"link_count": 4,
"onboard": 0,
"type": "Card Break"
},
{
@@ -131,9 +186,9 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Workspace",
"label": "Dashboard",
"link_count": 0,
"link_to": "Workspace",
"link_to": "Dashboard",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
@@ -142,71 +197,67 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Dashboard",
"label": "Workspace",
"link_count": 0,
"link_to": "Dashboard",
"link_to": "Workspace",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Scripting",
"link_count": 0,
"link_type": "DocType",
"label": "System Logs",
"link_count": 6,
"onboard": 0,
"only_for": "",
"type": "Card Break"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Server Script",
"label": "Background Jobs",
"link_count": 0,
"link_to": "Server Script",
"link_type": "DocType",
"link_to": "background_jobs",
"link_type": "Page",
"onboard": 0,
"only_for": "",
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Client Script",
"label": "Scheduled Jobs Logs",
"link_count": 0,
"link_to": "Client Script",
"link_to": "Scheduled Job Log",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Scheduled Job Type",
"label": "Error Logs",
"link_count": 0,
"link_to": "Scheduled Job Type",
"link_to": "Error Log",
"link_type": "DocType",
"onboard": 0,
"only_for": "",
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Packages",
"link_count": 2,
"label": "Error Snapshot",
"link_count": 0,
"link_to": "Error Snapshot",
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Package",
"label": "Communication Logs",
"link_count": 0,
"link_to": "Package",
"link_to": "Communication",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -214,21 +265,22 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Package Import",
"label": "Activity Log",
"link_count": 0,
"link_to": "Package Import",
"link_to": "Activity Log",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
"modified": "2022-01-13 17:26:02.736366",
"modified": "2022-09-02 01:48:28.029135",
"modified_by": "Administrator",
"module": "Core",
"name": "Build",
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 5.0,


+ 26
- 47
frappe/core/workspace/settings/settings.json Visa fil

@@ -1,6 +1,6 @@
{
"charts": [],
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Settings</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"System Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Print Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Website Settings\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Data\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Email / Notifications\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Website\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Core\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Printing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Workflow\",\"col\":4}}]",
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Settings</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"System Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Print Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Website Settings\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Data\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Email / Notifications\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Website\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Core\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Printing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Workflow\",\"col\":4}}]",
"creation": "2020-03-02 15:09:40.527211",
"docstatus": 0,
"doctype": "Workspace",
@@ -224,7 +224,7 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Core",
"label": "Printing",
"link_count": 0,
"onboard": 0,
"type": "Card Break"
@@ -233,10 +233,10 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "System Settings",
"label": "Print Format Builder",
"link_count": 0,
"link_to": "System Settings",
"link_type": "DocType",
"link_to": "print-format-builder",
"link_type": "Page",
"onboard": 0,
"type": "Link"
},
@@ -244,9 +244,9 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Error Log",
"label": "Print Settings",
"link_count": 0,
"link_to": "Error Log",
"link_to": "Print Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -255,9 +255,9 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Error Snapshot",
"label": "Print Format",
"link_count": 0,
"link_to": "Error Snapshot",
"link_to": "Print Format",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -266,9 +266,9 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Domain Settings",
"label": "Print Style",
"link_count": 0,
"link_to": "Domain Settings",
"link_to": "Print Style",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -276,7 +276,7 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Printing",
"label": "Workflow",
"link_count": 0,
"onboard": 0,
"type": "Card Break"
@@ -285,20 +285,9 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Print Format Builder",
"link_count": 0,
"link_to": "print-format-builder",
"link_type": "Page",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Print Settings",
"label": "Workflow",
"link_count": 0,
"link_to": "Print Settings",
"link_to": "Workflow",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -307,9 +296,9 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Print Format",
"label": "Workflow State",
"link_count": 0,
"link_to": "Print Format",
"link_to": "Workflow State",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -318,9 +307,9 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Print Style",
"label": "Workflow Action",
"link_count": 0,
"link_to": "Print Style",
"link_to": "Workflow Action",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -328,8 +317,8 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Workflow",
"link_count": 0,
"label": "Core",
"link_count": 2,
"onboard": 0,
"type": "Card Break"
},
@@ -337,20 +326,9 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Workflow",
"link_count": 0,
"link_to": "Workflow",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Workflow State",
"label": "System Settings",
"link_count": 0,
"link_to": "Workflow State",
"link_to": "System Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -359,21 +337,22 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Workflow Action",
"label": "Domain Settings",
"link_count": 0,
"link_to": "Workflow Action",
"link_to": "Domain Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
"modified": "2022-01-13 17:49:59.586909",
"modified": "2022-08-28 21:41:28.065190",
"modified_by": "Administrator",
"module": "Core",
"name": "Settings",
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 29.0,


+ 14
- 3
frappe/custom/workspace/customization/customization.json Visa fil

@@ -1,6 +1,6 @@
{
"charts": [],
"content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Customization\",\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customize Form\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Custom Role\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Client Script\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Server Script\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Dashboards\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Form Customization\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other\",\"col\":4}}]",
"content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Customization\",\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customize Form\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Custom Role\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Client Script\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Server Script\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Dashboards\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Form Customization\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other\",\"col\":4}}]",
"creation": "2020-03-02 15:15:03.839594",
"docstatus": 0,
"doctype": "Workspace",
@@ -107,7 +107,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Other",
"link_count": 0,
"link_count": 2,
"onboard": 0,
"type": "Card Break"
},
@@ -121,15 +121,26 @@
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Navbar Settings",
"link_count": 0,
"link_to": "Navbar Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
"modified": "2022-01-13 17:28:08.345794",
"modified": "2022-08-28 20:56:24.980719",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customization",
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 8.0,


+ 1
- 0
frappe/desk/doctype/workspace/workspace.py Visa fil

@@ -138,6 +138,7 @@ class Workspace(Document):
def disable_saving_as_public():
return (
frappe.flags.in_install
or frappe.flags.in_uninstall
or frappe.flags.in_patch
or frappe.flags.in_test
or frappe.flags.in_fixtures


+ 1
- 0
frappe/patches.txt Visa fil

@@ -196,6 +196,7 @@ frappe.patches.v14_0.log_settings_migration
frappe.patches.v14_0.setup_likes_from_feedback
frappe.patches.v14_0.update_webforms
frappe.patches.v14_0.delete_payment_gateways
frappe.patches.v14_0.event_streaming_deprecation_warning

[post_model_sync]
frappe.patches.v14_0.drop_data_import_legacy


+ 9
- 0
frappe/patches/v14_0/event_streaming_deprecation_warning.py Visa fil

@@ -0,0 +1,9 @@
import click


def execute():
click.secho(
"Event Streaming is moved to a separate app in version 15.\n"
"When upgrading to Frappe version-15, Please install the 'Event Streaming' app to continue using them: https://github.com/frappe/event_streaming",
fg="yellow",
)

+ 2
- 2
frappe/patches/v14_0/update_multistep_webforms.py Visa fil

@@ -2,11 +2,11 @@ import frappe


def execute():
frappe.reload_doctype("Web Form")
if not frappe.db.has_column("Web Form", "is_multi_step_form"):
return

for web_form in frappe.get_all("Web Form", filters={"is_multi_step_form": 1}):
web_form_fields = frappe.get_doc("Web Form", web_form.name).web_form_fields
for web_form_field in web_form_fields:
if web_form_field.fieldtype == "Section Break" and web_form_field.idx != 1:
frappe.db.set_value("Web Form Field", web_form_field.name, "fieldtype", "Page Break")
frappe.db.commit()

+ 4
- 0
frappe/public/js/frappe/views/treeview.js Visa fil

@@ -94,6 +94,10 @@ frappe.views.TreeView = class TreeView {
this.page.main.addClass("frappe-card");

if (this.opts.show_expand_all) {
this.page.add_inner_button(__("Collapse All"), function () {
me.tree.load_children(me.tree.root_node, false);
});

this.page.add_inner_button(__("Expand All"), function () {
me.tree.load_children(me.tree.root_node, true);
});


+ 1
- 0
frappe/public/js/frappe/views/workspace/workspace.js Visa fil

@@ -117,6 +117,7 @@ frappe.views.Workspace = class Workspace {
(page) => page.parent_page == "" || page.parent_page == null
);
}
root_pages = root_pages.uniqBy((d) => d.title);
this.build_sidebar_section(category, root_pages);
});



+ 2
- 2
frappe/public/scss/desk/report.scss Visa fil

@@ -196,8 +196,8 @@
&.blue { color: var(--blue-500); }

// SIZE & SPACING
margin-top: 12px;
margin-bottom: 5px;
padding-top: 12px;
padding-bottom: 5px;

// LAYOUT
text-align: center;


+ 9
- 0
frappe/tests/test_auth.py Visa fil

@@ -98,6 +98,7 @@ class TestAuth(FrappeTestCase):

def test_deny_multiple_login(self):
self.set_system_settings("deny_multiple_sessions", 1)
self.addCleanup(self.set_system_settings, "deny_multiple_sessions", 0)

first_login = FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password)
first_login.get_list("ToDo")
@@ -114,6 +115,14 @@ class TestAuth(FrappeTestCase):
second_login.get_list("ToDo")
third_login.get_list("ToDo")

def test_disable_user_pass_login(self):
FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password).get_list("ToDo")
self.set_system_settings("disable_user_pass_login", 1)
self.addCleanup(self.set_system_settings, "disable_user_pass_login", 0)

with self.assertRaises(Exception):
FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password).get_list("ToDo")


class TestLoginAttemptTracker(FrappeTestCase):
def test_account_lock(self):


+ 64
- 0
frappe/tests/test_perf.py Visa fil

@@ -0,0 +1,64 @@
"""
This file contains multiple primitive tests for avoiding performance regressions.

- Time bound tests: Benchmarks are done on GHA before adding numbers
- Query count tests: More than expected # of queries for any action is frequent source of
performance issues. This guards against such problems.


E.g. We know get_controller is supposed to be cached and hence shouldn't make query post first
query. This test can be written like this.

>>> def test_controller_caching(self):
>>>
>>> get_controller("User") # <- "warm up code"
>>> with self.assertQueryCount(0):
>>> get_controller("User")

"""
import unittest

import frappe
from frappe.model.base_document import get_controller
from frappe.tests.utils import FrappeTestCase
from frappe.website.path_resolver import PathResolver


class TestPerformance(FrappeTestCase):
def reset_request_specific_caches(self):
# To simulate close to request level of handling
frappe.destroy() # releases everything on frappe.local
frappe.init(site=self.TEST_SITE)
frappe.connect()
frappe.clear_cache()

def setUp(self) -> None:
self.reset_request_specific_caches()

def test_meta_caching(self):
frappe.get_meta("User")

with self.assertQueryCount(0):
frappe.get_meta("User")

def test_controller_caching(self):

get_controller("User")
with self.assertQueryCount(0):
get_controller("User")

def test_db_value_cache(self):
"""Link validation if repeated should just use db.value_cache, hence no extra queries"""
doc = frappe.get_last_doc("User")
doc.get_invalid_links()

with self.assertQueryCount(0):
doc.get_invalid_links()

@unittest.skip("Not implemented")
def test_homepage_resolver(self):
paths = ["/", "/app"]
for path in paths:
PathResolver(path).resolve()
with self.assertQueryCount(1):
PathResolver(path).resolve()

+ 1
- 2
frappe/tests/ui_test_helpers.py Visa fil

@@ -38,8 +38,7 @@ def create_if_not_exists(doc):

@frappe.whitelist()
def create_todo_records():
if frappe.get_all("ToDo", {"description": "this is first todo"}):
return
frappe.db.truncate("ToDo")

frappe.get_doc(
{"doctype": "ToDo", "date": add_to_date(now(), days=7), "description": "this is first todo"}


+ 18
- 0
frappe/tests/utils.py Visa fil

@@ -19,10 +19,13 @@ class FrappeTestCase(unittest.TestCase):
otherwise this class will become ineffective.
"""

TEST_SITE = "test_site"

SHOW_TRANSACTION_COMMIT_WARNINGS = False

@classmethod
def setUpClass(cls) -> None:
cls.TEST_SITE = getattr(frappe.local, "site", None) or cls.TEST_SITE
# flush changes done so far to avoid flake
frappe.db.commit()
frappe.db.begin()
@@ -64,6 +67,21 @@ class FrappeTestCase(unittest.TestCase):
else:
self.assertEqual(expected, actual, msg=msg)

@contextmanager
def assertQueryCount(self, count):
def _sql_with_count(*args, **kwargs):
frappe.db.sql_query_count += 1
return orig_sql(*args, **kwargs)

try:
orig_sql = frappe.db.sql
frappe.db.sql_query_count = 0
frappe.db.sql = _sql_with_count
yield
self.assertLessEqual(frappe.db.sql_query_count, count)
finally:
frappe.db.sql = orig_sql


def _commit_watcher():
import traceback


+ 2
- 4
frappe/utils/install.py Visa fil

@@ -289,12 +289,10 @@ def add_standard_navbar_items():
"is_standard": 1,
},
{
"item_label": "Background Jobs",
"item_type": "Route",
"route": "/app/background_jobs",
"item_type": "Separator",
"is_standard": 1,
"item_label": "",
},
{"item_type": "Separator", "is_standard": 1},
{
"item_label": "Log out",
"item_type": "Action",


+ 1
- 0
frappe/utils/safe_exec.py Visa fil

@@ -167,6 +167,7 @@ def get_safe_globals():
rollback=frappe.db.rollback,
add_index=frappe.db.add_index,
),
lang=getattr(frappe.local, "lang", "en"),
),
FrappeClient=FrappeClient,
style=frappe._dict(border_color="#d1d8dd"),


+ 8
- 3
frappe/www/login.html Visa fil

@@ -1,6 +1,7 @@
{% extends "templates/web.html" %}

{% macro email_login_body() -%}
{% if not disable_user_pass_login or (ldap_settings and ldap_settings.enabled) %}
<div class="page-card-body">
<div class="form-group">
<label class="form-label sr-only" for="login_email">{{ login_label or _("Email")}}</label>
@@ -38,13 +39,15 @@
</div>

<p class="forgot-password-message">
<a href="#forgot">{{ _("Forgot Password?") }}</a></p>
<a href="#forgot">{{ _("Forgot Password?") }}</a>
</p>
</div>
{% endif %}
<div class="page-card-actions">
{% if not disable_user_pass_login %}
<button class="btn btn-sm btn-primary btn-block btn-login" type="submit">
{{ _("Login") }}</button>
{% endif %}
{% if ldap_settings and ldap_settings.enabled %}
<button class="btn btn-sm btn-default btn-block btn-login btn-ldap-login">
{{ _("Login with LDAP") }}</button>
@@ -83,7 +86,9 @@
{{ email_login_body() }}
</form>
<div class="social-logins text-center">
{% if not disable_user_pass_login or (ldap_settings and ldap_settings.enabled) %}
<p class="text-muted login-divider">{{ _("or") }}</p>
{% endif %}
<div class="social-login-buttons">
{% for provider in provider_logins %}
<div class="login-button-wrapper">


+ 3
- 1
frappe/www/login.py Visa fil

@@ -7,6 +7,7 @@ from frappe import _
from frappe.auth import LoginManager
from frappe.integrations.doctype.ldap_settings.ldap_settings import LDAPSettings
from frappe.integrations.oauth2_logins import decoder_compat
from frappe.utils import cint
from frappe.utils.html_utils import get_icon_html
from frappe.utils.jinja import guess_is_path
from frappe.utils.oauth import (
@@ -40,7 +41,8 @@ def get_context(context):
context.for_test = "login.html"
context["title"] = "Login"
context["provider_logins"] = []
context["disable_signup"] = frappe.utils.cint(frappe.get_website_settings("disable_signup"))
context["disable_signup"] = cint(frappe.get_website_settings("disable_signup"))
context["disable_user_pass_login"] = cint(frappe.get_system_settings("disable_user_pass_login"))
context["logo"] = frappe.get_website_settings("app_logo") or frappe.get_hooks("app_logo_url")[-1]
context["app_name"] = (
frappe.get_website_settings("app_name") or frappe.get_system_settings("app_name") or _("Frappe")


Laddar…
Avbryt
Spara