From b417218f1d6261478f4c7e6ef41e53633965df1f Mon Sep 17 00:00:00 2001 From: MitulDavid Date: Tue, 17 Aug 2021 19:06:01 +0530 Subject: [PATCH] test: Add support for Testing-Library queries within Cypress tests --- cypress/integration/awesome_bar.js | 14 ++++++------- cypress/integration/control_barcode.js | 6 +++--- cypress/integration/control_icon.js | 10 ++++----- cypress/integration/control_link.js | 4 ++-- cypress/integration/control_select.js | 2 ++ cypress/integration/depends_on.js | 6 +++--- cypress/integration/file_uploader.js | 29 +++++++++++++------------- cypress/support/commands.js | 1 + frappe/commands/utils.py | 4 +++- 9 files changed, 41 insertions(+), 35 deletions(-) diff --git a/cypress/integration/awesome_bar.js b/cypress/integration/awesome_bar.js index 3e12101532..08a2a71843 100644 --- a/cypress/integration/awesome_bar.js +++ b/cypress/integration/awesome_bar.js @@ -10,9 +10,9 @@ context('Awesome Bar', () => { }); it('navigates to doctype list', () => { - cy.get('#navbar-search').type('todo', { delay: 200 }); - cy.get('#navbar-search + ul').should('be.visible'); - cy.get('#navbar-search').type('{downarrow}{enter}', { delay: 100 }); + cy.findByPlaceholderText('Search or type a command (Ctrl + G)').type('todo', { delay: 200 }); + cy.findByRole('listbox').should('be.visible'); + cy.findByPlaceholderText('Search or type a command (Ctrl + G)').type('{downarrow}{enter}', { delay: 100 }); cy.get('.title-text').should('contain', 'To Do'); @@ -20,24 +20,24 @@ context('Awesome Bar', () => { }); it('find text in doctype list', () => { - cy.get('#navbar-search') + cy.findByPlaceholderText('Search or type a command (Ctrl + G)') .type('test in todo{downarrow}{enter}', { delay: 200 }); cy.get('.title-text').should('contain', 'To Do'); - cy.get('[data-original-title="Name"] > .input-with-feedback') + cy.findByPlaceholderText('Name') .should('have.value', '%test%'); }); it('navigates to new form', () => { - cy.get('#navbar-search') + cy.findByPlaceholderText('Search or type a command (Ctrl + G)') .type('new blog post{downarrow}{enter}', { delay: 200 }); cy.get('.title-text:visible').should('have.text', 'New Blog Post'); }); it('calculates math expressions', () => { - cy.get('#navbar-search') + cy.findByPlaceholderText('Search or type a command (Ctrl + G)') .type('55 + 32{downarrow}{enter}', { delay: 200 }); cy.get('.modal-title').should('contain', 'Result'); diff --git a/cypress/integration/control_barcode.js b/cypress/integration/control_barcode.js index 1df5e64f0e..51984fdbd2 100644 --- a/cypress/integration/control_barcode.js +++ b/cypress/integration/control_barcode.js @@ -20,7 +20,7 @@ context('Control Barcode', () => { it('should generate barcode on setting a value', () => { get_dialog_with_barcode().as('dialog'); - cy.get('.frappe-control[data-fieldname=barcode] input') + cy.findByRole('textbox') .focus() .type('123456789') .blur(); @@ -37,11 +37,11 @@ context('Control Barcode', () => { it('should reset when input is cleared', () => { get_dialog_with_barcode().as('dialog'); - cy.get('.frappe-control[data-fieldname=barcode] input') + cy.findByRole('textbox') .focus() .type('123456789') .blur(); - cy.get('.frappe-control[data-fieldname=barcode] input') + cy.findByRole('textbox') .clear() .blur(); cy.get('.frappe-control[data-fieldname=barcode] svg[data-barcode-value="123456789"]') diff --git a/cypress/integration/control_icon.js b/cypress/integration/control_icon.js index f92927f267..73927a15c6 100644 --- a/cypress/integration/control_icon.js +++ b/cypress/integration/control_icon.js @@ -17,17 +17,17 @@ context('Control Icon', () => { it('should set icon', () => { get_dialog_with_icon().as('dialog'); - cy.get('.frappe-control[data-fieldname=icon] input').first().click(); + cy.findByRole('textbox').first().click(); cy.get('.icon-picker .icon-wrapper[id=active]').first().click(); - cy.get('.frappe-control[data-fieldname=icon] input').first().should('have.value', 'active'); + cy.findByRole('textbox').first().should('have.value', 'active'); cy.get('@dialog').then(dialog => { let value = dialog.get_value('icon'); expect(value).to.equal('active'); }); cy.get('.icon-picker .icon-wrapper[id=resting]').first().click(); - cy.get('.frappe-control[data-fieldname=icon] input').first().should('have.value', 'resting'); + cy.findByRole('textbox').first().should('have.value', 'resting'); cy.get('@dialog').then(dialog => { let value = dialog.get_value('icon'); expect(value).to.equal('resting'); @@ -36,14 +36,14 @@ context('Control Icon', () => { it('search for icon and clear search input', () => { let search_text = 'ed'; - cy.get('.icon-picker input[type=search]').first().click().type(search_text); + cy.findByRole('searchbox').first().click().type(search_text); cy.get('.icon-section .icon-wrapper:not(.hidden)').then(i => { cy.get(`.icon-section .icon-wrapper[id*='${search_text}']`).then(icons => { expect(i.length).to.equal(icons.length); }); }); - cy.get('.icon-picker input[type=search]').clear().blur(); + cy.findByRole('searchbox').clear().blur(); cy.get('.icon-section .icon-wrapper').should('not.have.class', 'hidden'); }); diff --git a/cypress/integration/control_link.js b/cypress/integration/control_link.js index 8f9257e9c4..d2aa467af1 100644 --- a/cypress/integration/control_link.js +++ b/cypress/integration/control_link.js @@ -35,7 +35,7 @@ context('Control Link', () => { cy.wait('@search_link'); cy.get('@input').type('todo for link', { delay: 200 }); cy.wait('@search_link'); - cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible'); + cy.findByRole('listbox').should('be.visible'); cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 }); cy.get('.frappe-control[data-fieldname=link] input').blur(); cy.get('@dialog').then(dialog => { @@ -71,7 +71,7 @@ context('Control Link', () => { cy.get('@input').type(todos[0]).blur(); cy.wait('@validate_link'); cy.get('@input').focus(); - cy.get('.frappe-control[data-fieldname=link] .link-btn') + cy.findByTitle('Open Link') .should('be.visible') .click(); cy.location('pathname').should('eq', `/app/todo/${todos[0]}`); diff --git a/cypress/integration/control_select.js b/cypress/integration/control_select.js index 0bc719b4a7..8e18d21260 100644 --- a/cypress/integration/control_select.js +++ b/cypress/integration/control_select.js @@ -24,8 +24,10 @@ context('Control Select', () => { cy.get('@control').get('.select-icon').should('exist'); cy.get('@control').get('.placeholder').should('have.css', 'display', 'block'); cy.get('@select').select('Option 1'); + cy.findByDisplayValue('Option 1').should('exist'); cy.get('@control').get('.placeholder').should('have.css', 'display', 'none'); cy.get('@select').invoke('val', ''); + cy.findByDisplayValue('Option 1').should('not.exist'); cy.get('@control').get('.placeholder').should('have.css', 'display', 'block'); diff --git a/cypress/integration/depends_on.js b/cypress/integration/depends_on.js index d33babb134..9aa6b5d89d 100644 --- a/cypress/integration/depends_on.js +++ b/cypress/integration/depends_on.js @@ -62,11 +62,11 @@ context('Depends On', () => { it('should set the field as mandatory depending on other fields value', () => { cy.new_form('Test Depends On'); cy.fill_field('test_field', 'Some Value'); - cy.get('button.primary-action').contains('Save').click(); + cy.findByRole('button', {name: 'Save'}).click(); cy.get('.msgprint-dialog .modal-title').contains('Missing Fields').should('be.visible'); cy.hide_dialog(); cy.fill_field('test_field', 'Random value'); - cy.get('button.primary-action').contains('Save').click(); + cy.findByRole('button', {name: 'Save'}).click(); cy.get('.msgprint-dialog .modal-title').contains('Missing Fields').should('not.be.visible'); }); it('should set the field as read only depending on other fields value', () => { @@ -84,7 +84,7 @@ context('Depends On', () => { cy.fill_field('dependant_field', 'Some Value'); //cy.fill_field('test_field', 'Some Other Value'); cy.get('.frappe-control[data-fieldname="child_test_depends_on_field"]').as('table'); - cy.get('@table').find('button.grid-add-row').click(); + cy.get('@table').findByRole('button', {name: 'Add Row'}).click(); cy.get('@table').find('[data-idx="1"]').as('row1'); cy.get('@row1').find('.btn-open-row').click(); cy.get('@row1').find('.form-in-grid').as('row1-form_in_grid'); diff --git a/cypress/integration/file_uploader.js b/cypress/integration/file_uploader.js index e1e232c058..3d4f92df3c 100644 --- a/cypress/integration/file_uploader.js +++ b/cypress/integration/file_uploader.js @@ -25,7 +25,7 @@ context('FileUploader', () => { cy.get_open_dialog().find('.file-name').should('contain', 'example.json'); cy.intercept('POST', '/api/method/upload_file').as('upload_file'); - cy.get_open_dialog().find('.btn-modal-primary').click(); + cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click(); cy.wait('@upload_file').its('response.statusCode').should('eq', 200); cy.get('.modal:visible').should('not.exist'); }); @@ -33,11 +33,11 @@ context('FileUploader', () => { it('should accept uploaded files', () => { open_upload_dialog(); - cy.get_open_dialog().find('.btn-file-upload div:contains("Library")').click(); - cy.get('.file-filter').type('example.json'); - cy.get_open_dialog().find('.tree-label:contains("example.json")').first().click(); + cy.get_open_dialog().findByRole('button', {name: 'Library'}).click(); + cy.findByPlaceholderText('Search by filename or extension').type('example.json'); + cy.get_open_dialog().findAllByText('example.json').first().click(); cy.intercept('POST', '/api/method/upload_file').as('upload_file'); - cy.get_open_dialog().find('.btn-primary').click(); + cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click(); cy.wait('@upload_file').its('response.body.message') .should('have.property', 'file_name', 'example.json'); cy.get('.modal:visible').should('not.exist'); @@ -46,10 +46,12 @@ context('FileUploader', () => { it('should accept web links', () => { open_upload_dialog(); - cy.get_open_dialog().find('.btn-file-upload div:contains("Link")').click(); - cy.get_open_dialog().find('.file-web-link input').type('https://github.com', { delay: 100, force: true }); + cy.get_open_dialog().findByRole('button', {name: 'Link'}).click(); + cy.get_open_dialog() + .findByPlaceholderText('Attach a web link') + .type('https://github.com', { delay: 100, force: true }); cy.intercept('POST', '/api/method/upload_file').as('upload_file'); - cy.get_open_dialog().find('.btn-primary').click(); + cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click(); cy.wait('@upload_file').its('response.body.message') .should('have.property', 'file_url', 'https://github.com'); cy.get('.modal:visible').should('not.exist'); @@ -62,15 +64,14 @@ context('FileUploader', () => { subjectType: 'drag-n-drop', }); - cy.get_open_dialog().find('.file-name').should('contain', 'sample_image.jpg'); + cy.get_open_dialog().findAllByText('sample_image.jpg').should('exist'); cy.get_open_dialog().find('.btn-crop').first().click(); - cy.get_open_dialog().find('.image-cropper-actions > .btn-primary').should('contain', 'Crop'); - cy.get_open_dialog().find('.image-cropper-actions > .btn-primary').click(); - cy.get_open_dialog().find('.optimize-checkbox').first().should('contain', 'Optimize'); - cy.get_open_dialog().find('.optimize-checkbox').first().click(); + cy.get_open_dialog().findByRole('button', {name: 'Crop'}).click(); + cy.get_open_dialog().findAllByRole('checkbox', {name: 'Optimize'}).should('exist'); + cy.get_open_dialog().findAllByLabelText('Optimize').first().click(); cy.intercept('POST', '/api/method/upload_file').as('upload_file'); - cy.get_open_dialog().find('.btn-modal-primary').click(); + cy.get_open_dialog().findByRole('button', {name: 'Upload'}).click(); cy.wait('@upload_file').its('response.statusCode').should('eq', 200); cy.get('.modal:visible').should('not.exist'); }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index a81ba60fb0..c941652487 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -1,4 +1,5 @@ import 'cypress-file-upload'; +import '@testing-library/cypress/add-commands'; // *********************************************** // This example commands.js shows you how to // create various custom commands and overwrite diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index f2395ae490..ac574841fe 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -597,16 +597,18 @@ def run_ui_tests(context, app, headless=False, parallel=True, ci_build_id=None): node_bin = subprocess.getoutput("npm bin") cypress_path = "{0}/cypress".format(node_bin) plugin_path = "{0}/../cypress-file-upload".format(node_bin) + testing_library_path = f"{node_bin}/../@testing-library" # check if cypress in path...if not, install it. if not ( os.path.exists(cypress_path) and os.path.exists(plugin_path) + and os.path.exists(testing_library_path) and cint(subprocess.getoutput("npm view cypress version")[:1]) >= 6 ): # install cypress click.secho("Installing Cypress...", fg="yellow") - frappe.commands.popen("yarn add cypress@^6 cypress-file-upload@^5 --no-lockfile") + frappe.commands.popen("yarn add cypress@^6 cypress-file-upload@^5 @testing-library/cypress@^8 --no-lockfile") # run for headless mode run_or_open = 'run --browser firefox --record' if headless else 'open'