diff --git a/.gitignore b/.gitignore index 19c8ba147..d3233ecbb 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ packages/server/scripts/local !**/.yarn/versions .claude +stats.html diff --git a/cypress/e2e/a-journal/002-assign_reviewers_spec.cy.js b/cypress/e2e/a-journal/002-assign_reviewers_spec.cy.js index c9b4bcc66..92d6ff9fe 100644 --- a/cypress/e2e/a-journal/002-assign_reviewers_spec.cy.js +++ b/cypress/e2e/a-journal/002-assign_reviewers_spec.cy.js @@ -41,6 +41,8 @@ describe('Editor assigning reviewers', () => { // Go to dashboard and verify number of invited reviewer Menu.clickDashboard() - cy.getByDataTestId('donut-center-label').contains('6') + cy.getByDataTestId('statusCounts') + .find('[data-testid="reviewer-status-square"]') + .should('have.length', 6) }) }) diff --git a/cypress/e2e/a-journal/003-review_spec.cy.js b/cypress/e2e/a-journal/003-review_spec.cy.js index 2c627a0b6..08b5efbeb 100644 --- a/cypress/e2e/a-journal/003-review_spec.cy.js +++ b/cypress/e2e/a-journal/003-review_spec.cy.js @@ -50,20 +50,20 @@ describe('Completing reviews', () => { cy.wait(1000) DashboardPage.clickDashboardTab(2) - cy.get('[fill="#56b984"]') - .should('be.visible') - .trigger('mouseover', { force: true }) - cy.contains('Completed: 3') - cy.wait(1000) - cy.get('[fill="#fff2cd"]').should('be.visible').trigger('mouseover') - cy.contains('Invited: 1') - cy.get('[fill="#d7efd4"]').should('be.visible').trigger('mouseover') - cy.contains('Accepted: 1') - - cy.get('[fill="#c23d20"]').should('be.visible').trigger('mouseover') - cy.contains('Declined: 1') - - cy.get('[data-testid="control-panel-team"]').click() + cy.getByDataTestId('statusCounts') + .find('[role="img"][aria-label$="Completed"]') + .should('have.length', 3) + cy.getByDataTestId('statusCounts') + .find('[role="img"][aria-label$="Invited"]') + .should('have.length', 1) + cy.getByDataTestId('statusCounts') + .find('[role="img"][aria-label$="Accepted"]') + .should('have.length', 1) + cy.getByDataTestId('statusCounts') + .find('[role="img"][aria-label$="Declined"]') + .should('have.length', 1) + + cy.get('[data-testid="control-link"]').click() cy.awaitDisappearSpinner() cy.get( '[data-testid=kanban] > :nth-child(1) > [data-testid=kanban-cards-wrapper] > [data-testid=kanban-card]', @@ -92,7 +92,9 @@ const doReview = (name, reviewData) => { // Accpet Review Request Workflow if (reviewData.verdict === 'accept') { DashboardPage.clickAcceptReviewButton() - cy.contains('button', 'Do Review').should('exist') + cy.contains('Accept this review invitation?').should('be.visible') + cy.contains('button', 'OK').click() + DashboardPage.getDoReviewButton().should('contain', 'Do Review') // Only do the review if there'a comment present if (reviewData.comment) { @@ -116,6 +118,7 @@ const doReview = (name, reviewData) => { // Verify the review got completed cy.get('nav').contains('Dashboard').click() + cy.url().should('contain', '/dashboard/reviews') DashboardPage.getDoReviewButton().should('contain', 'View') cy.getByDataTestId('menu-user').click() cy.contains('Logout').click() @@ -125,6 +128,8 @@ const doReview = (name, reviewData) => { // Reject Review Request Workflow if (reviewData.verdict === 'reject') { DashboardPage.clickRejectReviewButton() + cy.contains('Decline this review invitation?').should('be.visible') + cy.contains('button', 'OK').click() DashboardPage.getDoReviewButton().should('not.exist') cy.getByDataTestId('menu-user').click() cy.contains('Logout').click() diff --git a/cypress/e2e/a-journal/004-assigning_editor_spec.cy.js b/cypress/e2e/a-journal/004-assigning_editor_spec.cy.js index 631c4cdf8..eecc7e5ee 100644 --- a/cypress/e2e/a-journal/004-assigning_editor_spec.cy.js +++ b/cypress/e2e/a-journal/004-assigning_editor_spec.cy.js @@ -21,7 +21,7 @@ describe('Assigning editors and decision reject', () => { cy.wait(1000) Menu.clickManuscripts() - ManuscriptsPage.selectOptionWithText('Control') + ManuscriptsPage.clickControlLink() cy.wait(1000) ControlPage.clickAssignSeniorEditorDropdown() diff --git a/cypress/e2e/a-journal/005-decision_spec.cy.js b/cypress/e2e/a-journal/005-decision_spec.cy.js index dceaa9838..3d74de361 100644 --- a/cypress/e2e/a-journal/005-decision_spec.cy.js +++ b/cypress/e2e/a-journal/005-decision_spec.cy.js @@ -25,7 +25,7 @@ describe('Completing a decision', () => { cy.wait(2000) DashboardPage.clickManuscriptNavButton() - ManuscriptsPage.selectOptionWithText('Control') + ManuscriptsPage.clickControlLink() ControlPage.getAssignSeniorEditorDropdown().click({ force: true }) cy.contains(name.role.seniorEditor).click({ force: true }) }) @@ -61,7 +61,7 @@ describe('Completing a decision', () => { /* View Decision as an Author */ cy.login(name.role.author, dashboard) // Login as an Author - DashboardPage.getSubmittedManuscript().click() // Click on first MySubmission + DashboardPage.clickSubmissionActionLink() // Click on first MySubmission // Verify Decision Content DashboardPage.getDecisionField(0).should('contain', decisionTextContent) @@ -77,9 +77,7 @@ describe('Completing a decision', () => { ) SubmissionFormPage.clickSubmitResearch() SubmissionFormPage.clickSubmitYourManuscript() - DashboardPage.getSubmittedManuscript() - .contains('test pdf') - .should('exist') // Verify new submission got created + DashboardPage.getSubmissionTitle().contains('test pdf').should('exist') // Verify new submission got created }) }) diff --git a/cypress/e2e/b-colab-prc/102-manuscript_page_label_and_tooltip_spec.cy.js b/cypress/e2e/b-colab-prc/102-manuscript_page_label_and_tooltip_spec.cy.js index 8f47bdc5d..a9fc53b7d 100644 --- a/cypress/e2e/b-colab-prc/102-manuscript_page_label_and_tooltip_spec.cy.js +++ b/cypress/e2e/b-colab-prc/102-manuscript_page_label_and_tooltip_spec.cy.js @@ -27,22 +27,16 @@ describe('Checking manuscripts page: label selection and tooltip', () => { it('verifies the selected label after choosing from the dropdown', () => { cy.contains('div', 'Manuscripts').should('exist') - // Click the Select button - ManuscriptsPage.getSelectButton().should('be.visible') - ManuscriptsPage.clickArticleLabel(-1) + ManuscriptsPage.getArticleLabel().should('be.visible') // Function to select a label and verify it const selectLabelAndVerify = label => { - cy.getByDataTestId('label-dropdown-element').click() - cy.screenshot('before-dropdown') - cy.get('body').then($body => { - if (!$body.find('[data-testid=label-dropdown-menu]').length) { - cy.log('Dropdown not found in body!') - } - }) - - cy.get('[data-testid=label-dropdown-menu]').contains(label).click() - ManuscriptsPage.getLabelDropdown().should('contain', label) + ManuscriptsPage.clickArticleLabel(-1) + cy.getByDataTestId('select-dropdown') + .find('[data-testid="editable-option"]') + .contains(label) + .click({ force: true }) + ManuscriptsPage.getArticleLabel().eq(-1).should('contain', label) Menu.clickManuscriptsAndAssertPageLoad() } @@ -51,11 +45,10 @@ describe('Checking manuscripts page: label selection and tooltip', () => { labels.forEach(selectLabelAndVerify) // Unset the custom label - cy.get('[data-testid=label-dropdown-styled-button]').eq(0).click() - cy.get('[data-testid=manuscripts-table-styled-button]').should( - 'contain', - 'Select', - ) + ManuscriptsPage.clickArticleLabelClear(-1) + ManuscriptsPage.getArticleLabel() + .eq(-1) + .should('not.contain', 'Ready to publish') }) context('tooltip tests', () => { @@ -69,7 +62,8 @@ describe('Checking manuscripts page: label selection and tooltip', () => { it('check no tooltip for empty abstract', () => { Menu.clickManuscriptsAndAssertPageLoad() ManuscriptsPage.getTooltipText().should('not.exist') - ManuscriptsPage.getTooltipIcon().should('not.exist') + ManuscriptsPage.getTooltipIcon().should('be.visible').click() + ManuscriptsPage.getTooltipText().should('contain', 'No abstract provided') }) it('check tooltip text', () => { @@ -78,28 +72,29 @@ describe('Checking manuscripts page: label selection and tooltip', () => { SubmissionFormPage.fillInAbstractColab(data.abstract) Menu.clickManuscriptsAndAssertPageLoad() ManuscriptsPage.getTooltipText().should('not.exist') - ManuscriptsPage.getTooltipIcon() - .should('be.visible') - .trigger('mouseover') + ManuscriptsPage.getTooltipIcon().should('be.visible').click() ManuscriptsPage.getTooltipText() .should('contain', data.abstract) .and('not.contain', '

') }) }) - it('check length for the tooltip text, to be less than 1000', () => { + it('truncates a long abstract to 60 words', () => { + const longAbstract = Array.from( + { length: 65 }, + (_, i) => `word${i + 1}`, + ).join(' ') + cy.contains('Continue Submission').click() - cy.fixture('submission_form_data').then(data => { - SubmissionFormPage.fillInAbstractColab( - data.abstractWithMoreThan1000Characters, - ) - Menu.clickManuscriptsAndAssertPageLoad() - ManuscriptsPage.getTooltipText().should('not.exist') - ManuscriptsPage.getTooltipIcon().trigger('mouseover') - ManuscriptsPage.getTooltipText() - .should('contain', '...') - .should('have.lengthOf.lessThan', 1000) - }) + SubmissionFormPage.fillInAbstractColab(longAbstract) + Menu.clickManuscriptsAndAssertPageLoad() + ManuscriptsPage.getTooltipText().should('not.exist') + ManuscriptsPage.getTooltipIcon().click() + ManuscriptsPage.getTooltipText() + .should('contain', '...') + .should($el => { + expect($el.text().trim().split(/\s+/).length).to.be.at.most(61) + }) }) }) }) diff --git a/cypress/e2e/b-colab-prc/103-manuscripts_page_filter_sort_archive_spec.cy.js b/cypress/e2e/b-colab-prc/103-manuscripts_page_filter_sort_archive_spec.cy.js index 63087c8c9..6effc8b80 100644 --- a/cypress/e2e/b-colab-prc/103-manuscripts_page_filter_sort_archive_spec.cy.js +++ b/cypress/e2e/b-colab-prc/103-manuscripts_page_filter_sort_archive_spec.cy.js @@ -81,7 +81,7 @@ describe('manuscripts page tests - Filter, sort, bulk select, archive', () => { context('filter and sort articles', () => { it('filter per status', () => { ManuscriptsPage.getTableRowsCount().should('eq', 3) - ManuscriptsPage.clickStatus(-1) + ManuscriptsPage.filterColumnByValue('status', 'Unsubmitted') ManuscriptsPage.getTableRowsCount().should('eq', 2) ManuscriptsPage.getStatus(0).should('contain', 'Unsubmitted') cy.url().should('contain', 'new') @@ -97,18 +97,15 @@ describe('manuscripts page tests - Filter, sort, bulk select, archive', () => { cy.url().should('contain', 'readyToEvaluate') ManuscriptsPage.getLabelRow(0).should('contain', 'Ready to evaluate') ManuscriptsPage.getLabelRow(1).should('contain', 'Ready to evaluate') - ManuscriptsPage.getLabelRow(2).should('contain', 'Ready to evaluate') Menu.clickManuscriptsAndAssertPageLoad() ManuscriptsPage.selectCustomStatus('Evaluated') ManuscriptsPage.getTableRowsCount().should('eq', 1) cy.url().should('contain', 'evaluated') ManuscriptsPage.getLabelRow(0).should('contain', 'Evaluated') - ManuscriptsPage.getLabelRow(1).should('contain', 'Evaluated') Menu.clickManuscriptsAndAssertPageLoad() ManuscriptsPage.selectCustomStatus('Ready to publish') ManuscriptsPage.assertNoTableRows() cy.url().should('contain', 'readyToPublish') - ManuscriptsPage.getLabelRow(0).should('contain', 'Ready to publish') }) }) @@ -134,7 +131,7 @@ describe('manuscripts page tests - Filter, sort, bulk select, archive', () => { ManuscriptsPage.clickDelete() ManuscriptsPage.getConfirmationMessageForBulkDelete().should( 'contain', - 'Please confirm you wish to archive the selected manuscripts.', + 'Archive 2 manuscripts?', ) ManuscriptsPage.clickConfirm() ManuscriptsPage.getConfirmationMessageForBulkDelete().should('not.exist') diff --git a/cypress/e2e/b-colab-prc/104-control_page_spec.cy.js b/cypress/e2e/b-colab-prc/104-control_page_spec.cy.js index bea358245..31cd62123 100644 --- a/cypress/e2e/b-colab-prc/104-control_page_spec.cy.js +++ b/cypress/e2e/b-colab-prc/104-control_page_spec.cy.js @@ -175,9 +175,10 @@ describe('control page tests', () => { cy.login(name.role.admin, manuscripts) cy.awaitDisappearSpinner() Menu.clickManuscriptsAndAssertPageLoad() - ManuscriptsPage.selectOptionWithText('Control') + ManuscriptsPage.clickControlLink() cy.awaitDisappearSpinner() ControlPage.getAssignSeniorEditorDropdown().should('be.visible') + cy.awaitDisappearSpinner() ControlPage.inviteReviewer(name.role.reviewers[1]) cy.reload() cy.get('input[value = "isCollaborative"]').should('not.exist') @@ -188,8 +189,10 @@ describe('control page tests', () => { cy.wait(1000) DashboardPage.clickDashboardTab(1) DashboardPage.clickAcceptReviewButton() + cy.contains('Accept this review invitation?').should('be.visible') + cy.contains('button', 'OK').click() - cy.contains('button', 'Do Review').should('exist') + DashboardPage.getDoReviewButton().should('contain', 'Do Review') DashboardPage.clickDoReview() cy.fixture('submission_form_data').then(data => { cy.contains('div', 'Metadata').should('be.visible') @@ -202,7 +205,7 @@ describe('control page tests', () => { ReviewPage.clickSubmitButton() ReviewPage.clickConfirmSubmitButton() - cy.get('[name="submission.$title"]').contains('test pdf') + cy.get('[data-testid="submission.$title"]').contains('test pdf') }) }) }) @@ -211,7 +214,7 @@ describe('control page tests', () => { cy.fixture('role_names').then(name => { cy.login(name.role.admin, manuscripts) cy.awaitDisappearSpinner() - ManuscriptsPage.selectOptionWithText('Control') + ManuscriptsPage.clickControlLink() cy.awaitDisappearSpinner() ControlPage.getAssignSeniorEditorDropdown().should('be.visible') }) @@ -223,7 +226,7 @@ describe('control page tests', () => { ControlPage.getHideReviewerNameCheckbox().should('be.checked') cy.fixture('role_names').then(name => { cy.login(name.role.reviewers[1], dashboard) - cy.get('[data-testid="submission.$title"]:last').click() + DashboardPage.clickDoReview() cy.get('[data-testid=tab-container]').contains('Review').click() ControlPage.getReviewerName().should( 'not.contain', @@ -239,7 +242,7 @@ describe('control page tests', () => { ControlPage.getHideReviewerNameCheckbox('should', 'not.be.checked') cy.fixture('role_names').then(name => { cy.login(name.role.reviewers[1], dashboard) - cy.get('[data-testid="submission.$title"]:last').click() + DashboardPage.clickDoReview() cy.get('[data-testid=tab-container]').contains('Review').click() ControlPage.getReviewerName().should('contain', name.role.reviewers[1]) }) @@ -304,7 +307,7 @@ describe('control page tests', () => { cy.awaitDisappearSpinner() DashboardPage.getHeader().should('be.visible') Menu.clickManuscriptsAndAssertPageLoad() - ManuscriptsPage.selectOptionWithText('Control') + ManuscriptsPage.clickControlLink() cy.awaitDisappearSpinner() cy.contains('Tasks & Notifications').click() }) diff --git a/cypress/e2e/b-colab-prc/105-review_and_decision_page_spec.cy.js b/cypress/e2e/b-colab-prc/105-review_and_decision_page_spec.cy.js index b6fae20ff..89f41913b 100644 --- a/cypress/e2e/b-colab-prc/105-review_and_decision_page_spec.cy.js +++ b/cypress/e2e/b-colab-prc/105-review_and_decision_page_spec.cy.js @@ -96,7 +96,9 @@ Cypress.Commands.add( cy.wait(1000) DashboardPage.clickDashboardTab(1) DashboardPage.clickAcceptReviewButton() - cy.contains('button', 'Do Review').should('be.visible') + cy.contains('Accept this review invitation?').should('be.visible') + cy.contains('button', 'OK').click() + DashboardPage.getDoReviewButton().should('contain', 'Do Review') DashboardPage.clickDoReviewAndVerifyPageLoaded() cy.contains('div', 'Metadata').should('be.visible') @@ -121,7 +123,7 @@ Cypress.Commands.add( Cypress.Commands.add('submitDecision', (decisionText, decisionAction) => { cy.awaitDisappearSpinner() - ManuscriptsPage.selectOptionWithText('Control') + ManuscriptsPage.clickControlLink() ControlPage.clickDecisionTab(1) ControlPage.fillInDecision(decisionText) diff --git a/cypress/e2e/b-colab-prc/107-new_manuscript_version_spec.cy.js b/cypress/e2e/b-colab-prc/107-new_manuscript_version_spec.cy.js index 1b95f83c3..854d0bc29 100644 --- a/cypress/e2e/b-colab-prc/107-new_manuscript_version_spec.cy.js +++ b/cypress/e2e/b-colab-prc/107-new_manuscript_version_spec.cy.js @@ -50,7 +50,7 @@ describe('checking manuscript version', () => { cy.wait(1000) cy.login(name.role.author, dashboard) /* Click on first MySubmission */ - DashboardPage.getSubmittedManuscript().click() + DashboardPage.clickSubmissionActionLink() /* Verify Decision Content */ DashboardPage.getDecisionField(0).should('contain', decisionTextContent) @@ -76,9 +76,7 @@ describe('checking manuscript version', () => { SubmissionFormPage.clickSubmitResearch() SubmissionFormPage.clickSubmitYourManuscript() /* Verify new submission got created */ - DashboardPage.getSubmittedManuscript() - .contains('test pdf') - .should('exist') + DashboardPage.getSubmissionTitle().contains('test pdf').should('exist') /* Login as editor and check the new version submission form */ cy.login(name.role.seniorEditor, dashboard) diff --git a/cypress/e2e/c-elife-single_form/202-modify_submission_form_spec.cy.js b/cypress/e2e/c-elife-single_form/202-modify_submission_form_spec.cy.js index c2983ed61..2f39fdfd1 100644 --- a/cypress/e2e/c-elife-single_form/202-modify_submission_form_spec.cy.js +++ b/cypress/e2e/c-elife-single_form/202-modify_submission_form_spec.cy.js @@ -28,7 +28,7 @@ describe('validating required field and doi values in submission form', () => { }) ManuscriptsPage.getTableHead().should('be.visible') Menu.clickManuscripts() - ManuscriptsPage.getOptionsElifeText('Evaluation').click() + ManuscriptsPage.clickEvaluationLink() }) context('check the Submission form based on form builder', () => { @@ -75,7 +75,7 @@ describe('validating required field and doi values in submission form', () => { SubmissionFormPage.clickElifeSubmitResearch() // check for the submission form contains doi - ManuscriptsPage.getOptionsElifeText('Evaluation').click() + ManuscriptsPage.clickEvaluationLink() SubmissionFormPage.getDoi().should( 'have.value', data.doi.split('https://doi.org/')[1], diff --git a/cypress/e2e/c-elife-single_form/203-manuscripts_page_spec.cy.js b/cypress/e2e/c-elife-single_form/203-manuscripts_page_spec.cy.js index c85503be0..11aed82a4 100644 --- a/cypress/e2e/c-elife-single_form/203-manuscripts_page_spec.cy.js +++ b/cypress/e2e/c-elife-single_form/203-manuscripts_page_spec.cy.js @@ -44,7 +44,7 @@ describe('Manuscripts page tests', () => { ManuscriptsPage.getEvaluationButton() .scrollIntoView() .should('be.visible') - ManuscriptsPage.getOptionsElifeText('Publish').should('not.exist') + ManuscriptsPage.getPublishLink().should('not.exist') }) }) @@ -196,8 +196,8 @@ describe('Manuscripts page tests', () => { ManuscriptsPage.getEvaluationButton() .scrollIntoView() .should('be.visible') - ManuscriptsPage.getOptionsElife().should('not.contain', 'Control') - ManuscriptsPage.getOptionsElife().should('contain', 'Publish') + ManuscriptsPage.getControlLink().should('not.exist') + ManuscriptsPage.getPublishLink().should('exist') }) it('submission details should be visible', () => { diff --git a/cypress/e2e/c-elife-single_form/204-publish_submission_form_field.cy.js b/cypress/e2e/c-elife-single_form/204-publish_submission_form_field.cy.js index 9321c3fda..c2d5a53c8 100644 --- a/cypress/e2e/c-elife-single_form/204-publish_submission_form_field.cy.js +++ b/cypress/e2e/c-elife-single_form/204-publish_submission_form_field.cy.js @@ -52,10 +52,12 @@ describe('Update the submission form field', () => { // Verify that changes are autosaved SubmissionFormPage.getTitleField().should('have.value', data.description) SubmissionFormPage.clickSubmitResearchAndWaitPageLoadElife() - ManuscriptsPage.getOptionsElifeText('Evaluation').click() + ManuscriptsPage.clickEvaluationLink() SubmissionFormPage.clickSubmitResearchAndWaitPageLoadElife() cy.intercept('/graphql').as('getResponse') - ManuscriptsPage.getOptionsElifeText('Publish').click() + ManuscriptsPage.clickPublishLink() + cy.contains('Publish this manuscript?').should('be.visible') + cy.contains('button', 'OK').click() cy.wait('@getResponse').its('response').should('deep.include', { statusCode: 200, statusMessage: 'OK', diff --git a/cypress/e2e/ncrc/05-manuscripts_page_spec.cy.js b/cypress/e2e/ncrc/05-manuscripts_page_spec.cy.js index bed4e5063..7e9d79060 100644 --- a/cypress/e2e/ncrc/05-manuscripts_page_spec.cy.js +++ b/cypress/e2e/ncrc/05-manuscripts_page_spec.cy.js @@ -1,4 +1,4 @@ -/* eslint-disable promise/always-return, promise/catch-or-return */ +/* eslint-disable promise/always-return */ /* eslint-disable cypress/no-unnecessary-waiting */ import { manuscripts } from '../../support/routes' diff --git a/cypress/page-object/dashboard-page.js b/cypress/page-object/dashboard-page.js index 6c7bde677..10af84c63 100644 --- a/cypress/page-object/dashboard-page.js +++ b/cypress/page-object/dashboard-page.js @@ -14,7 +14,7 @@ const SUBMISSION_FILE_UPLOAD_INPUT = 'input[type=file]' const SUBMISSION_CREATED = 'Submission created' /* My Submissions */ -const SUBMITTED_MANUSCRIPTS = '[data-testid=clickable-manuscripts-row]' +const SUBMISSION_ACTION_LINK = 'submission-action-link' const CREATE_NEW_VERSION_BUTTON = 'create-new-manuscript-version-button' /* Submitted Info */ @@ -22,29 +22,25 @@ const DECISION_FIELDS = ':nth-child(1) > [data-testid=section] > [data-testid=section] > div' // 'To Review section' -const DO_REVIEW_BUTTON = '[data-testid="reviewerLinks"] button' +const DO_REVIEW_BUTTON = 'review-action-link' const ACCEPT_REVIEW_BUTTON = 'accept-review' const REJECT_REVIEW_BUTTON = 'reject-review' // 'Manuscripts I'm editor of' section -const CONTROL_BUTTON = 'control-panel-team' +const CONTROL_BUTTON = 'control-link' const MANUSCRIPT_NAV_BUTTON = '[href*="/admin/manuscripts"]' const INVITED_REVIEWS_STATUS = 'invited' const COMPLETED_REVIEWS_STATUS = 'completed' const REJECTED_REVIEWS_STATUS = 'rejected' const ACCEPTED_REVIEWS_STATUS = 'accepted' const VERSION_TITLE = 'VersionTitle__Root-sc' -const ARTICLE_LINK = '[data-testid="reviewerLinks"] button' export const DashboardPage = { - getSubmittedManuscript() { - return cy.get(SUBMITTED_MANUSCRIPTS) + getSubmissionActionLink() { + return cy.getByDataTestId(SUBMISSION_ACTION_LINK) }, - getSubmittedManuscripts(nth) { - return cy.get(SUBMITTED_MANUSCRIPTS).eq(nth) - }, - clickSubmittedManuscript(nth) { - this.getSubmittedManuscripts(nth).click() + clickSubmissionActionLink() { + this.getSubmissionActionLink().click() }, getSubmitButton() { return cy.get(BUTTON).contains(SUBMISSION_BUTTON) @@ -77,7 +73,7 @@ export const DashboardPage = { return this.getSubmitButton().click() }, getSectionPlaceholder(nth) { - return cy.getByDataTestId('placeholder').eq(nth) + return cy.getByDataTestId('empty-manuscripts-table-placeholder').eq(nth) }, getCreateNewVersionButton() { return cy.getByDataTestId(CREATE_NEW_VERSION_BUTTON) @@ -118,10 +114,9 @@ export const DashboardPage = { }, clickRejectReviewButton() { this.getRejectReviewButton().click({ force: true }) - cy.reload() }, getDoReviewButton() { - return cy.get(DO_REVIEW_BUTTON) + return cy.getByDataTestId(DO_REVIEW_BUTTON) }, clickDoReview() { this.getDoReviewButton().click({ force: true }) @@ -147,7 +142,7 @@ export const DashboardPage = { return cy.getByContainsClass(VERSION_TITLE) }, getCompletedReviewButton() { - return cy.get(ARTICLE_LINK) + return this.getDoReviewButton() }, clickCompletedReviewButton() { this.getCompletedReviewButton().click({ force: true }) diff --git a/cypress/page-object/manuscripts-page.js b/cypress/page-object/manuscripts-page.js index a10e17f86..695b55bcd 100644 --- a/cypress/page-object/manuscripts-page.js +++ b/cypress/page-object/manuscripts-page.js @@ -15,47 +15,55 @@ const EVALUATION_BUTTON = '[href*=evaluation]' const CREATED_CARET = 'Carets__Caret' const TABLE_HEADER = '[class*=Table__Header]' const ARTICLE_TITLE = '[class*=Table__Row]>td:nth-child(1)' -const ARTICLE_ID = '[name="submission.articleId"]' -const ARTICLE_LABEL = '[name="submission.$customStatus"]' -const DROPDOWN_LABEL = '[data-testid=label-dropdown-base-dropdown]' +const ARTICLE_ID = '[data-testid="submission.articleId"]' +const ARTICLE_LABEL = + '[data-testid="submission.$customStatus"] [data-testid="editable-option-select"]' const ARTICLE_TOPIC = '[class*=Table__Cell] > [title]' -const TABLE_ROW = '[data-testid=manuscripts-row]' +const TABLE_ROW = '.ant-table-tbody .ant-table-row' const TABLE_CELL = 'Table__Cell' -const LABEL = '[name="submission.$customStatus"]' -const CONFIRM_BUTTON = - '[data-testid="confirmation-modal-container"] button:nth-child(1)' -const ARTICLE_CHECKBOX = - '[data-testid=row-item-checkbox]label > [type*=checkbox]' - -const SELECT_ALL_CHECKBOX = '[type=checkbox]' +const ARTICLE_CHECKBOX = '.ant-table-tbody .ant-checkbox-wrapper' +const SELECT_ALL_CHECKBOX = '.ant-table-thead .ant-checkbox-wrapper' const EDITOR_NAME_CELL = 'style__StyledAuthor' -const TOOLTIP_TEXT = 'rc-tooltip-inner' -const ARTICLES_COUNT = '[data-testid=pagination-container] > div > strong' +const ARTICLES_COUNT = '.ant-pagination-total-text strong' const PAGINATION_PAGE_BUTTON = 'Page ' -const CONFIRMATION_MESSAGE = - '[data-testid="confirmation-modal-container"] [data-testid="message-string"]' +const CONFIRMATION_MESSAGE = '.ant-modal-confirm-content' const IMPORT_CONFIRMATION_POPUP = '[class*=Toastify] > [role=alert]' // const CONTROL = '[href*=decision]' const DROPDOWN_OPTION = '[data-testid="select-option"]' export const ManuscriptsPage = { - getManuscriptsOptionsList() { - return cy.getByDataTestId('action-link') + getEvaluationLink() { + return cy.getByDataTestId('evaluation-action-link').first() + }, + clickEvaluationLink() { + this.getEvaluationLink().click() + }, + getControlLink() { + return cy.getByDataTestId('control-action-link') + }, + clickControlLink() { + this.getControlLink().click() }, - selectOptionWithText(text) { - this.getManuscriptsOptionsList().contains(text).click() + getViewLink() { + return cy.getByDataTestId('view-action-link') }, - getOptionWithText(text) { - return this.getManuscriptsOptionsList().contains(text) + clickViewLink() { + this.getViewLink().click() }, - getOptionsElife() { - return cy.getByDataTestId('action-link') + getProductionLink() { + return cy.getByDataTestId('production-action-link') }, - getOptionsElifeText(text) { - return cy.getByDataTestId('action-link').contains(text) + clickProductionLink() { + this.getProductionLink().click() + }, + getPublishLink() { + return cy.getByDataTestId('publish-action-link') + }, + clickPublishLink() { + this.getPublishLink().click() }, getSubmitButton() { return cy.get(BUTTON).contains('New submission') @@ -79,7 +87,7 @@ export const ManuscriptsPage = { return cy.get('h1') }, getEvaluationButton() { - return cy.contains('button', 'Evaluation') + return this.getEvaluationLink() }, getNthEvaluationButton(nth) { return cy.get(EVALUATION_BUTTON).eq(nth) @@ -127,7 +135,7 @@ export const ManuscriptsPage = { return cy.getByContainsClass(CREATED_CARET).eq(nth) }, getAuthorField(nth) { - return cy.getByDataTestId('usercombo-primary').eq(nth) + return cy.getByDataTestId('person-name').eq(nth) }, getAuthor(nth) { return this.getAuthorField(nth).invoke('text') @@ -138,11 +146,8 @@ export const ManuscriptsPage = { getStatus(nth) { return this.getStatusField(nth).invoke('text') }, - clickStatus(nth) { - this.getStatusField(nth).click() - }, getTableHead() { - return cy.getByDataTestId('manuscripts-header-row') + return cy.get('.ant-table-thead') }, getArticleTitleByRow(nth) { return cy.get(ARTICLE_TITLE).eq(nth) @@ -151,18 +156,17 @@ export const ManuscriptsPage = { return cy.get(ARTICLE_ID).eq(nth) }, clickArticleId() { - return this.getArticleIdByRow(0).click() + return this.getArticleIdByRow(0).scrollIntoView().click() }, - clickTableHead(nth) { - this.getTableHead(nth).click() + filterColumnByValue(columnKey, valueLabel) { + cy.get(`.ant-table-thead [data-testid="${columnKey}"]`) + .find('.ant-table-filter-trigger') + .click() + cy.get('.ant-table-filter-dropdown').contains(valueLabel).click() + cy.get('.ant-table-filter-dropdown').contains('button', 'OK').click() }, selectCustomStatus(statusLabel) { - cy.get( - '[data-testid=manuscripts-header-row] [data-testid="submission.$customStatus"]', - ) - // .scrollIntoView() - .click() - cy.get('[data-testid="select-option"]').contains(statusLabel).click() + this.filterColumnByValue('submission.$customStatus', statusLabel) }, getArticleLabel() { return cy.get(ARTICLE_LABEL) @@ -170,6 +174,12 @@ export const ManuscriptsPage = { clickArticleLabel(nth) { this.getArticleLabel().eq(nth).click() }, + clickArticleLabelClear(nth) { + this.getArticleLabel() + .eq(nth) + .find('.ant-select-clear') + .click({ force: true }) + }, getAllArticleTopics() { return cy.get(ARTICLE_TOPIC) }, @@ -211,7 +221,7 @@ export const ManuscriptsPage = { return cy.getByContainsClass(TABLE_CELL).eq(1) }, getLabelRow(nth) { - return cy.get(LABEL).eq(nth) + return cy.get(ARTICLE_LABEL).eq(nth) }, getTableHeader() { return cy.get(TABLE_HEADER, { timeout: 15000 }) @@ -234,38 +244,32 @@ export const ManuscriptsPage = { getSelectedArticlesCount() { return cy.getByDataTestId('selected-manuscripts-number').invoke('text') }, - getActionDropdown() { - return cy.getByDataTestId('manuscripts-dropdown-container').click() - }, clickDelete() { - this.getActionDropdown().contains('Archive').click() + cy.contains('button', /^Archive$/).click() }, getConfirmButton() { - return cy.get(CONFIRM_BUTTON) - }, - getLabelDropdown() { - return cy.get(DROPDOWN_LABEL).eq(0) + return cy.contains('button', 'OK') }, clickConfirm() { - this.getConfirmButton().click() + this.getConfirmButton().should('be.visible').click() }, getConfirmationMessageForBulkDelete() { return cy.get(CONFIRMATION_MESSAGE) }, getCloseButton() { - return cy.get(BUTTON).contains('Cancel') + return cy.contains('button', 'Cancel') }, clickClose() { - this.getCloseButton().click() + this.getCloseButton().should('be.visible').click() }, getEditorName() { return cy.getByContainsClass(EDITOR_NAME_CELL) }, getTooltipIcon() { - return cy.getByDataTestId('info-icon') + return cy.getByDataTestId('abstract-tooltip-icon') }, getTooltipText() { - return cy.getByContainsClass(TOOLTIP_TEXT) + return cy.getByDataTestId('abstract-tooltip') }, getNumberOfAvailableArticles() { return cy.get(ARTICLES_COUNT).eq(-1) @@ -276,12 +280,6 @@ export const ManuscriptsPage = { clickPaginationButton(nth) { this.getPaginationButton(nth).click({ force: true }) }, - getSelectButton() { - return cy.get(BUTTON).contains('Select') - }, - clickSelect() { - this.getSelectButton().click() - }, getSuccessfulImportPopup() { return cy.get(IMPORT_CONFIRMATION_POPUP, { timeout: 600000 }) }, diff --git a/docker-compose.yml b/docker-compose.yml index 74d67882c..cba8530d2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -144,6 +144,8 @@ services: - ./packages/server/utils:/home/node/app/utils - ./packages/server/profiles:/home/node/app/profiles - ./packages/server/vitest.config.ts:/home/node/app/vitest.config.ts + - ./packages/server/vitest.setup.ts:/home/node/app/vitest.setup.ts + - ./packages/server/vitest.workerDb.ts:/home/node/app/vitest.workerDb.ts healthcheck: test: ['CMD', '/bin/sh', '/home/node/app/scripts/server_healthy.sh'] interval: 1s diff --git a/packages/client/app/components/component-chat/src/EllipsisDropdown.jsx b/packages/client/app/components/component-chat/src/EllipsisDropdown.jsx index 9e3fe61ac..65103895a 100644 --- a/packages/client/app/components/component-chat/src/EllipsisDropdown.jsx +++ b/packages/client/app/components/component-chat/src/EllipsisDropdown.jsx @@ -41,9 +41,14 @@ const EllipsisDropdown = ({ } } - window.addEventListener('click', handleClickOutside) + // Deferred so the click that opened the dropdown (still bubbling to + // window at mount time) doesn't immediately close it again. + const timeoutId = setTimeout(() => { + window.addEventListener('click', handleClickOutside) + }, 0) return () => { + clearTimeout(timeoutId) window.removeEventListener('click', handleClickOutside) } }, [show]) diff --git a/packages/client/app/components/component-chat/src/MessageContainer.jsx b/packages/client/app/components/component-chat/src/MessageContainer.jsx index f7df9c365..86adec55f 100644 --- a/packages/client/app/components/component-chat/src/MessageContainer.jsx +++ b/packages/client/app/components/component-chat/src/MessageContainer.jsx @@ -16,7 +16,7 @@ const MessageContainer = styled.section` background: rgb(255 255 255); display: flex; flex-direction: column; - height: 100vh; + height: 100%; ${props => props.$channels diff --git a/packages/client/app/components/component-chat/src/Messages/style.jsx b/packages/client/app/components/component-chat/src/Messages/style.jsx index 45d913b13..7d745bc4f 100644 --- a/packages/client/app/components/component-chat/src/Messages/style.jsx +++ b/packages/client/app/components/component-chat/src/Messages/style.jsx @@ -217,7 +217,7 @@ export const UnreadLabel = styled(ChatLabel)` export const DateLabelContainer = styled(ChatLabelContainer)` border-top: 2px solid #6c6c6c; - margin: 40px 0 30px; + margin: ${grid(5)} ${grid(2)}; ` export const DateLabel = styled(ChatLabel)` background-color: #fff; diff --git a/packages/client/app/components/component-cms-manager/src/collection/CollectionList.jsx b/packages/client/app/components/component-cms-manager/src/collection/CollectionList.jsx index 38f4c30b3..310e69394 100644 --- a/packages/client/app/components/component-cms-manager/src/collection/CollectionList.jsx +++ b/packages/client/app/components/component-cms-manager/src/collection/CollectionList.jsx @@ -14,9 +14,9 @@ import { ActionButton, PaddedContent, SectionContent, - LabelBadge, } from '../../../shared' import Page from '../../../../ui/shared/Page' +import Badge from '../../../../ui/shared/Badge' import CollectionModalForm from './CollectionModalForm' const StyledContent = styled.div` @@ -184,9 +184,9 @@ const CollectionList = ({ component: ({ collection }) => { return collection && collection.active ? ( - True + True ) : ( - False + False ) }, }, diff --git a/packages/client/app/components/component-config-manager/src/ConfigManagerForm.jsx b/packages/client/app/components/component-config-manager/src/ConfigManagerForm.jsx index 069d3c47b..e76f746a9 100644 --- a/packages/client/app/components/component-config-manager/src/ConfigManagerForm.jsx +++ b/packages/client/app/components/component-config-manager/src/ConfigManagerForm.jsx @@ -24,7 +24,6 @@ import emailTemplatesToSchema from './helpers' import { EmailTemplatesProvider } from '../../component-email-templates/hooks/EmailTemplatesContext' import NotificationPage from '../../component-notification-event/NotificationPage' import { T } from '../../component-notification-event/misc/constants' -import { getFormBadgeBg } from '../../component-email-templates/src/handlebarsAutocomplete/helpers' import DescriptionField from './ui/DescriptionField' const StyledContainer = styled.div` @@ -105,8 +104,9 @@ const EmailsTabWrapper = styled(StyledSectionContent)` padding: ${grid(2)} ${grid(4)}; span.handlebars { - background-color: ${getFormBadgeBg('common')}; - border-radius: 5px; + background-color: ${th('colorSecondary')}; + color: ${th('colorTextReverse')}; + border-radius: 3px; box-shadow: 0 0 6px 0 #0001, inset 0 0 4px 0 #0002; @@ -114,22 +114,6 @@ const EmailsTabWrapper = styled(StyledSectionContent)` padding: 2px 4px; text-rendering: geometricprecision; } - - span.handlebars.submission-form { - background-color: ${getFormBadgeBg('submission')}; - } - - span.handlebars.review-form { - background-color: ${getFormBadgeBg('review')}; - } - - span.handlebars.decision-form { - background-color: ${getFormBadgeBg('decision')}; - } - - span.handlebars.editors-form { - background-color: ${getFormBadgeBg('editors')}; - } } .wax-surface-scroll { diff --git a/packages/client/app/components/component-dashboard/src/components/DashboardEditsPage.jsx b/packages/client/app/components/component-dashboard/src/components/DashboardEditsPage.jsx index 8da161414..fe7b37564 100644 --- a/packages/client/app/components/component-dashboard/src/components/DashboardEditsPage.jsx +++ b/packages/client/app/components/component-dashboard/src/components/DashboardEditsPage.jsx @@ -1,118 +1,23 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { useMutation, useQuery } from '@apollo/client/react' -import { useEffect, useContext } from 'react' -import { useLocation } from 'react-router-dom' +import { useMutation } from '@apollo/client/react' +import { useEffect } from 'react' -import { ConfigContext } from '../../../config/src' -import { useCurrentUser } from '../../../../pages/hooks/useCurrentUser' -import { - extractFilters, - extractSortData, - URI_PAGENUM_PARAM, - useQueryParams, -} from '../../../../shared/urlParamUtils' -import { - UPDATE_TAB, - REMOVE_TASK_ALERTS_FOR_CURRENT_USER, - DASHBOARD, - UPDATE_MANUSCRIPT, -} from '../../../../queries' +import { UPDATE_TAB } from '../../../../queries' import EditorTable from './sections/EditorTable' -import { CommsErrorBanner, Spinner } from '../../../shared' const DashboardEditsPage = () => { - const location = useLocation() - const config = useContext(ConfigContext) - const wantedRoles = ['seniorEditor', 'handlingEditor', 'editor'] - - const currentUser = useCurrentUser() - - const applyQueryParams = useQueryParams() - - const uriQueryParams = new URLSearchParams(location.search) - const page = uriQueryParams.get(URI_PAGENUM_PARAM) || 1 - const sortName = extractSortData(uriQueryParams).name - const sortDirection = extractSortData(uriQueryParams).direction - const filters = extractFilters(uriQueryParams) - - const limit = config?.manuscript?.paginationCount || 10 - - const { data, loading, error } = useQuery(DASHBOARD, { - variables: { - wantedRoles, - sort: sortName - ? { field: sortName, isAscending: sortDirection === 'ASC' } - : null, - offset: (page - 1) * limit, - limit, - filters, - timezoneOffsetMinutes: new Date().getTimezoneOffset(), - groupId: config.groupId, - searchInAllVersions: false, - }, - fetchPolicy: 'network-only', - }) - const [updateTab] = useMutation(UPDATE_TAB) - const [update] = useMutation(UPDATE_MANUSCRIPT) - const [doUpdateManuscript] = useMutation(UPDATE_MANUSCRIPT) - - const setReadyToEvaluateLabels = id => { - update({ - variables: { - id, - input: JSON.stringify({ - submission: { - $customStatus: 'readyToEvaluate', - }, - }), - }, - }) - } - - const unsetCustomStatus = id => { - update({ - variables: { - id, - input: JSON.stringify({ - submission: { - $customStatus: null, - }, - }), - }, - }) - } - - const [removeTaskAlertsForCurrentUser] = useMutation( - REMOVE_TASK_ALERTS_FOR_CURRENT_USER, - ) - useEffect(() => { updateTab({ variables: { tab: 'edits', }, }) - removeTaskAlertsForCurrentUser() }, []) - if (loading) return - if (error) return - - return ( - - ) + return } export default DashboardEditsPage diff --git a/packages/client/app/components/component-dashboard/src/components/DashboardLayout.jsx b/packages/client/app/components/component-dashboard/src/components/DashboardLayout.jsx index 8cd886172..7ea7838ab 100644 --- a/packages/client/app/components/component-dashboard/src/components/DashboardLayout.jsx +++ b/packages/client/app/components/component-dashboard/src/components/DashboardLayout.jsx @@ -9,13 +9,7 @@ import { useTranslation } from 'react-i18next' import { Button } from '../../../pubsweet' import { HiddenTabsContainer, Tab, TabContainer } from '../../../shared' import Page from '../../../../ui/shared/Page' -import SearchControl from '../../../component-manuscripts/src/SearchControl' import { ControlsContainer } from '../../../component-manuscripts/src/style' -import { - URI_PAGENUM_PARAM, - URI_SEARCH_PARAM, - useQueryParams, -} from '../../../../shared/urlParamUtils' import { FlexRow } from '../../../../globals' import { ConfigContext } from '../../../config/src' @@ -43,9 +37,6 @@ const DashboardLayout = ({ children }) => { const navigate = useNavigate() const location = useLocation() const { groupName } = useParams() - const applyQueryParams = useQueryParams() - const uriQueryParams = new URLSearchParams(location.search) - const currentSearchQuery = uriQueryParams.get(URI_SEARCH_PARAM) const dashboardPages = [] const { t } = useTranslation() @@ -85,15 +76,6 @@ const DashboardLayout = ({ children }) => { ))} - - applyQueryParams({ - [URI_SEARCH_PARAM]: newQuery, - [URI_PAGENUM_PARAM]: 1, - }) - } - currentSearchQuery={currentSearchQuery} - />