Add Submission e2e tests, enhance MyDSpace tests. Minor cleanup to support tests

This commit is contained in:
Tim Donohue
2022-02-09 14:03:50 -06:00
parent fe0e414343
commit 53042179f0
10 changed files with 200 additions and 20 deletions

View File

@@ -16,8 +16,9 @@
"DSPACE_TEST_ADMIN_PASSWORD": "dspace", "DSPACE_TEST_ADMIN_PASSWORD": "dspace",
"DSPACE_TEST_COMMUNITY": "0958c910-2037-42a9-81c7-dca80e3892b4", "DSPACE_TEST_COMMUNITY": "0958c910-2037-42a9-81c7-dca80e3892b4",
"DSPACE_TEST_COLLECTION": "282164f5-d325-4740-8dd1-fa4d6d3e7200", "DSPACE_TEST_COLLECTION": "282164f5-d325-4740-8dd1-fa4d6d3e7200",
"DSPACE_TEST_COLLECTION_NAME": "Sample Collection",
"DSPACE_TEST_ENTITY_PUBLICATION": "e98b0f27-5c19-49a0-960d-eb6ad5287067", "DSPACE_TEST_ENTITY_PUBLICATION": "e98b0f27-5c19-49a0-960d-eb6ad5287067",
"DSPACE_TEST_SEARCH_TERM": "test" "DSPACE_TEST_SEARCH_TERM": "test",
"DSPACE_TEST_SUBMIT_COLLECTION_NAME": "Sample Collection",
"DSPACE_TEST_SUBMIT_COLLECTION_UUID": "9d8334e9-25d3-4a67-9cea-3dffdef80144"
} }
} }

View File

