Skip to content

Commit

Permalink
Replace Protrator e2e tests with Cypress. Move all existing tests to …
Browse files Browse the repository at this point in the history
…Cypress & add basic Deque Axe integration.
  • Loading branch information
tdonohue committed Aug 26, 2021
1 parent adb40d8 commit c86b68a
Show file tree
Hide file tree
Showing 30 changed files with 925 additions and 506 deletions.
16 changes: 9 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ jobs:
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ runner.os }}-yarn-

- name: Install latest ChromeDriver compatible with installed Chrome
#- name: Install latest ChromeDriver compatible with installed Chrome
# needs to be npm, the --detect_chromedriver_version flag doesn't work with yarn global
run: |
npm install -g chromedriver --detect_chromedriver_version
chromedriver -v
# run: |
# npm install -g chromedriver --detect_chromedriver_version
# chromedriver -v

- name: Install Yarn dependencies
run: yarn install --frozen-lockfile
Expand Down Expand Up @@ -112,10 +112,12 @@ jobs:
- name: Get DSpace REST Backend info/properties
run: curl http://localhost:8080/server/api

# Run integration tests via Cypress.io
- name: Run e2e tests (integration tests)
run: |
chromedriver --url-base='/wd/hub' --port=4444 &
yarn run e2e:ci
uses: cypress-io/github-action@v2
with:
browser: chrome
headless: true

# Start up the app with SSR enabled (run in background)
- name: Start app in SSR (server-side rendering) mode
Expand Down
22 changes: 7 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ Default configuration file is located in `src/environments/` folder.
To change the default configuration values, create local files that override the parameters you need to change. You can use `environment.template.ts` as a starting point.

- Create a new `environment.dev.ts` file in `src/environments/` for a `development` environment;
- Create a new `environment.prod.ts` file in `src/environments/` for a `production` environment;
- Create a new `environment.prod.ts` file in `src/environments/` for a `production` environment;

The server settings can also be overwritten using an environment file.
The server settings can also be overwritten using an environment file.

This file should be called `.env` and be placed in the project root.

Expand All @@ -125,7 +125,7 @@ DSPACE_REST_SSL # Whether the angular REST uses SSL [true/false]
```

The same settings can also be overwritten by setting system environment variables instead, E.g.:
```bash
```bash
export DSPACE_HOST=api7.dspace.org
```

