Skip to content

Commit

Permalink
Initial version of creating test data. Works but crashes basic tests …
Browse files Browse the repository at this point in the history
…like header/footer
  • Loading branch information
tdonohue committed Sep 8, 2023
1 parent 7fd548b commit 80a3e41
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 43 deletions.
10 changes: 10 additions & 0 deletions cypress/fixtures/new_community.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"type":{"value":"community"},
"metadata":{
"dc.title":[{"value":"e2e testing Community"}],
"dc.description":[{"value":"This is the introductory text for a test community"}],
"dc.description.abstract":[{"value":"All communities need a short description"}],
"dc.rights":[{"value":"This community includes a custom license that you must agree to."}],
"dc.description.tableofcontents":[{"value":"Great news! This Community is used for e2e testing and can be deleted safely."}]
}
}
12 changes: 12 additions & 0 deletions cypress/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const fs = require('fs');
let REST_BASE_URL: string;
let REST_DOMAIN: string;

let E2E_TEST_COMMUNITIES = [];

// Plugins enable you to tap into, modify, or extend the internal behavior of Cypress
// For more info, visit https://on.cypress.io/plugins-api
module.exports = (on, config) => {
Expand Down Expand Up @@ -54,6 +56,16 @@ module.exports = (on, config) => {
// Retrieve currently saved value of REST Domain
getRestBaseDomain() {
return REST_DOMAIN ;
},

saveCommunityData(data: string): number {
return E2E_TEST_COMMUNITIES.push(data);
},
getCommunityData(): string[] {
return E2E_TEST_COMMUNITIES;
},
resetCommunityData() {
return (E2E_TEST_COMMUNITIES = []);
}
});
};
207 changes: 164 additions & 43 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import { AuthTokenInfo, TOKENITEM } from 'src/app/core/auth/models/auth-token-info.model';
import { DSPACE_XSRF_COOKIE, XSRF_REQUEST_HEADER } from 'src/app/core/xsrf/xsrf.constants';
import { v4 as uuidv4 } from 'uuid';
import { TEST_ADMIN_PASSWORD, TEST_ADMIN_USER } from './e2e';

// Flag to mark e2e content to be deleted/cleaned up
const TYPE_E2E_CONTENT = 'e2e-content-to-delete';

