mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Replace Protrator e2e tests with Cypress. Move all existing tests to Cypress & add basic Deque Axe integration.
This commit is contained in:
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
@@ -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
|
||||
@@ -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
|
||||
|
22
README.md
22
README.md
@@ -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.
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
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.
|
||||
|
||||
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 test files can be found in the `./cypress/integration/` folder.
|
||||
|
||||
The default browser is Google Chrome.
|
||||
|
||||
Place your tests at the following path: `./e2e`
|
||||
|
||||
and run: `ng e2e`
|
||||
|
||||
### Continuous Integration (CI) Test
|
||||
|
||||
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
|
||||
--------------
|
||||
|
29
angular.json
29
angular.json
@@ -147,7 +147,7 @@
|
||||
"tsConfig": [
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.spec.json",
|
||||
"e2e/tsconfig.json"
|
||||
"cypress/tsconfig.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
@@ -155,10 +155,11 @@
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
@@ -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
cypress.json
Normal file
9
cypress.json
Normal file
@@ -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
cypress/fixtures/example.json
Normal file
5
cypress/fixtures/example.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
37
cypress/integration/homepage.spec.ts
Normal file
37
cypress/integration/homepage.spec.ts
Normal file
@@ -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
cypress/integration/item-page.spec.ts
Normal file
15
cypress/integration/item-page.spec.ts
Normal file
@@ -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
cypress/integration/item-statistics.spec.ts
Normal file
24
cypress/integration/item-statistics.spec.ts
Normal file
@@ -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
cypress/integration/pagenotfound.spec.ts
Normal file
12
cypress/integration/pagenotfound.spec.ts
Normal file
@@ -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
cypress/integration/search-navbar.spec.ts
Normal file
49
cypress/integration/search-navbar.spec.ts
Normal file
@@ -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
cypress/integration/search-page.spec.ts
Normal file
63
cypress/integration/search-page.spec.ts
Normal file
@@ -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
cypress/plugins/index.ts
Normal file
5
cypress/plugins/index.ts
Normal file
@@ -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 */
|
43
cypress/support/commands.ts
Normal file
43
cypress/support/commands.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
// ***********************************************
|
||||
// This example namespace declaration will help
|
||||
// with Intellisense and code completion in your
|
||||
// IDE or Text Editor.
|
||||
// ***********************************************
|
||||
// declare namespace Cypress {
|
||||
// interface Chainable<Subject = any> {
|
||||
// customCommand(param: any): typeof customCommand;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// function customCommand(param: any): void {
|
||||
// console.warn(param);
|
||||
// }
|
||||
//
|
||||
// NOTE: You can use it like so:
|
||||
// Cypress.Commands.add('customCommand', customCommand);
|
||||
//
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
21
cypress/support/index.ts
Normal file
21
cypress/support/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// When a command from ./commands is ready to use, import with `import './commands'` syntax
|
||||
// import './commands';
|
||||
|
||||
// Import Cypress Axe tools for all tests
|
||||
// https://github.com/component-driven/cypress-axe
|
||||
import 'cypress-axe';
|
13
cypress/tsconfig.json
Normal file
13
cypress/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"types": [
|
||||
"cypress",
|
||||
"cypress-axe"
|
||||
]
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
const config = require('./protractor.conf').config;
|
||||
|
||||
config.capabilities = {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
args: ['--headless', '--no-sandbox', '--disable-gpu']
|
||||
}
|
||||
};
|
||||
|
||||
// don't use protractor's webdriver, as it may be incompatible with the installed chrome version
|
||||
config.directConnect = false;
|
||||
config.seleniumAddress = 'http://localhost:4444/wd/hub';
|
||||
|
||||
exports.config = config;
|
@@ -1,91 +0,0 @@
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/docs/referenceConf.js
|
||||
|
||||
/*global jasmine */
|
||||
var SpecReporter = require('jasmine-spec-reporter').SpecReporter;
|
||||
|
||||
exports.config = {
|
||||
allScriptsTimeout: 600000,
|
||||
// -----------------------------------------------------------------
|
||||
// Uncomment to run tests using a remote Selenium server
|
||||
//seleniumAddress: 'http://selenium.address:4444/wd/hub',
|
||||
// Change to 'false' to run tests using a remote Selenium server
|
||||
directConnect: true,
|
||||
// Change if the website to test is not on the localhost
|
||||
baseUrl: 'http://localhost:4000/',
|
||||
// -----------------------------------------------------------------
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
// -----------------------------------------------------------------
|
||||
// Browser and Capabilities: PhantomJS
|
||||
// -----------------------------------------------------------------
|
||||
// capabilities: {
|
||||
// 'browserName': 'phantomjs',
|
||||
// 'version': '',
|
||||
// 'platform': 'ANY'
|
||||
// },
|
||||
// -----------------------------------------------------------------
|
||||
// Browser and Capabilities: Chrome
|
||||
// -----------------------------------------------------------------
|
||||
capabilities: {
|
||||
'browserName': 'chrome',
|
||||
'version': '',
|
||||
'platform': 'ANY',
|
||||
'chromeOptions': {
|
||||
'args': [ '--headless', '--disable-gpu' ]
|
||||
}
|
||||
},
|
||||
// -----------------------------------------------------------------
|
||||
// Browser and Capabilities: Firefox
|
||||
// -----------------------------------------------------------------
|
||||
// capabilities: {
|
||||
// 'browserName': 'firefox',
|
||||
// 'version': '',
|
||||
// 'platform': 'ANY'
|
||||
// },
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Browser and Capabilities: MultiCapabilities
|
||||
// -----------------------------------------------------------------
|
||||
//multiCapabilities: [
|
||||
// {
|
||||
// 'browserName': 'phantomjs',
|
||||
// 'version': '',
|
||||
// 'platform': 'ANY'
|
||||
// },
|
||||
// {
|
||||
// 'browserName': 'chrome',
|
||||
// 'version': '',
|
||||
// 'platform': 'ANY'
|
||||
// }
|
||||
// {
|
||||
// 'browserName': 'firefox',
|
||||
// 'version': '',
|
||||
// 'platform': 'ANY'
|
||||
// }
|
||||
//],
|
||||
|
||||
plugins: [{
|
||||
path: '../node_modules/protractor-istanbul-plugin'
|
||||
}],
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 600000,
|
||||
print: function () {}
|
||||
},
|
||||
useAllAngular2AppRoots: true,
|
||||
beforeLaunch: function () {
|
||||
require('ts-node').register({
|
||||
project: './e2e/tsconfig.json'
|
||||
});
|
||||
},
|
||||
onPrepare: function () {
|
||||
jasmine.getEnv().addReporter(new SpecReporter({
|
||||
spec: {
|
||||
displayStacktrace: 'pretty'
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
@@ -1,22 +0,0 @@
|
||||
import { ProtractorPage } from './app.po';
|
||||
|
||||
describe('protractor App', () => {
|
||||
let page: ProtractorPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new ProtractorPage();
|
||||
});
|
||||
|
||||
it('should display translated title "DSpace Angular :: Home"', () => {
|
||||
page.navigateTo();
|
||||
page.waitUntilNotLoading();
|
||||
expect<any>(page.getPageTitleText()).toEqual('DSpace Angular :: Home');
|
||||
});
|
||||
|
||||
it('should contain a news section', () => {
|
||||
page.navigateTo();
|
||||
page.waitUntilNotLoading();
|
||||
const text = page.getHomePageNewsText();
|
||||
expect<any>(text).toBeDefined();
|
||||
});
|
||||
});
|
@@ -1,23 +0,0 @@
|
||||
import { browser, element, by, protractor, promise } from 'protractor';
|
||||
|
||||
export class ProtractorPage {
|
||||
navigateTo() {
|
||||
return browser.get('/')
|
||||
.then(() => browser.waitForAngular());
|
||||
}
|
||||
|
||||
getPageTitleText() {
|
||||
return browser.getTitle();
|
||||
}
|
||||
|
||||
getHomePageNewsText() {
|
||||
return element(by.css('ds-home-news')).getText();
|
||||
}
|
||||
|
||||
waitUntilNotLoading(): promise.Promise<unknown> {
|
||||
const loading = element(by.css('.loader'));
|
||||
const EC = protractor.ExpectedConditions;
|
||||
const notLoading = EC.not(EC.presenceOf(loading));
|
||||
return browser.wait(notLoading, 10000);
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
import { ProtractorPage } from './item-statistics.po';
|
||||
import { browser } from 'protractor';
|
||||
import { UIURLCombiner } from '../../../src/app/core/url-combiner/ui-url-combiner';
|
||||
|
||||
describe('protractor Item statics', () => {
|
||||
let page: ProtractorPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new ProtractorPage();
|
||||
});
|
||||
|
||||
it('should contain element ds-item-page when navigating when navigating to an item page', () => {
|
||||
page.navigateToItemPage();
|
||||
expect<any>(page.elementTagExists('ds-item-page')).toEqual(true);
|
||||
expect<any>(page.elementTagExists('ds-item-statistics-page')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should redirect to the entity page when navigating to an item page', () => {
|
||||
page.navigateToItemPage();
|
||||
expect(browser.getCurrentUrl()).toEqual(new UIURLCombiner(page.ENTITYPAGE).toString());
|
||||
expect(browser.getCurrentUrl()).not.toEqual(new UIURLCombiner(page.ITEMSTATISTICSPAGE).toString());
|
||||
expect(browser.getCurrentUrl()).not.toEqual(new UIURLCombiner(page.ITEMPAGE).toString());
|
||||
});
|
||||
|
||||
it('should contain element ds-item-statistics-page when navigating when navigating to an item statistics page', () => {
|
||||
page.navigateToItemStatisticsPage();
|
||||
expect<any>(page.elementTagExists('ds-item-statistics-page')).toEqual(true);
|
||||
expect<any>(page.elementTagExists('ds-item-page')).toEqual(false);
|
||||
});
|
||||
it('should contain the item statistics page url when navigating to an item statistics page', () => {
|
||||
page.navigateToItemStatisticsPage();
|
||||
expect(browser.getCurrentUrl()).toEqual(new UIURLCombiner(page.ITEMSTATISTICSPAGE).toString());
|
||||
expect(browser.getCurrentUrl()).not.toEqual(new UIURLCombiner(page.ENTITYPAGE).toString());
|
||||
expect(browser.getCurrentUrl()).not.toEqual(new UIURLCombiner(page.ITEMPAGE).toString());
|
||||
});
|
||||
});
|
@@ -1,18 +0,0 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
export class ProtractorPage {
|
||||
ITEMPAGE = '/items/e98b0f27-5c19-49a0-960d-eb6ad5287067';
|
||||
ENTITYPAGE = '/entities/publication/e98b0f27-5c19-49a0-960d-eb6ad5287067';
|
||||
ITEMSTATISTICSPAGE = '/statistics/items/e98b0f27-5c19-49a0-960d-eb6ad5287067';
|
||||
|
||||
navigateToItemPage() {
|
||||
return browser.get(this.ITEMPAGE);
|
||||
}
|
||||
navigateToItemStatisticsPage() {
|
||||
return browser.get(this.ITEMSTATISTICSPAGE);
|
||||
}
|
||||
|
||||
elementTagExists(tag: string) {
|
||||
return element(by.tagName(tag)).isPresent();
|
||||
}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
import { ProtractorPage } from './pagenotfound.po';
|
||||
|
||||
describe('protractor PageNotFound', () => {
|
||||
let page: ProtractorPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new ProtractorPage();
|
||||
});
|
||||
|
||||
it('should contain element ds-pagenotfound when navigating to page that doesnt exist', () => {
|
||||
page.navigateToNonExistingPage();
|
||||
expect<any>(page.elementTagExists('ds-pagenotfound')).toEqual(true);
|
||||
});
|
||||
|
||||
it('should not contain element ds-pagenotfound when navigating to existing page', () => {
|
||||
page.navigateToExistingPage();
|
||||
expect<any>(page.elementTagExists('ds-pagenotfound')).toEqual(false);
|
||||
});
|
||||
});
|
@@ -1,18 +0,0 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
export class ProtractorPage {
|
||||
HOMEPAGE = '/home';
|
||||
NONEXISTINGPAGE = '/e9019a69-d4f1-4773-b6a3-bd362caa46f2';
|
||||
|
||||
navigateToNonExistingPage() {
|
||||
return browser.get(this.NONEXISTINGPAGE);
|
||||
}
|
||||
navigateToExistingPage() {
|
||||
return browser.get(this.HOMEPAGE);
|
||||
}
|
||||
|
||||
elementTagExists(tag: string) {
|
||||
return element(by.tagName(tag)).isPresent();
|
||||
}
|
||||
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
import { ProtractorPage } from './search-navbar.po';
|
||||
import { browser } from 'protractor';
|
||||
|
||||
describe('protractor SearchNavbar', () => {
|
||||
let page: ProtractorPage;
|
||||
let queryString: string;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new ProtractorPage();
|
||||
queryString = 'the test query';
|
||||
});
|
||||
|
||||
it('should go to search page with correct query if submitted (from home)', () => {
|
||||
page.navigateToHome();
|
||||
return checkIfSearchWorks();
|
||||
});
|
||||
|
||||
it('should go to search page with correct query if submitted (from search)', () => {
|
||||
page.navigateToSearch();
|
||||
return checkIfSearchWorks();
|
||||
});
|
||||
|
||||
it('check if can submit search box with pressing button', () => {
|
||||
page.navigateToHome();
|
||||
page.expandAndFocusSearchBox();
|
||||
page.setCurrentQuery(queryString);
|
||||
page.submitNavbarSearchForm();
|
||||
browser.wait(() => {
|
||||
return browser.getCurrentUrl().then((url: string) => {
|
||||
return url.indexOf('query=' + encodeURI(queryString)) !== -1;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function checkIfSearchWorks(): boolean {
|
||||
page.setCurrentQuery(queryString);
|
||||
page.submitByPressingEnter();
|
||||
browser.wait(() => {
|
||||
return browser.getCurrentUrl().then((url: string) => {
|
||||
return url.indexOf('query=' + encodeURI(queryString)) !== -1;
|
||||
});
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
@@ -1,35 +0,0 @@
|
||||
import { browser, by, element, protractor } from 'protractor';
|
||||
import { promise } from 'selenium-webdriver';
|
||||
|
||||
export class ProtractorPage {
|
||||
HOME = '/home';
|
||||
SEARCH = '/search';
|
||||
|
||||
navigateToHome() {
|
||||
return browser.get(this.HOME);
|
||||
}
|
||||
|
||||
navigateToSearch() {
|
||||
return browser.get(this.SEARCH);
|
||||
}
|
||||
|
||||
getCurrentQuery(): promise.Promise<string> {
|
||||
return element(by.css('.navbar-container #search-navbar-container form input')).getAttribute('value');
|
||||
}
|
||||
|
||||
expandAndFocusSearchBox() {
|
||||
element(by.css('.navbar-container #search-navbar-container form a')).click();
|
||||
}
|
||||
|
||||
setCurrentQuery(query: string) {
|
||||
element(by.css('.navbar-container #search-navbar-container form input[name="query"]')).sendKeys(query);
|
||||
}
|
||||
|
||||
submitNavbarSearchForm() {
|
||||
element(by.css('.navbar-container #search-navbar-container form .submit-icon')).click();
|
||||
}
|
||||
|
||||
submitByPressingEnter() {
|
||||
element(by.css('.navbar-container #search-navbar-container form input[name="query"]')).sendKeys(protractor.Key.ENTER);
|
||||
}
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
import { ProtractorPage } from './search-page.po';
|
||||
import { browser } from 'protractor';
|
||||
|
||||
describe('protractor SearchPage', () => {
|
||||
let page: ProtractorPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new ProtractorPage();
|
||||
});
|
||||
|
||||
it('should contain query value when navigating to page with query parameter', () => {
|
||||
const queryString = 'Interesting query string';
|
||||
page.navigateToSearchWithQueryParameter(queryString)
|
||||
.then(() => page.getCurrentQuery())
|
||||
.then((query: string) => {
|
||||
expect<string>(query).toEqual(queryString);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have right scope selected when navigating to page with scope parameter', () => {
|
||||
page.navigateToSearch()
|
||||
.then(() => page.getRandomScopeOption())
|
||||
.then((scopeString: string) => {
|
||||
page.navigateToSearchWithScopeParameter(scopeString);
|
||||
page.waitUntilNotLoading();
|
||||
page.getCurrentScope()
|
||||
.then((s: string) => {
|
||||
expect<string>(s).toEqual(scopeString);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should redirect to the correct url when scope was set and submit button was triggered', () => {
|
||||
page.navigateToSearch()
|
||||
.then(() => page.getRandomScopeOption())
|
||||
.then((scopeString: string) => {
|
||||
page.setCurrentScope(scopeString)
|
||||
.then(() => page.submitSearchForm())
|
||||
.then(() => page.waitUntilNotLoading())
|
||||
.then(() => () => {
|
||||
browser.wait(() => {
|
||||
return browser.getCurrentUrl().then((url: string) => {
|
||||
return url.indexOf('scope=' + encodeURI(scopeString)) !== -1;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should redirect to the correct url when query was set and submit button was triggered', () => {
|
||||
const queryString = 'Another interesting query string';
|
||||
page.setCurrentQuery(queryString);
|
||||
page.submitSearchForm();
|
||||
browser.wait(() => {
|
||||
return browser.getCurrentUrl().then((url: string) => {
|
||||
return url.indexOf('query=' + encodeURI(queryString)) !== -1;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,55 +0,0 @@
|
||||
import { browser, by, element, protractor } from 'protractor';
|
||||
import { promise } from 'selenium-webdriver';
|
||||
|
||||
export class ProtractorPage {
|
||||
SEARCH = '/search';
|
||||
|
||||
navigateToSearch() {
|
||||
return browser.get(this.SEARCH);
|
||||
}
|
||||
|
||||
navigateToSearchWithQueryParameter(query: string) {
|
||||
return browser.get(this.SEARCH + '?query=' + query);
|
||||
}
|
||||
|
||||
navigateToSearchWithScopeParameter(scope: string) {
|
||||
return browser.get(this.SEARCH + '?scope=' + scope);
|
||||
}
|
||||
|
||||
getCurrentScope(): promise.Promise<string> {
|
||||
const scopeSelect = element(by.css('#search-form select'));
|
||||
browser.wait(protractor.ExpectedConditions.presenceOf(scopeSelect), 10000);
|
||||
return scopeSelect.getAttribute('value');
|
||||
}
|
||||
|
||||
getCurrentQuery(): promise.Promise<string> {
|
||||
return element(by.css('#search-form input')).getAttribute('value');
|
||||
}
|
||||
|
||||
setCurrentScope(scope: string) {
|
||||
return element(by.css('#search-form option[value="' + scope + '"]')).click();
|
||||
}
|
||||
|
||||
setCurrentQuery(query: string) {
|
||||
element(by.css('#search-form input[name="query"]')).sendKeys(query);
|
||||
}
|
||||
|
||||
submitSearchForm() {
|
||||
return element(by.css('#search-form button.search-button')).click();
|
||||
}
|
||||
|
||||
getRandomScopeOption(): promise.Promise<string> {
|
||||
const options = element(by.css('select[name="scope"]')).all(by.tagName('option'));
|
||||
return options.count().then((c: number) => {
|
||||
const index: number = Math.floor(Math.random() * (c - 1));
|
||||
return options.get(index + 1).getAttribute('value');
|
||||
});
|
||||
}
|
||||
|
||||
waitUntilNotLoading(): promise.Promise<unknown> {
|
||||
const loading = element(by.css('.loader'));
|
||||
const EC = protractor.ExpectedConditions;
|
||||
const notLoading = EC.not(EC.presenceOf(loading));
|
||||
return browser.wait(notLoading, 10000);
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"declaration": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "../dist/out-tsc-e2e",
|
||||
"sourceMap": true,
|
||||
"target": "es2018",
|
||||
"typeRoots": [
|
||||
"../node_modules/@types"
|
||||
]
|
||||
}
|
||||
}
|
14
package.json
14
package.json
@@ -15,7 +15,6 @@
|
||||
"pretest:headless": "yarn run config:test",
|
||||
"prebuild:prod": "yarn run config:prod",
|
||||
"pree2e": "yarn run config:prod",
|
||||
"pree2e:ci": "yarn run config:prod",
|
||||
"start": "yarn run start:prod",
|
||||
"serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts",
|
||||
"start:dev": "npm-run-all --parallel config:dev:watch serve",
|
||||
@@ -32,7 +31,6 @@
|
||||
"lint": "ng lint",
|
||||
"lint-fix": "ng lint --fix=true",
|
||||
"e2e": "ng e2e",
|
||||
"e2e:ci": "ng e2e --webdriver-update=false --protractor-config=./e2e/protractor-ci.conf.js",
|
||||
"compile:server": "webpack --config webpack.server.config.js --progress --color",
|
||||
"serve:ssr": "node dist/server",
|
||||
"clean:coverage": "rimraf coverage",
|
||||
@@ -46,7 +44,9 @@
|
||||
"clean": "yarn run clean:prod && yarn run clean:env && yarn run clean:node",
|
||||
"clean:env": "rimraf src/environments/environment.ts",
|
||||
"sync-i18n": "yarn run config:dev && ts-node --project ./tsconfig.ts-node.json scripts/sync-i18n-files.ts",
|
||||
"postinstall": "ngcc"
|
||||
"postinstall": "ngcc",
|
||||
"cypress:open": "cypress open",
|
||||
"cypress:run": "cypress run"
|
||||
},
|
||||
"browser": {
|
||||
"fs": false,
|
||||
@@ -72,6 +72,7 @@
|
||||
"@angular/platform-server": "~10.2.3",
|
||||
"@angular/router": "~10.2.3",
|
||||
"@angularclass/bootloader": "1.0.1",
|
||||
"@kolkov/ngx-gallery": "^1.2.3",
|
||||
"@ng-bootstrap/ng-bootstrap": "7.0.0",
|
||||
"@ng-dynamic-forms/core": "^12.0.0",
|
||||
"@ng-dynamic-forms/ui-ng-bootstrap": "^12.0.0",
|
||||
@@ -123,8 +124,7 @@
|
||||
"sortablejs": "1.13.0",
|
||||
"tslib": "^2.0.0",
|
||||
"webfontloader": "1.6.28",
|
||||
"zone.js": "^0.10.3",
|
||||
"@kolkov/ngx-gallery": "^1.2.3"
|
||||
"zone.js": "^0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-builders/custom-webpack": "10.0.1",
|
||||
@@ -132,6 +132,7 @@
|
||||
"@angular/cli": "~10.2.0",
|
||||
"@angular/compiler-cli": "~10.2.3",
|
||||
"@angular/language-service": "~10.2.3",
|
||||
"@cypress/schematic": "^1.5.0",
|
||||
"@fortawesome/fontawesome-free": "^5.5.0",
|
||||
"@ngrx/store-devtools": "^10.0.1",
|
||||
"@ngtools/webpack": "10.2.0",
|
||||
@@ -144,11 +145,14 @@
|
||||
"@types/js-cookie": "2.2.6",
|
||||
"@types/lodash": "^4.14.165",
|
||||
"@types/node": "^14.14.9",
|
||||
"axe-core": "^4.3.3",
|
||||
"codelyzer": "^6.0.1",
|
||||
"compression-webpack-plugin": "^3.0.1",
|
||||
"copy-webpack-plugin": "^6.4.1",
|
||||
"css-loader": "3.4.0",
|
||||
"cssnano": "^4.1.10",
|
||||
"cypress": "^8.3.0",
|
||||
"cypress-axe": "^0.13.0",
|
||||
"deep-freeze": "0.0.1",
|
||||
"dotenv": "^8.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.0.3",
|
||||
|
Reference in New Issue
Block a user