Expand All @@ -140,7 +140,7 @@ To use environment variables in a UI component, use:
import { environment } from '../environment.ts';
```

This file is generated by the script located in `scripts/set-env.ts`. This script will run automatically before every build, or can be manually triggered using the appropriate `config` script in `package.json`
This file is generated by the script located in `scripts/set-env.ts`. This script will run automatically before every build, or can be manually triggered using the appropriate `config` script in `package.json`


Running the app
Expand Down Expand Up @@ -219,19 +219,11 @@ and run: `yarn run test`

### E2E test

E2E tests use Protractor + Selenium server + browsers. You can find the configuration file at the same level of this README file:`./protractor.conf.js` Protractor is installed as 'local' as a dev dependency.

If you are going to use a remote test enviroment you need to edit the './e2e//protractor.conf.js'. Follow the instructions you will find inside it.

The default browser is Google Chrome.

Place your tests at the following path: `./e2e`

and run: `ng e2e`
E2E tests (aka integration tests) use [Cypress.io](https://www.cypress.io/). Configuration for cypress can be found in the `cypress.json` file in the root directory.

### Continuous Integration (CI) Test
The test files can be found in the `./cypress/integration/` folder.

To run all the tests (e.g.: to run tests with Continuous Integration software) you can execute:`yarn run ci` Keep in mind that this command prerequisites are the sum of unit test and E2E tests.
Run `ng e2e` to kick off the tests. This will start Cypress and allow you to select the browser you wish to use, as well as whether you wish to run all tests or an individual test file.

Documentation
--------------
Expand Down
29 changes: 24 additions & 5 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,18 +147,19 @@
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
"cypress/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"builder": "@cypress/schematic:cypress",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "dspace-angular:serve"
"devServerTarget": "dspace-angular:serve",
"watch": true,
"headless": false
},
"configurations": {
"production": {
Expand Down Expand Up @@ -215,9 +216,27 @@
"configurations": {
"production": {}
}
},
"cypress-run": {
"builder": "@cypress/schematic:cypress",
"options": {
"devServerTarget": "dspace-angular:serve"
},
"configurations": {
"production": {
"devServerTarget": "dspace-angular:serve:production"
}
}
},
"cypress-open": {
"builder": "@cypress/schematic:cypress",
"options": {
"watch": true,
"headless": false
}
}
}
}
},
"defaultProject": "dspace-angular"
}
}
9 changes: 9 additions & 0 deletions cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"integrationFolder": "cypress/integration",
"supportFile": "cypress/support/index.ts",
"videosFolder": "cypress/videos",
"screenshotsFolder": "cypress/screenshots",
"pluginsFile": "cypress/plugins/index.ts",
"fixturesFolder": "cypress/fixtures",
"baseUrl": "http://localhost:4000"
}
5 changes: 5 additions & 0 deletions cypress/fixtures/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "[email protected]",
"body": "Fixtures are a great way to mock data for responses to routes"
}
37 changes: 37 additions & 0 deletions cypress/integration/homepage.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
describe('Homepage', () => {
beforeEach(() => {
// All tests start with visiting homepage
cy.visit('/');
});

it('should display translated title "DSpace Angular :: Home"', () => {
cy.title().should('eq', 'DSpace Angular :: Home');
});

it('should contain a news section', () => {
cy.get('ds-home-news').should('be.visible');
});

it('should have a working search box', () => {
const queryString = 'test';
cy.get('ds-search-form input[name="query"]').type(queryString);
cy.get('ds-search-form button.search-button').click();
cy.url().should('include', '/search');
cy.url().should('include', 'query=' + encodeURI(queryString));
});

it('should pass accessibility tests', () => {
// first must inject Axe into current page
cy.injectAxe();

// Analyze entire page for accessibility issues
// NOTE: this test checks accessibility of header/footer as well
cy.checkA11y({
exclude: [
['#klaro'], // Klaro plugin (privacy policy popup) has color contrast issues
['#search-navbar-container'], // search in navbar has duplicative ID. Will be fixed in #1174
['.dropdownLogin'] // "Log in" link in header has color contrast issues
],
});
});
});
15 changes: 15 additions & 0 deletions cypress/integration/item-page.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
describe('Item Page', () => {
const ITEMPAGE = '/items/e98b0f27-5c19-49a0-960d-eb6ad5287067';
const ENTITYPAGE = '/entities/publication/e98b0f27-5c19-49a0-960d-eb6ad5287067';

it('should contain element ds-item-page when navigating to an item page', () => {
cy.visit(ENTITYPAGE);
cy.get('ds-item-page').should('exist');
});

// Test that entities will redirect to /entities/[type]/[uuid] when accessed via /items/[uuid]
it('should redirect to the entity page when navigating to an item page', () => {
cy.visit(ITEMPAGE);
cy.location('pathname').should('eq', ENTITYPAGE);
});
});
24 changes: 24 additions & 0 deletions cypress/integration/item-statistics.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
describe('Item Statistics Page', () => {
const ITEMSTATISTICSPAGE = '/statistics/items/e98b0f27-5c19-49a0-960d-eb6ad5287067';

it('should contain element ds-item-statistics-page when navigating to an item statistics page', () => {
cy.visit(ITEMSTATISTICSPAGE);
cy.get('ds-item-statistics-page').should('exist');
cy.get('ds-item-page').should('not.exist');
});

it('should contain the item statistics page url when navigating to an item statistics page', () => {
cy.visit(ITEMSTATISTICSPAGE);
cy.location('pathname').should('eq', ITEMSTATISTICSPAGE);
});

it('should contain a "Total visits" section', () => {
cy.visit(ITEMSTATISTICSPAGE);
cy.get('ds-statistics-table h3').contains('Total visits');
});

it('should contain a "Total visits per month" section', () => {
cy.visit(ITEMSTATISTICSPAGE);
cy.get('ds-statistics-table h3').contains('Total visits per month');
});
});
12 changes: 12 additions & 0 deletions cypress/integration/pagenotfound.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
describe('PageNotFound', () => {
it('should contain element ds-pagenotfound when navigating to page that doesnt exist', () => {
cy.visit('/e9019a69-d4f1-4773-b6a3-bd362caa46f2');
cy.get('ds-pagenotfound').should('exist');
});

it('should not contain element ds-pagenotfound when navigating to existing page', () => {
cy.visit('/home');
cy.get('ds-pagenotfound').should('not.exist');
});

});
49 changes: 49 additions & 0 deletions cypress/integration/search-navbar.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const page = {
fillOutQueryInNavBar(query) {
// Click the magnifying glass
cy.get('.navbar-container #search-navbar-container form a').click();
// Fill out a query and click Enter
cy.get('.navbar-container #search-navbar-container form input[name = "query"]').type(query);
},
submitQueryByPressingEnter() {
cy.get('.navbar-container #search-navbar-container form input[name = "query"]').type('{enter}');
},
submitQueryByPressingIcon() {
cy.get('.navbar-container #search-navbar-container form .submit-icon').click();
}
};

describe('Search from Navigation Bar', () => {
// NOTE: these tests currently assume this query will return results!
const query = 'test';

it('should go to search page with correct query if submitted (from home)', () => {
cy.visit('/');
page.fillOutQueryInNavBar(query);
page.submitQueryByPressingEnter();
// New URL should include query param
cy.url().should('include', 'query=' + query);
// At least one search result should be displayed
cy.get('ds-item-search-result-list-element').should('be.visible');
});

it('should go to search page with correct query if submitted (from search)', () => {
cy.visit('/search');
page.fillOutQueryInNavBar(query);
page.submitQueryByPressingEnter();
// New URL should include query param
cy.url().should('include', 'query=' + query);
// At least one search result should be displayed
cy.get('ds-item-search-result-list-element').should('be.visible');
});

it('should allow user to also submit query by clicking icon', () => {
cy.visit('/');
page.fillOutQueryInNavBar(query);
page.submitQueryByPressingIcon();
// New URL should include query param
cy.url().should('include', 'query=' + query);
// At least one search result should be displayed
cy.get('ds-item-search-result-list-element').should('be.visible');
});
});
63 changes: 63 additions & 0 deletions cypress/integration/search-page.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
describe('Search Page', () => {

it('should contain query value when navigating to page with query parameter', () => {
const queryString = 'test query';
cy.visit('/search?query=' + queryString);
cy.get('#search-form input').should('have.value', queryString);
});


it('should have right scope selected when navigating to page with scope parameter', () => {
// First, visit search with no params just to get the set of the scope options
cy.visit('/search');
cy.get('#search-form select[name="scope"] > option').as('options');

// Find length of scope options, select a random index
cy.get('@options').its('length')
.then(len => Math.floor(Math.random() * Math.floor(len)))
.then((index) => {
// return the option at that (randomly selected) index
return cy.get('@options').eq(index);
})
.then((option) => {
const randomScope: any = option.val();
// Visit the search page with the randomly selected option as a pararmeter
cy.visit('/search?scope=' + randomScope);
// Verify that scope is selected when the page reloads
cy.get('#search-form select[name="scope"]').find('option:selected').should('have.value', randomScope);
});
});


it('should redirect to the correct url when scope was set and submit button was triggered', () => {
// First, visit search with no params just to get the set of scope options
cy.visit('/search');
cy.get('#search-form select[name="scope"] > option').as('options');

// Find length of scope options, select a random index (i.e. a random option in selectbox)
cy.get('@options').its('length')
.then(len => Math.floor(Math.random() * Math.floor(len)))
.then((index) => {
// return the option at that (randomly selected) index
return cy.get('@options').eq(index);
})
.then((option) => {
const randomScope: any = option.val();
// Select the option at our random index & click the search button
cy.get('#search-form select[name="scope"]').select(randomScope);
cy.get('#search-form button.search-button').click();
// Result should be the page URL should include that scope & page will reload with scope selected
cy.url().should('include', 'scope=' + randomScope);
cy.get('#search-form select[name="scope"]').find('option:selected').should('have.value', randomScope);
});
});

it('should redirect to the correct url when query was set and submit button was triggered', () => {
const queryString = 'Another interesting query string';
cy.visit('/search');
cy.get('#search-form input[name="query"]').type(queryString);
cy.get('#search-form button.search-button').click();
cy.url().should('include', 'query=' + encodeURI(queryString));
});

});
5 changes: 5 additions & 0 deletions cypress/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Plugins enable you to tap into, modify, or extend the internal behavior of Cypress
// For more info, visit https://on.cypress.io/plugins-api
/* tslint:disable:no-empty */
module.exports = (on, config) => { };
/* tslint:enable:no-empty */
Loading

0 comments on commit c86b68a

Please sign in to comment.