// Declare Cypress namespace to help with Intellisense & code completion in IDEs
// ALL custom commands MUST be listed here for code completion to work
Expand Down Expand Up @@ -43,7 +47,21 @@ declare global {
* in chainable in order to allow it to be sent also in required CSRF header.
* @returns Chainable reference to allow CSRF token to also be sent in header.
*/
createCSRFCookie(): Chainable<any>;
createCSRFCookie(): Chainable<string>;

/**
* Create a new DSpace Community with given name and optional parent Community index.
*
* If name is not specified, a default name is given.
* @param name name of Community
* @param parentIndex index of a previously created community to create this community under.
*/
createCommunity(name?: string, parentIndex?: number): Chainable<any>;

/**
* Delete any test content created
*/
cleanupTestContent(): typeof cleanupTestContent;
}
}
}
Expand All @@ -58,30 +76,32 @@ declare global {
*/
function login(email: string, password: string): void {
// Create a fake CSRF cookie/token to use in POST
cy.createCSRFCookie().then((csrfToken: string) => {
// get our REST API's base URL, also needed for POST
cy.task('getRestBaseURL').then((baseRestUrl: string) => {
// Now, send login POST request including that CSRF token
cy.request({
method: 'POST',
url: baseRestUrl + '/api/authn/login',
headers: { [XSRF_REQUEST_HEADER]: csrfToken},
form: true, // indicates the body should be form urlencoded
body: { user: email, password: password }
}).then((resp) => {
// We expect a successful login
expect(resp.status).to.eq(200);
// We expect to have a valid authorization header returned (with our auth token)
expect(resp.headers).to.have.property('authorization');

// Initialize our AuthTokenInfo object from the authorization header.
const authheader = resp.headers.authorization as string;
const authinfo: AuthTokenInfo = new AuthTokenInfo(authheader);

// Save our AuthTokenInfo object to our dsAuthInfo UI cookie
// This ensures the UI will recognize we are logged in on next "visit()"
cy.setCookie(TOKENITEM, JSON.stringify(authinfo));
});
cy.createCSRFCookie().as('csrf_token');

// get our REST API's base URL, also needed for POST
cy.task('getRestBaseURL').then((baseRestUrl: string) => {
// Now, send login POST request including that CSRF token
cy.request({
method: 'POST',
url: baseRestUrl + '/api/authn/login',
headers: { [XSRF_REQUEST_HEADER]: this.csrf_token},
form: true, // indicates the body should be form urlencoded
body: { user: email, password: password }
}).then((resp) => {
// We expect a successful login
expect(resp.status).to.eq(200);
// We expect to have a valid authorization header returned (with our auth token)
expect(resp.headers).to.have.property('authorization');

// Initialize our AuthTokenInfo object from the authorization header.
const authheader = resp.headers.authorization as string;
const authinfo: AuthTokenInfo = new AuthTokenInfo(authheader);

// Save our AuthTokenInfo object to our dsAuthInfo UI cookie
// This ensures the UI will recognize we are logged in on next "visit()"
cy.setCookie(TOKENITEM, JSON.stringify(authinfo));

cy.setCookie('AUTH_HEADER', authheader);
});
});
}
Expand Down Expand Up @@ -118,24 +138,24 @@ Cypress.Commands.add('loginViaForm', loginViaForm);
*/
function generateViewEvent(uuid: string, dsoType: string): void {
// Create a fake CSRF cookie/token to use in POST
cy.createCSRFCookie().then((csrfToken: string) => {
// get our REST API's base URL, also needed for POST
cy.task('getRestBaseURL').then((baseRestUrl: string) => {
// Now, send 'statistics/viewevents' POST request including that fake CSRF token in required header
cy.request({
method: 'POST',
url: baseRestUrl + '/api/statistics/viewevents',
headers: {
[XSRF_REQUEST_HEADER] : csrfToken,
// use a known public IP address to avoid being seen as a "bot"
'X-Forwarded-For': '1.1.1.1',
},
//form: true, // indicates the body should be form urlencoded
body: { targetId: uuid, targetType: dsoType },
}).then((resp) => {
// We expect a 201 (which means statistics event was created)
expect(resp.status).to.eq(201);
});
cy.createCSRFCookie().as('csrf_token');

// get our REST API's base URL, needed for POST
cy.task('getRestBaseURL').then((baseRestUrl: string) => {
// Now, send 'statistics/viewevents' POST request including that fake CSRF token in required header
cy.request({
method: 'POST',
url: baseRestUrl + '/api/statistics/viewevents',
headers: {
[XSRF_REQUEST_HEADER] : this.csrf_token,
// use a known public IP address to avoid being seen as a "bot"
'X-Forwarded-For': '1.1.1.1',
},
//form: true, // indicates the body should be form urlencoded
body: { targetId: uuid, targetType: dsoType },
}).then((resp) => {
// We expect a 201 (which means statistics event was created)
expect(resp.status).to.eq(201);
});
});
}
Expand Down Expand Up @@ -165,3 +185,104 @@ function createCSRFCookie(): Cypress.Chainable {
}
// Add as a Cypress command (i.e. assign to 'cy.createCSRFCookie')
Cypress.Commands.add('createCSRFCookie', createCSRFCookie);


//TODO FIX THIS TIM TO CREATE TEST DATA!
export function createCommunity(name?: string, parentIndex?: number): void {
// First login as an Admin
cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD);

cy.getCookie('AUTH_HEADER').as('auth_cookie');

cy.createCSRFCookie().as('csrf_token');

// get our REST API's base URL, also needed for POST
cy.task('getRestBaseURL').then((baseRestUrl: string) => {
// Get the "new_community.json" fixture which can be sent to create a community
cy.fixture('new_community.json').then((json) => {
// if name specified, override dc.title value in fixture
if (name !== undefined) { json.metadata['dc.title'][0].value = name; }

let postUrl = baseRestUrl + '/api/core/communities';
if (parentIndex !== undefined) {
postUrl += '?parent=' + String(Cypress.env('communities')[parentIndex].uuid);
} else {
// If top level community, save 'dc.type' flag to note this
// community was created by e2e tests & can be deleted later
json.metadata['dc.type'] = [{value:TYPE_E2E_CONTENT}];
}

cy.request({
method: 'POST',
url: postUrl,
headers: {
[XSRF_REQUEST_HEADER] : this.csrf_token,
// use a known public IP address to avoid being seen as a "bot"
'X-Forwarded-For': '1.1.1.1',
'Authorization': this.auth_cookie.value,
},
//form: true, // indicates the body should be form urlencoded
body: json,
}).then((resp) => {
// We expect a 201 (which means community was created)
expect(resp.status).to.eq(201);

// Save this response body to our list of created communities
// in our environment (this is initialized in e2e.ts)
cy.task('saveCommunityData', JSON.stringify(resp.body));
});

});
});
}
// Add as a Cypress command (i.e. assign to 'cy.createCommunity')
Cypress.Commands.add('createCommunity', createCommunity);

// TODO create "deleteContent"
// Loop through 'e2e_test_communities' and look for ones flagged "to_delete"=true in JSON
// SEND DELETE request for each!!!

/**
* Cleanup all test content by looping through created test communities
* looking for those with our "dc.type" flag with the "delete" flag
*/
export function cleanupTestContent(): void {
// First login as an Admin
cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD);

cy.getCookie('AUTH_HEADER').as('auth_cookie');

cy.createCSRFCookie().as('csrf_token');

// Loop through all test communities
cy.task('getCommunityData').then((communities: string[]) => {
for (const community of communities) {
const object = JSON.parse(community);
// Get id of community to delete
const id: string = object.id;
const type: string = object.metadata['dc.type'][0].value;

if (id !== null && type === TYPE_E2E_CONTENT) {
console.log('Deleting test Community ' + id);
// get our REST API's base URL, neded for delete
cy.task('getRestBaseURL').then((baseRestUrl: string) => {
cy.request({
method: 'DELETE',
url: baseRestUrl + '/api/core/communities/' + id,
headers: {
[XSRF_REQUEST_HEADER] : this.csrf_token,
// use a known public IP address to avoid being seen as a "bot"
'X-Forwarded-For': '1.1.1.1',
'Authorization': this.auth_cookie.value,
},
failOnStatusCode: false,
});
});
}
}
});

cy.task('resetCommunityData');
}
// Add as a Cypress command (i.e. assign to 'cy.cleanupTestContent')
Cypress.Commands.add('cleanupTestContent', cleanupTestContent);
6 changes: 6 additions & 0 deletions cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,19 @@ before(() => {
});

// Runs once before the first test in each "block"
// (In Cypress, a test "block" starts with "describe()")
beforeEach(() => {
// Pre-agree to all Klaro cookies by setting the klaro-anonymous cookie
// This just ensures it doesn't get in the way of matching other objects in the page.
cy.setCookie('klaro-anonymous', '{%22authentication%22:true%2C%22preferences%22:true%2C%22acknowledgement%22:true%2C%22google-analytics%22:true%2C%22google-recaptcha%22:true}');

// Remove any CSRF cookies saved from prior tests
cy.clearCookie(DSPACE_XSRF_COOKIE);

// TODO: These methods *WORK*. However, they end up crashing/overwhelming the UI in tests
// (request timed out). NEED TO DEBUG WHY. Maybe it's several calls to login or several CSRF tokens?
//cy.cleanupTestContent();
//cy.createCommunity();
});

// Global constants used in tests
Expand Down

0 comments on commit 80a3e41

Please sign in to comment.