diff --git a/.github/workflows/patch-mariadb-tests.yml b/.github/workflows/patch-mariadb-tests.yml index 3ac5cfa349..56137d0bea 100644 --- a/.github/workflows/patch-mariadb-tests.yml +++ b/.github/workflows/patch-mariadb-tests.yml @@ -102,4 +102,25 @@ jobs: cd ~/frappe-bench/ wget https://frappeframework.com/files/v10-frappe.sql.gz bench --site test_site --force restore ~/frappe-bench/v10-frappe.sql.gz + + source env/bin/activate + cd apps/frappe/ + git remote set-url upstream https://github.com/frappe/frappe.git + git fetch --all --tags + + taglist=$(git tag --sort version:refname | grep -v "beta") + last_release=$(echo "$taglist" | tail -1 | cut -d . -f 1 | cut -c 2-) + + for version in $(seq 12 "$last_release") + do + last_tag=$(echo "$taglist" | grep "v$version" | tail -1) + echo "Updating to $last_tag" + git checkout -q -f "$last_tag" + pip install -q -r requirements.txt + bench --site test_site migrate + done + + echo "Updating to last commit" + git checkout -q -f "$GITHUB_SHA" + bench setup requirements --python bench --site test_site migrate diff --git a/cypress/integration/dashboard_links.js b/cypress/integration/dashboard_links.js index b77965ee1a..973694c84b 100644 --- a/cypress/integration/dashboard_links.js +++ b/cypress/integration/dashboard_links.js @@ -9,17 +9,20 @@ context('Dashboard links', () => { cy.clear_filters(); cy.visit('/app/user'); - cy.get('.list-row-col > .level-item > .ellipsis').eq(0).click(); + cy.get('.list-row-col > .level-item > .ellipsis').eq(0).click({ force: true }); //To check if initially the dashboard contains only the "Contact" link and there is no counter cy.get('[data-doctype="Contact"]').should('contain', 'Contact'); //Adding a new contact - cy.get('.btn[data-doctype="Contact"]').click(); + cy.get('.document-link-badge[data-doctype="Contact"]').click(); + cy.wait(300); + cy.findByRole('button', {name: 'Add Contact'}).should('be.visible'); + cy.findByRole('button', {name: 'Add Contact'}).click(); cy.get('[data-doctype="Contact"][data-fieldname="first_name"]').type('Admin'); cy.findByRole('button', {name: 'Save'}).click(); cy.visit('/app/user'); - cy.get('.list-row-col > .level-item > .ellipsis').eq(0).click(); + cy.get('.list-row-col > .level-item > .ellipsis').eq(0).click({ force: true }); //To check if the counter for contact doc is "1" after adding the contact cy.get('[data-doctype="Contact"] > .count').should('contain', '1'); @@ -27,7 +30,7 @@ context('Dashboard links', () => { //Deleting the newly created contact cy.visit('/app/contact'); - cy.get('.list-subject > .select-like > .list-row-checkbox').eq(0).click(); + cy.get('.list-subject > .select-like > .list-row-checkbox').eq(0).click({ force: true }); cy.findByRole('button', {name: 'Actions'}).click(); cy.get('.actions-btn-group [data-label="Delete"]').click(); cy.findByRole('button', {name: 'Yes'}).click({delay: 700}); @@ -36,7 +39,7 @@ context('Dashboard links', () => { //To check if the counter from the "Contact" doc link is removed cy.wait(700); cy.visit('/app/user'); - cy.get('.list-row-col > .level-item > .ellipsis').eq(0).click(); + cy.get('.list-row-col > .level-item > .ellipsis').eq(0).click({ force: true }); cy.get('[data-doctype="Contact"]').should('contain', 'Contact'); }); diff --git a/cypress/integration/folder_navigation.js b/cypress/integration/folder_navigation.js index 1b7c02d98c..cec7edb59f 100644 --- a/cypress/integration/folder_navigation.js +++ b/cypress/integration/folder_navigation.js @@ -71,7 +71,7 @@ context('Folder Navigation', () => { it('Deleting Test Folder from the home', () => { //Deleting the Test Folder added in the home directory cy.visit('/app/file/view/home'); - cy.get('.level-left > .list-subject > .list-row-checkbox').eq(0).click({force: true, delay: 500}); + cy.get('.level-left > .list-subject > .file-select >.list-row-checkbox').eq(0).click({force: true, delay: 500}); cy.findByRole('button', {name: 'Actions'}).click(); cy.get('.actions-btn-group [data-label="Delete"]').click(); cy.findByRole('button', {name: 'Yes'}).click(); diff --git a/cypress/integration/form.js b/cypress/integration/form.js index d20750b1d5..f860a742ef 100644 --- a/cypress/integration/form.js +++ b/cypress/integration/form.js @@ -8,7 +8,10 @@ context('Form', () => { }); it('create a new form', () => { cy.visit('/app/todo/new'); - cy.fill_field('description', 'this is a test todo', 'Text Editor'); + cy.get('[data-fieldname="description"] .ql-editor') + .first() + .click() + .type('this is a test todo'); cy.wait(300); cy.get('.page-title').should('contain', 'Not Saved'); cy.intercept({ diff --git a/cypress/integration/list_view.js b/cypress/integration/list_view.js index 2e865e6a57..ce9e87274b 100644 --- a/cypress/integration/list_view.js +++ b/cypress/integration/list_view.js @@ -14,12 +14,7 @@ context('List View', () => { cy.get('.dropdown-menu li:visible .dropdown-item .menu-item-label[data-label="Edit"]').click(); cy.get('.modal-body .form-control[data-fieldname="field"]').first().select('Due Date').wait(200); - cy.get('.modal-body .frappe-control[data-fieldname="value"] input:visible').first().focus(); - cy.get('.datepickers-container .datepicker.active').should('be.visible'); - - cy.get('.datepickers-container .datepicker.active .datepicker--cell-day.-current-').click({force: true}); - cy.get('.modal-body .frappe-control[data-fieldname="value"] input:visible').first().focus(); - cy.get('.datepickers-container .datepicker.active .datepicker--cell-day.-current-').click({force: true}); + cy.fill_field('value', '09-28-21', 'Date'); cy.get('.modal-footer .standard-actions .btn-primary').click(); cy.wait(500); @@ -46,7 +41,9 @@ context('List View', () => { }).as('real-time-update'); cy.wrap(elements).contains('Approve').click(); cy.wait(['@bulk-approval', '@real-time-update']); - cy.hide_dialog(); + cy.wait(300); + cy.get_open_dialog().find('.btn-modal-close').click(); + cy.reload(); cy.clear_filters(); cy.get('.list-row-container:visible').should('contain', 'Approved'); }); diff --git a/cypress/integration/navigation.js b/cypress/integration/navigation.js index ba45137cbd..c4d0638f26 100644 --- a/cypress/integration/navigation.js +++ b/cypress/integration/navigation.js @@ -13,6 +13,7 @@ context('Navigation', () => { it.only('Navigate to previous page after login', () => { cy.visit('/app/todo'); + cy.findByTitle('To Do').should('be.visible'); cy.request('/api/method/logout'); cy.reload(); cy.get('.btn-primary').contains('Login').click(); diff --git a/cypress/integration/timeline.js b/cypress/integration/timeline.js index 6387485220..66ba0761c4 100644 --- a/cypress/integration/timeline.js +++ b/cypress/integration/timeline.js @@ -11,6 +11,7 @@ context('Timeline', () => { cy.visit('/app/todo'); cy.click_listview_primary_button('Add ToDo'); cy.findByRole('button', {name: 'Edit in full page'}).click(); + cy.findByTitle('New ToDo').should('be.visible'); cy.get('[data-fieldname="description"] .ql-editor').eq(0).type('Test ToDo', {force: true}); cy.wait(200); cy.findByRole('button', {name: 'Save'}).click(); diff --git a/cypress/integration/timeline_email.js b/cypress/integration/timeline_email.js index 82af24e822..dfe80e0019 100644 --- a/cypress/integration/timeline_email.js +++ b/cypress/integration/timeline_email.js @@ -5,14 +5,16 @@ context('Timeline Email', () => { cy.visit('/app/todo'); }); - it('Adding new ToDo, adding email and verifying timeline content for email attachment, deleting attachment and ToDo', () => { - //Adding new ToDo + it('Adding new ToDo', () => { cy.click_listview_primary_button('Add ToDo'); cy.get('.custom-actions:visible > .btn').contains("Edit in full page").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, deleting attachment and ToDo', () => { cy.visit('/app/todo'); cy.get('.list-row > .level-left > .list-subject').eq(0).click(); @@ -41,11 +43,13 @@ context('Timeline Email', () => { 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(); + cy.visit('/app/todo'); cy.get('.list-row > .level-left > .list-subject > .level-item.ellipsis > .ellipsis').eq(0).click(); //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 diff --git a/frappe/handler.py b/frappe/handler.py index 352a9672bf..ea654517c3 100755 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -223,7 +223,10 @@ def run_doc_method(method, docs=None, dt=None, dn=None, arg=None, args=None): doc = frappe.get_doc(dt, dn) else: - doc = frappe.get_doc(json.loads(docs)) + if isinstance(docs, str): + docs = json.loads(docs) + + doc = frappe.get_doc(docs) doc._original_modified = doc.modified doc.check_if_latest() diff --git a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py index 9ae16a31f8..9bbab9db9b 100644 --- a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py +++ b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py @@ -371,6 +371,7 @@ def capture_payment(is_sandbox=False, sanbox_response=None): doc = frappe.get_doc("Integration Request", doc.name) doc.status = "Failed" doc.error = frappe.get_traceback() + doc.save() frappe.log_error(doc.error, '{0} Failed'.format(doc.name)) diff --git a/frappe/public/js/frappe/ui/tree.js b/frappe/public/js/frappe/ui/tree.js index 1308c3931c..4b11b092eb 100644 --- a/frappe/public/js/frappe/ui/tree.js +++ b/frappe/public/js/frappe/ui/tree.js @@ -39,6 +39,7 @@ frappe.ui.Tree = class { method: this.method, args: args, callback: (r) => { + this.on_get_node && this.on_get_node(r.message); resolve(r.message); } }); @@ -58,6 +59,7 @@ frappe.ui.Tree = class { method: 'frappe.desk.treeview.get_all_nodes', args: args, callback: (r) => { + this.on_get_node && this.on_get_node(r.message, true); resolve(r.message); } }); diff --git a/frappe/public/js/frappe/views/communication.js b/frappe/public/js/frappe/views/communication.js index 0eeb5d9ffc..beeea40c5e 100755 --- a/frappe/public/js/frappe/views/communication.js +++ b/frappe/public/js/frappe/views/communication.js @@ -36,7 +36,7 @@ frappe.views.CommunicationComposer = class { minimizable: true }); - this.dialog.sections[0].wrapper.addClass('to_section'); + $(this.dialog.$wrapper.find(".form-section").get(0)).addClass('to_section'); this.prepare(); this.dialog.show(); diff --git a/frappe/public/js/frappe/views/treeview.js b/frappe/public/js/frappe/views/treeview.js index 1bdb9cbe6f..30da212f0a 100644 --- a/frappe/public/js/frappe/views/treeview.js +++ b/frappe/public/js/frappe/views/treeview.js @@ -165,6 +165,7 @@ frappe.views.TreeView = class TreeView { get_label: this.opts.get_label, on_render: this.opts.onrender, + on_get_node: this.opts.on_get_node, on_click: (node) => { this.select_node(node); }, }); diff --git a/frappe/utils/print_format.py b/frappe/utils/print_format.py index 130ee24f21..6dfa3a350b 100644 --- a/frappe/utils/print_format.py +++ b/frappe/utils/print_format.py @@ -98,7 +98,7 @@ def report_to_pdf(html, orientation="Landscape"): frappe.local.response.type = "pdf" @frappe.whitelist() -def print_by_server(doctype, name, printer_setting, print_format=None, doc=None, no_letterhead=0): +def print_by_server(doctype, name, printer_setting, print_format=None, doc=None, no_letterhead=0, file_path=None): print_settings = frappe.get_doc("Network Printer Settings", printer_setting) try: import cups @@ -111,9 +111,10 @@ def print_by_server(doctype, name, printer_setting, print_format=None, doc=None, conn = cups.Connection() output = PdfFileWriter() output = frappe.get_print(doctype, name, print_format, doc=doc, no_letterhead=no_letterhead, as_pdf = True, output = output) - file = os.path.join("/", "tmp", "frappe-pdf-{0}.pdf".format(frappe.generate_hash())) - output.write(open(file,"wb")) - conn.printFile(print_settings.printer_name,file , name, {}) + if not file_path: + file_path = os.path.join("/", "tmp", "frappe-pdf-{0}.pdf".format(frappe.generate_hash())) + output.write(open(file_path,"wb")) + conn.printFile(print_settings.printer_name,file_path , name, {}) except IOError as e: if ("ContentNotFoundError" in e.message or "ContentOperationNotPermittedError" in e.message diff --git a/requirements.txt b/requirements.txt index a0ad0b6266..59cc03b970 100644 --- a/requirements.txt +++ b/requirements.txt @@ -39,7 +39,7 @@ pdfkit~=0.6.1 Pillow~=8.2.0 premailer~=3.8.0 psutil~=5.8.0 -psycopg2-binary~=2.8.6 +psycopg2-binary~=2.9.1 pyasn1~=0.4.8 pycryptodome~=3.10.1 PyJWT~=2.0.1 @@ -77,3 +77,4 @@ wrapt~=1.12.1 xlrd~=2.0.1 zxcvbn-python~=4.4.24 tenacity~=8.0.1 +pycups~=2.0.1