@@ -1,5 +1,5 @@
import { Options } from 'cypress-axe'; import { Options } from 'cypress-axe';
import { TEST_ADMIN_USER, TEST_ADMIN_PASSWORD, TEST_COLLECTION_NAME } from 'cypress/support'; import { TEST_ADMIN_USER, TEST_ADMIN_PASSWORD, TEST_SUBMIT_COLLECTION_NAME } from 'cypress/support';
import { testA11y } from 'cypress/support/utils'; import { testA11y } from 'cypress/support/utils';
describe('My DSpace page', () => { describe('My DSpace page', () => {
@@ -37,7 +37,7 @@ describe('My DSpace page', () => {
it('should have a working detailed view that passes accessibility tests', () => { it('should have a working detailed view that passes accessibility tests', () => {
cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD); cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD);
cy.visit('/mydspace'); cy.visit('/mydspace');
cy.get('ds-my-dspace-page').should('exist'); cy.get('ds-my-dspace-page').should('exist');
@@ -59,8 +59,8 @@ describe('My DSpace page', () => {
); );
}); });
// NOTE: Deleting existing submissions is exercised by submission.spec.ts
it('should let you start a new submission', () => { it('should let you start a new submission & edit in-progress submissions', () => {
cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD); cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD);
cy.visit('/mydspace'); cy.visit('/mydspace');
@@ -73,10 +73,10 @@ describe('My DSpace page', () => {
cy.get('ds-create-item-parent-selector').should('be.visible'); cy.get('ds-create-item-parent-selector').should('be.visible');
// Type in a known Collection name in the search box // Type in a known Collection name in the search box
cy.get('ds-authorized-collection-selector input[type="search"]').type(TEST_COLLECTION_NAME); cy.get('ds-authorized-collection-selector input[type="search"]').type(TEST_SUBMIT_COLLECTION_NAME);
// Click on the button matching that known Collection name // Click on the button matching that known Collection name
cy.get('ds-authorized-collection-selector button[title="' + TEST_COLLECTION_NAME + '"]').click(); cy.get('ds-authorized-collection-selector button[title="' + TEST_SUBMIT_COLLECTION_NAME + '"]').click();
// New URL should include /workspaceitems, as we've started a new submission // New URL should include /workspaceitems, as we've started a new submission
cy.url().should('include', '/workspaceitems'); cy.url().should('include', '/workspaceitems');
@@ -84,8 +84,44 @@ describe('My DSpace page', () => {
// The Submission edit form tag should be visible // The Submission edit form tag should be visible
cy.get('ds-submission-edit').should('be.visible'); cy.get('ds-submission-edit').should('be.visible');
// A Collection menu button should exist & it's value should be the selected collection // A Collection menu button should exist & its value should be the selected collection
cy.get('#collectionControlsMenuButton span').should('have.text', TEST_COLLECTION_NAME); cy.get('#collectionControlsMenuButton span').should('have.text', TEST_SUBMIT_COLLECTION_NAME);
// Now that we've created a submission, we'll test that we can go back and Edit it.
// Get our Submission URL, to parse out the ID of this new submission
cy.location().then(fullUrl => {
// This will be the full path (/workspaceitems/[id]/edit)
const path = fullUrl.pathname;
// Split on the slashes
const subpaths = path.split('/');
// Part 2 will be the [id] of the submission
const id = subpaths[2];
// Go back to the MyDSpace page
cy.visit('/mydspace');
// This is the GET command that will actually run the search
cy.intercept('GET', '/server/api/discover/search/objects*').as('search-results');
// On MyDSpace, find the submission we just created via its ID
cy.get('[data-e2e="search-box"]').type(id);
cy.get('[data-e2e="search-button"]').click();
// Wait for search results to come back from the above GET command
cy.wait('@search-results');
// Click the Edit button for this in-progress submission
cy.get('#edit_' + id).click();
// Should send us back to the submission form
cy.url().should('include', '/workspaceitems/' + id + '/edit');
// Discard our new submission by clicking Discard in Submission form & confirming
cy.get('button#discard').click();
cy.get('button#discard_submit').click();
// Discarding should send us back to MyDSpace
cy.url().should('include', '/mydspace');
});
}); });
it('should let you import from external sources', () => { it('should let you import from external sources', () => {

View File

@@ -0,0 +1,134 @@
import { Options } from 'cypress-axe';
import { TEST_ADMIN_USER, TEST_ADMIN_PASSWORD, TEST_SUBMIT_COLLECTION_NAME, TEST_SUBMIT_COLLECTION_UUID } from 'cypress/support';
import { testA11y } from 'cypress/support/utils';
describe('New Submission page', () => {
// NOTE: We already test that new submissions can be started from MyDSpace in my-dspace.spec.ts
it('should create a new submission when using /submit path', () => {
cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD);
// Test that calling /submit with collection & entityType will create a new submission
cy.visit('/submit?collection=' + TEST_SUBMIT_COLLECTION_UUID + '&entityType=none');
// Should redirect to /workspaceitems, as we've started a new submission
cy.url().should('include', '/workspaceitems');
// The Submission edit form tag should be visible
cy.get('ds-submission-edit').should('be.visible');
// A Collection menu button should exist & it's value should be the selected collection
cy.get('#collectionControlsMenuButton span').should('have.text', TEST_SUBMIT_COLLECTION_NAME);
// 4 sections should be visible by default
cy.get('div#section_traditionalpageone').should('be.visible');
cy.get('div#section_traditionalpagetwo').should('be.visible');
cy.get('div#section_upload').should('be.visible');
cy.get('div#section_license').should('be.visible');
// Discard button should work
// Clicking it will display a confirmation, which we will confirm with another click
cy.get('button#discard').click();
cy.get('button#discard_submit').click();
});
it('should block submission & show errors if required fields are missing', () => {
cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD);
// Create a new submission
cy.visit('/submit?collection=' + TEST_SUBMIT_COLLECTION_UUID + '&entityType=none');
// Attempt an immediate deposit without filling out any fields
cy.get('button#deposit').click();
// A warning alert should display.
cy.get('ds-notification div.alert-warning').should('be.visible');
// First section should have an exclamation error in the header
// (as it has required fields)
cy.get('div#traditionalpageone-header i.fa-exclamation-circle').should('be.visible');
// Title field should have class "is-invalid" applied, as it's required
cy.get('input#dc_title').should('have.class', 'is-invalid');
// Date Year field should also have "is-valid" class
cy.get('input#dc_date_issued_year').should('have.class', 'is-invalid');
// FINALLY, cleanup after ourselves. This also exercises the MyDSpace delete button.
// Get our Submission URL, to parse out the ID of this submission
cy.location().then(fullUrl => {
// This will be the full path (/workspaceitems/[id]/edit)
const path = fullUrl.pathname;
// Split on the slashes
const subpaths = path.split('/');
// Part 2 will be the [id] of the submission
const id = subpaths[2];
// Even though form is incomplete, the "Save for Later" button should still work
cy.get('button#saveForLater').click();
// "Save for Later" should send us to MyDSpace
cy.url().should('include', '/mydspace');
// A success alert should be visible
cy.get('ds-notification div.alert-success').should('be.visible');
// Now, dismiss any open alert boxes (may be multiple, as tests run quickly)
cy.get('[data-dismiss="alert"]').click({multiple: true});
// This is the GET command that will actually run the search
cy.intercept('GET', '/server/api/discover/search/objects*').as('search-results');
// On MyDSpace, find the submission we just saved via its ID
cy.get('[data-e2e="search-box"]').type(id);
cy.get('[data-e2e="search-button"]').click();
// Wait for search results to come back from the above GET command
cy.wait('@search-results');
// Delete our created submission & confirm deletion
cy.get('button#delete_' + id).click();
cy.get('button#delete_confirm').click();
});
});
it('should allow for deposit if all required fields completed & file uploaded', () => {
cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD);
// Create a new submission
cy.visit('/submit?collection=' + TEST_SUBMIT_COLLECTION_UUID + '&entityType=none');
// Fill out all required fields (Title, Date)
cy.get('input#dc_title').type('DSpace logo uploaded via e2e tests');
cy.get('input#dc_date_issued_year').type('2022');
// Confirm the required license by checking checkbox
// (NOTE: requires "force:true" cause Cypress claims this checkbox is covered by its own <span>)
cy.get('input#granted').check( {force: true} );
// Before using Cypress drag & drop, we have to manually trigger the "dragover" event.
// This ensures our UI displays the dropzone that covers the entire submission page.
// (For some reason Cypress drag & drop doesn't trigger this even itself & upload won't work without this trigger)
cy.get('ds-uploader').trigger('dragover');
// This is the POST command that will upload the file
cy.intercept('POST', '/server/api/submission/workspaceitems/*').as('upload');
// Upload our DSpace logo via drag & drop onto submission form
// cy.get('div#section_upload')
cy.get('div.ds-document-drop-zone').selectFile('src/assets/images/dspace-logo.png', {
action: 'drag-drop'
});
// Wait for upload to complete before proceeding
cy.wait('@upload');
// Close the upload success notice
cy.get('[data-dismiss="alert"]').click({multiple: true});
// Wait for deposit button to not be disabled & click it.
cy.get('button#deposit').should('not.be.disabled').click();
// No warnings should exist. Instead, just successful deposit alert is displayed
cy.get('ds-notification div.alert-warning').should('not.exist');
cy.get('ds-notification div.alert-success').should('be.visible');
});
});

View File

@@ -32,8 +32,8 @@ before(() => {
// Borrowed from: https://glebbahmutov.com/blog/visit-blank-page-between-tests/ // Borrowed from: https://glebbahmutov.com/blog/visit-blank-page-between-tests/
afterEach(() => { afterEach(() => {
cy.window().then((win) => { cy.window().then((win) => {
win.location.href = 'about:blank' win.location.href = 'about:blank';
}) });
}); });
@@ -53,4 +53,4 @@ export const TEST_ENTITY_PUBLICATION = Cypress.env('DSPACE_TEST_ENTITY_PUBLICATI
export const TEST_SEARCH_TERM = Cypress.env('DSPACE_TEST_SEARCH_TERM') || 'test'; export const TEST_SEARCH_TERM = Cypress.env('DSPACE_TEST_SEARCH_TERM') || 'test';
// Collection used for submission tests // Collection used for submission tests
export const TEST_SUBMIT_COLLECTION_NAME = Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_NAME') || 'Sample Collection'; export const TEST_SUBMIT_COLLECTION_NAME = Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_NAME') || 'Sample Collection';
export const TEST_SUBMIT_COLLECTION_UUID = Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_UUID') || '9d8334e9-25d3-4a67-9cea-3dffdef80144'; export const TEST_SUBMIT_COLLECTION_UUID = Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_UUID') || '9d8334e9-25d3-4a67-9cea-3dffdef80144';

View File

@@ -5,6 +5,7 @@
</legend> </legend>
<ds-number-picker <ds-number-picker
tabindex="1" tabindex="1"
[id]="model.id + '_year'"
[disabled]="model.disabled" [disabled]="model.disabled"
[min]="minYear" [min]="minYear"
[max]="maxYear" [max]="maxYear"
@@ -21,6 +22,7 @@
<ds-number-picker <ds-number-picker
tabindex="2" tabindex="2"
[id]="model.id + '_month'"
[min]="minMonth" [min]="minMonth"
[max]="maxMonth" [max]="maxMonth"
[name]="'month'" [name]="'month'"
@@ -36,6 +38,7 @@
<ds-number-picker <ds-number-picker
tabindex="3" tabindex="3"
[id]="model.id + '_day'"
[min]="minDay" [min]="minDay"
[max]="maxDay" [max]="maxDay"
[name]="'day'" [name]="'day'"

View File

@@ -1,4 +1,5 @@
<a class="btn btn-primary mt-1 mb-3" <a class="btn btn-primary mt-1 mb-3"
id="{{'edit_' + object.id}}"
ngbTooltip="{{'submission.workflow.generic.edit-help' | translate}}" ngbTooltip="{{'submission.workflow.generic.edit-help' | translate}}"
[routerLink]="['/workspaceitems/' + object.id + '/edit']" [routerLink]="['/workspaceitems/' + object.id + '/edit']"
role="button"> role="button">
@@ -6,6 +7,7 @@
</a> </a>
<button type="button" <button type="button"
id="{{'delete_' + object.id}}"
class="btn btn-danger mt-1 mb-3" class="btn btn-danger mt-1 mb-3"
ngbTooltip="{{'submission.workflow.generic.delete-help' | translate}}" ngbTooltip="{{'submission.workflow.generic.delete-help' | translate}}"
(click)="$event.preventDefault();confirmDiscard(content)"> (click)="$event.preventDefault();confirmDiscard(content)">
@@ -16,7 +18,7 @@
<ng-template #content let-c="close" let-d="dismiss"> <ng-template #content let-c="close" let-d="dismiss">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title text-danger">{{'submission.general.discard.confirm.title' | translate}}</h4> <h4 class="modal-title text-danger">{{'submission.general.discard.confirm.title' | translate}}</h4>
<button type="button" class="close" aria-label="Close" (click)="d('cancel')"> <button type="button" id="delete_close" class="close" aria-label="Close" (click)="d('cancel')">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
@@ -24,7 +26,7 @@
<p>{{'submission.general.discard.confirm.info' | translate}}</p> <p>{{'submission.general.discard.confirm.info' | translate}}</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="c('cancel')">{{'submission.general.discard.confirm.cancel' | translate}}</button> <button type="button" id="delete_cancel" class="btn btn-secondary" (click)="c('cancel')">{{'submission.general.discard.confirm.cancel' | translate}}</button>
<button type="button" class="btn btn-danger" (click)="c('ok')">{{'submission.general.discard.confirm.submit' | translate}}</button> <button type="button" id="delete_confirm"class="btn btn-danger" (click)="c('ok')">{{'submission.general.discard.confirm.submit' | translate}}</button>
</div> </div>
</ng-template> </ng-template>

View File

@@ -9,6 +9,7 @@
<span class="sr-only">Increment</span> <span class="sr-only">Increment</span>
</button> </button>
<input <input
id="{{id}}"
type="text" type="text"
class="form-control d-inline-block text-center" class="form-control d-inline-block text-center"
maxlength="{{size}}" maxlength="{{size}}"
@@ -22,7 +23,7 @@
[readonly]="disabled" [readonly]="disabled"
[disabled]="disabled" [disabled]="disabled"
[ngClass]="{'is-invalid': invalid}" [ngClass]="{'is-invalid': invalid}"
aria-label="name" title="{{placeholder}}"
> >
<button <button
class="btn btn-link-focus" class="btn btn-link-focus"

View File

@@ -48,6 +48,7 @@ describe('NumberPickerComponent test suite', () => {
[disabled]="disabled" [disabled]="disabled"
[min]="min" [min]="min"
[max]="max" [max]="max"
[id]="'ds_test_field'"
[name]="'test'" [name]="'test'"
[size]="size" [size]="size"
[(ngModel)]="initValue" [(ngModel)]="initValue"

View File

@@ -12,7 +12,7 @@ import { isEmpty } from '../empty.util';
}) })
export class NumberPickerComponent implements OnInit, ControlValueAccessor { export class NumberPickerComponent implements OnInit, ControlValueAccessor {
@Input() id: string;
@Input() step: number; @Input() step: number;
@Input() min: number; @Input() min: number;
@Input() max: number; @Input() max: number;

View File

@@ -2,6 +2,7 @@
<div class="col"> <div class="col">
<button *ngIf="(showDepositAndDiscard | async)" <button *ngIf="(showDepositAndDiscard | async)"
type="button" type="button"
id="discard"
class="btn btn-danger" class="btn btn-danger"
[disabled]="(processingSaveStatus | async) || (processingDepositStatus | async)" [disabled]="(processingSaveStatus | async) || (processingDepositStatus | async)"
(click)="$event.preventDefault();confirmDiscard(content)"> (click)="$event.preventDefault();confirmDiscard(content)">
@@ -40,6 +41,7 @@
</button> </button>
<button *ngIf="(showDepositAndDiscard | async)" <button *ngIf="(showDepositAndDiscard | async)"
type="button" type="button"
id="deposit"
class="btn btn-success" class="btn btn-success"
[disabled]="(processingSaveStatus | async) || (processingDepositStatus | async)" [disabled]="(processingSaveStatus | async) || (processingDepositStatus | async)"
(click)="deposit($event)"> (click)="deposit($event)">
@@ -60,7 +62,7 @@
<p>{{'submission.general.discard.confirm.info' | translate}}</p> <p>{{'submission.general.discard.confirm.info' | translate}}</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="c('cancel')">{{'submission.general.discard.confirm.cancel' | translate}}</button> <button type="button" id="discard_cancel" class="btn btn-secondary" (click)="c('cancel')">{{'submission.general.discard.confirm.cancel' | translate}}</button>
<button type="button" class="btn btn-danger" (click)="c('ok')">{{'submission.general.discard.confirm.submit' | translate}}</button> <button type="button" id="discard_submit" class="btn btn-danger" (click)="c('ok')">{{'submission.general.discard.confirm.submit' | translate}}</button>
</div> </div>
</ng-template> </ng-template>