From c86b68a6e0228d75b671de0340c1ec0f50c7f2c0 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Thu, 26 Aug 2021 16:53:33 -0500 Subject: [PATCH 1/8] Replace Protrator e2e tests with Cypress. Move all existing tests to Cypress & add basic Deque Axe integration. --- .github/workflows/build.yml | 16 +- README.md | 22 +- angular.json | 29 +- cypress.json | 9 + cypress/fixtures/example.json | 5 + cypress/integration/homepage.spec.ts | 37 ++ cypress/integration/item-page.spec.ts | 15 + cypress/integration/item-statistics.spec.ts | 24 + cypress/integration/pagenotfound.spec.ts | 12 + cypress/integration/search-navbar.spec.ts | 49 ++ cypress/integration/search-page.spec.ts | 63 ++ cypress/plugins/index.ts | 5 + cypress/support/commands.ts | 43 ++ cypress/support/index.ts | 21 + cypress/tsconfig.json | 13 + e2e/protractor-ci.conf.js | 14 - e2e/protractor.conf.js | 91 --- e2e/src/app.e2e-spec.ts | 22 - e2e/src/app.po.ts | 23 - .../item-statistics.e2e-spec.ts | 36 -- e2e/src/item-statistics/item-statistics.po.ts | 18 - e2e/src/pagenotfound/pagenotfound.e2e-spec.ts | 19 - e2e/src/pagenotfound/pagenotfound.po.ts | 18 - .../search-navbar/search-navbar.e2e-spec.ts | 46 -- e2e/src/search-navbar/search-navbar.po.ts | 35 - e2e/src/search-page/search-page.e2e-spec.ts | 60 -- e2e/src/search-page/search-page.po.ts | 55 -- e2e/tsconfig.json | 16 - package.json | 14 +- yarn.lock | 603 +++++++++++++++++- 30 files changed, 926 insertions(+), 507 deletions(-) create mode 100644 cypress.json create mode 100644 cypress/fixtures/example.json create mode 100644 cypress/integration/homepage.spec.ts create mode 100644 cypress/integration/item-page.spec.ts create mode 100644 cypress/integration/item-statistics.spec.ts create mode 100644 cypress/integration/pagenotfound.spec.ts create mode 100644 cypress/integration/search-navbar.spec.ts create mode 100644 cypress/integration/search-page.spec.ts create mode 100644 cypress/plugins/index.ts create mode 100644 cypress/support/commands.ts create mode 100644 cypress/support/index.ts create mode 100644 cypress/tsconfig.json delete mode 100644 e2e/protractor-ci.conf.js delete mode 100644 e2e/protractor.conf.js delete mode 100644 e2e/src/app.e2e-spec.ts delete mode 100644 e2e/src/app.po.ts delete mode 100644 e2e/src/item-statistics/item-statistics.e2e-spec.ts delete mode 100644 e2e/src/item-statistics/item-statistics.po.ts delete mode 100644 e2e/src/pagenotfound/pagenotfound.e2e-spec.ts delete mode 100644 e2e/src/pagenotfound/pagenotfound.po.ts delete mode 100644 e2e/src/search-navbar/search-navbar.e2e-spec.ts delete mode 100644 e2e/src/search-navbar/search-navbar.po.ts delete mode 100644 e2e/src/search-page/search-page.e2e-spec.ts delete mode 100644 e2e/src/search-page/search-page.po.ts delete mode 100644 e2e/tsconfig.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6f4a11acc8..3f08e6483d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 diff --git a/README.md b/README.md index 660152349f..e6060d0241 100644 --- a/README.md +++ b/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 -------------- diff --git a/angular.json b/angular.json index d158577e06..047fbc6806 100644 --- a/angular.json +++ b/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" -} +} \ No newline at end of file diff --git a/cypress.json b/cypress.json new file mode 100644 index 0000000000..cded267c48 --- /dev/null +++ b/cypress.json @@ -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" +} \ No newline at end of file diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json new file mode 100644 index 0000000000..02e4254378 --- /dev/null +++ b/cypress/fixtures/example.json @@ -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" +} diff --git a/cypress/integration/homepage.spec.ts b/cypress/integration/homepage.spec.ts new file mode 100644 index 0000000000..6425186c0b --- /dev/null +++ b/cypress/integration/homepage.spec.ts @@ -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 + ], + }); + }); +}); diff --git a/cypress/integration/item-page.spec.ts b/cypress/integration/item-page.spec.ts new file mode 100644 index 0000000000..bd91b6506c --- /dev/null +++ b/cypress/integration/item-page.spec.ts @@ -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); + }); +}); diff --git a/cypress/integration/item-statistics.spec.ts b/cypress/integration/item-statistics.spec.ts new file mode 100644 index 0000000000..6d78e93ca2 --- /dev/null +++ b/cypress/integration/item-statistics.spec.ts @@ -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'); + }); +}); diff --git a/cypress/integration/pagenotfound.spec.ts b/cypress/integration/pagenotfound.spec.ts new file mode 100644 index 0000000000..dcc38e1ef2 --- /dev/null +++ b/cypress/integration/pagenotfound.spec.ts @@ -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'); + }); + +}); diff --git a/cypress/integration/search-navbar.spec.ts b/cypress/integration/search-navbar.spec.ts new file mode 100644 index 0000000000..e4431b0e62 --- /dev/null +++ b/cypress/integration/search-navbar.spec.ts @@ -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'); + }); +}); diff --git a/cypress/integration/search-page.spec.ts b/cypress/integration/search-page.spec.ts new file mode 100644 index 0000000000..6ef1633f10 --- /dev/null +++ b/cypress/integration/search-page.spec.ts @@ -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)); + }); + +}); diff --git a/cypress/plugins/index.ts b/cypress/plugins/index.ts new file mode 100644 index 0000000000..c106e08011 --- /dev/null +++ b/cypress/plugins/index.ts @@ -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 */ diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts new file mode 100644 index 0000000000..af1f44a0fc --- /dev/null +++ b/cypress/support/commands.ts @@ -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 { +// 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) => { ... }) diff --git a/cypress/support/index.ts b/cypress/support/index.ts new file mode 100644 index 0000000000..a1456bfcd4 --- /dev/null +++ b/cypress/support/index.ts @@ -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'; diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json new file mode 100644 index 0000000000..a03a5d0871 --- /dev/null +++ b/cypress/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "include": [ + "**/*.ts" + ], + "compilerOptions": { + "sourceMap": false, + "types": [ + "cypress", + "cypress-axe" + ] + } +} \ No newline at end of file diff --git a/e2e/protractor-ci.conf.js b/e2e/protractor-ci.conf.js deleted file mode 100644 index 0cfc1f9eaf..0000000000 --- a/e2e/protractor-ci.conf.js +++ /dev/null @@ -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; diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js deleted file mode 100644 index 93bf7f3301..0000000000 --- a/e2e/protractor.conf.js +++ /dev/null @@ -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' - } - })); - } -}; diff --git a/e2e/src/app.e2e-spec.ts b/e2e/src/app.e2e-spec.ts deleted file mode 100644 index 116934c1f8..0000000000 --- a/e2e/src/app.e2e-spec.ts +++ /dev/null @@ -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(page.getPageTitleText()).toEqual('DSpace Angular :: Home'); - }); - - it('should contain a news section', () => { - page.navigateTo(); - page.waitUntilNotLoading(); - const text = page.getHomePageNewsText(); - expect(text).toBeDefined(); - }); -}); diff --git a/e2e/src/app.po.ts b/e2e/src/app.po.ts deleted file mode 100644 index 0fd016ec68..0000000000 --- a/e2e/src/app.po.ts +++ /dev/null @@ -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 { - const loading = element(by.css('.loader')); - const EC = protractor.ExpectedConditions; - const notLoading = EC.not(EC.presenceOf(loading)); - return browser.wait(notLoading, 10000); - } -} diff --git a/e2e/src/item-statistics/item-statistics.e2e-spec.ts b/e2e/src/item-statistics/item-statistics.e2e-spec.ts deleted file mode 100644 index 58cf569d2a..0000000000 --- a/e2e/src/item-statistics/item-statistics.e2e-spec.ts +++ /dev/null @@ -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(page.elementTagExists('ds-item-page')).toEqual(true); - expect(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(page.elementTagExists('ds-item-statistics-page')).toEqual(true); - expect(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()); - }); -}); diff --git a/e2e/src/item-statistics/item-statistics.po.ts b/e2e/src/item-statistics/item-statistics.po.ts deleted file mode 100644 index ec227b9636..0000000000 --- a/e2e/src/item-statistics/item-statistics.po.ts +++ /dev/null @@ -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(); - } -} diff --git a/e2e/src/pagenotfound/pagenotfound.e2e-spec.ts b/e2e/src/pagenotfound/pagenotfound.e2e-spec.ts deleted file mode 100644 index bad2036c3a..0000000000 --- a/e2e/src/pagenotfound/pagenotfound.e2e-spec.ts +++ /dev/null @@ -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(page.elementTagExists('ds-pagenotfound')).toEqual(true); - }); - - it('should not contain element ds-pagenotfound when navigating to existing page', () => { - page.navigateToExistingPage(); - expect(page.elementTagExists('ds-pagenotfound')).toEqual(false); - }); -}); diff --git a/e2e/src/pagenotfound/pagenotfound.po.ts b/e2e/src/pagenotfound/pagenotfound.po.ts deleted file mode 100644 index a3c02ab644..0000000000 --- a/e2e/src/pagenotfound/pagenotfound.po.ts +++ /dev/null @@ -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(); - } - -} diff --git a/e2e/src/search-navbar/search-navbar.e2e-spec.ts b/e2e/src/search-navbar/search-navbar.e2e-spec.ts deleted file mode 100644 index b60f71919d..0000000000 --- a/e2e/src/search-navbar/search-navbar.e2e-spec.ts +++ /dev/null @@ -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; - } - -}); diff --git a/e2e/src/search-navbar/search-navbar.po.ts b/e2e/src/search-navbar/search-navbar.po.ts deleted file mode 100644 index c1ac817fd2..0000000000 --- a/e2e/src/search-navbar/search-navbar.po.ts +++ /dev/null @@ -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 { - 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); - } -} diff --git a/e2e/src/search-page/search-page.e2e-spec.ts b/e2e/src/search-page/search-page.e2e-spec.ts deleted file mode 100644 index f54fc9b662..0000000000 --- a/e2e/src/search-page/search-page.e2e-spec.ts +++ /dev/null @@ -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(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(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; - }); - }); - }); -}); diff --git a/e2e/src/search-page/search-page.po.ts b/e2e/src/search-page/search-page.po.ts deleted file mode 100644 index 83a66a848b..0000000000 --- a/e2e/src/search-page/search-page.po.ts +++ /dev/null @@ -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 { - const scopeSelect = element(by.css('#search-form select')); - browser.wait(protractor.ExpectedConditions.presenceOf(scopeSelect), 10000); - return scopeSelect.getAttribute('value'); - } - - getCurrentQuery(): promise.Promise { - 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 { - 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 { - const loading = element(by.css('.loader')); - const EC = protractor.ExpectedConditions; - const notLoading = EC.not(EC.presenceOf(loading)); - return browser.wait(notLoading, 10000); - } -} diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json deleted file mode 100644 index fdb29acf69..0000000000 --- a/e2e/tsconfig.json +++ /dev/null @@ -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" - ] - } -} diff --git a/package.json b/package.json index 652ab409ac..9c9944c95b 100644 --- a/package.json +++ b/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", diff --git a/yarn.lock b/yarn.lock index 2d7427bd97..db02d7cbb7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30,6 +30,14 @@ "@angular-devkit/core" "10.1.7" rxjs "6.6.2" +"@angular-devkit/architect@^0.1200.0": + version "0.1200.5" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1200.5.tgz#db20520c1beff0c206197652c5c46a7755caa2a0" + integrity sha512-222VZ4OeaDK3vON8V5m+w15SRWfUs5uOb4H9ij/H9/6tyHD83uWfCDoOGg+ax4wJVdWEFJIS+Vn4ijGcZCq9WQ== + dependencies: + "@angular-devkit/core" "12.0.5" + rxjs "6.6.7" + "@angular-devkit/build-angular@>=0.1000.0 < 0.1100.0", "@angular-devkit/build-angular@~0.1002.0": version "0.1002.0" resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1002.0.tgz#b2d1de921b1fd37906e50442d730ca2516d7d747" @@ -143,6 +151,30 @@ rxjs "6.6.2" source-map "0.7.3" +"@angular-devkit/core@12.0.5": + version "12.0.5" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-12.0.5.tgz#3d02abd97684434f6e1ffee470b7a9c63987ae51" + integrity sha512-zVSQV+8/vjUjsUKGlj8Kf5LioA6AXJTGI0yhHW9q1dFX4dPpbW63k0R1UoIB2wJ0F/AbYVgpnPGPe9BBm2fvZA== + dependencies: + ajv "8.2.0" + ajv-formats "2.0.2" + fast-json-stable-stringify "2.1.0" + magic-string "0.25.7" + rxjs "6.6.7" + source-map "0.7.3" + +"@angular-devkit/core@12.2.2", "@angular-devkit/core@^12.0.0": + version "12.2.2" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-12.2.2.tgz#48e8f627abf54474b885c75ac8ae48dc076d62cb" + integrity sha512-iaPQc0M9FZWvE4MmxRFm5qFNBefvyN7H96pQIIPqT2yalSoiWv1HeQg/OS0WY61lvFPSHnR1n4DZsHCvLdZrFA== + dependencies: + ajv "8.6.2" + ajv-formats "2.1.0" + fast-json-stable-stringify "2.1.0" + magic-string "0.25.7" + rxjs "6.6.7" + source-map "0.7.3" + "@angular-devkit/schematics@10.2.0": version "10.2.0" resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-10.2.0.tgz#a45f316bbaa54cbabc06e2e54b04e1c8d103bc0b" @@ -152,6 +184,15 @@ ora "5.0.0" rxjs "6.6.2" +"@angular-devkit/schematics@12.2.2", "@angular-devkit/schematics@^12.0.0": + version "12.2.2" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-12.2.2.tgz#bfeaddcd161b9491fa30c1f018d0d835a27b8e9a" + integrity sha512-KHPxZCSCbVFjaIlBMaxnoA96FnU62HDk8TpWRSnQY2dIkvEUU7+9UmWVodISaQ+MIYur35bFHPJ19im0YkR0tg== + dependencies: + "@angular-devkit/core" "12.2.2" + ora "5.4.1" + rxjs "6.6.7" + "@angular/animations@~10.2.3": version "10.2.3" resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-10.2.3.tgz#eb12630b530ff861577ead9470c066bb37cd8655" @@ -1226,6 +1267,51 @@ resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== +"@cypress/request@^2.88.5": + version "2.88.6" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.6.tgz#a970dd675befc6bdf8a8921576c01f51cc5798e9" + integrity sha512-z0UxBE/+qaESAHY9p9sM2h8Y4XqtsbDCt0/DPOrqA/RZgKi4PkxdpXyK4wCCnSk1xHqWHZZAE+gV6aDAR6+caQ== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^8.3.2" + +"@cypress/schematic@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@cypress/schematic/-/schematic-1.5.0.tgz#7eb2a13a71641bded3ef6e1cb4f3f6201f087ccc" + integrity sha512-mixvdJaRimgJkoW7dTQaxiVQwfnjVqUVLYhadFYRCm6bR0HMN+6woXAYUXmxlWhpK7lEyDJPllL3NAI4Ik3YAw== + dependencies: + "@angular-devkit/architect" "^0.1200.0" + "@angular-devkit/core" "^12.0.0" + "@angular-devkit/schematics" "^12.0.0" + "@schematics/angular" "^12.0.0" + jsonc-parser "^3.0.0" + rxjs "~6.6.0" + +"@cypress/xvfb@^1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" + integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + "@fortawesome/fontawesome-free@^5.5.0": version "5.15.1" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.1.tgz#ccfef6ddbe59f8fe8f694783e1d3eb88902dc5eb" @@ -1397,6 +1483,15 @@ "@angular-devkit/schematics" "10.2.0" jsonc-parser "2.3.0" +"@schematics/angular@^12.0.0": + version "12.2.2" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-12.2.2.tgz#3eaa470b1adf639fc17034969298777021930551" + integrity sha512-Nqw9rHOTUIzhCxAgj/J1S9C7YLhrsbLbEKJ8gVy6Aakj4jdJBJ9oqPCLnVpP+48k8hSyIZ6TA5X9eVmrUhDDWQ== + dependencies: + "@angular-devkit/core" "12.2.2" + "@angular-devkit/schematics" "12.2.2" + jsonc-parser "3.0.0" + "@schematics/update@0.1002.0": version "0.1002.0" resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1002.0.tgz#ff4c134afe1796960f51308ff4e07218f70a6bbd" @@ -1544,6 +1639,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.12.tgz#0b1d86f8c40141091285dea02e4940df73bba43f" integrity sha512-ASH8OPHMNlkdjrEdmoILmzFfsJICvhBsFfAum4aKZ/9U4B6M6tTmTPh+f3ttWdD74CEGV5XvXWkbyfSdXaTd7g== +"@types/node@^14.14.31": + version "14.17.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.11.tgz#82d266d657aec5ff01ca59f2ffaff1bb43f7bf0f" + integrity sha512-n2OQ+0Bz6WEsUjrvcHD1xZ8K+Kgo4cn9/w94s1bJS690QMUWfJPW/m7CCb7gPkA1fcYwL2UpjXP/rq/Eo41m6w== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -1582,6 +1682,16 @@ "@types/mime" "*" "@types/node" "*" +"@types/sinonjs__fake-timers@^6.0.2": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.3.tgz#79df6f358ae8f79e628fe35a63608a0ea8e7cf08" + integrity sha512-E1dU4fzC9wN2QK2Cr1MLCfyHM8BoNnRFvuf45LYMPNDA+WqbNzC45S4UzPxvp1fFJ1rvSGU0bPvdd35VLmXG8g== + +"@types/sizzle@^2.3.2": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef" + integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ== + "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -1634,6 +1744,13 @@ "@types/webpack-sources" "*" source-map "^0.6.0" +"@types/yauzl@^2.9.1": + version "2.9.2" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a" + integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA== + dependencies: + "@types/node" "*" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -1927,6 +2044,20 @@ ajv-errors@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== +ajv-formats@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.0.2.tgz#69875cb99d76c74be46e9c7a4444bc232354eba0" + integrity sha512-Brah4Uo5/U8v76c6euTwtjVFFaVishwnJrQBYpev1JRh4vjA1F4HY3UzQez41YUCszUCXKagG8v6eVRBHV1gkw== + dependencies: + ajv "^8.0.0" + +ajv-formats@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.0.tgz#96eaf83e38d32108b66d82a9cb0cfa24886cdfeb" + integrity sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" @@ -1942,6 +2073,26 @@ ajv@6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.2.0.tgz#c89d3380a784ce81b2085f48811c4c101df4c602" + integrity sha512-WSNGFuyWd//XO8n/m/EaOlNLtO0yL8EXT/74LqT4khdhpZjP7lkj/kT5uwRmGitKEVp/Oj7ZUHeGfPtgHhQ5CA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ajv@8.6.2, ajv@^8.0.0: + version "8.6.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.2.tgz#2fb45e0e5fcbc0813326c1c3da535d1881bb0571" + integrity sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -2000,6 +2151,13 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.11.0" +ansi-escapes@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + ansi-html@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" @@ -2070,6 +2228,11 @@ aproba@^1.1.1: resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +arch@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -2202,6 +2365,11 @@ ast-types-flow@0.0.7: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-each-series@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/async-each-series/-/async-each-series-0.1.1.tgz#7617c1917401fd8ca4a28aadce3dbae98afeb432" @@ -2272,6 +2440,11 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axe-core@^4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.3.3.tgz#b55cd8e8ddf659fe89b064680e1c6a4dceab0325" + integrity sha512-/lqqLAmuIPi79WYfRpy2i8z+x+vxU3zX2uAm0gs1q52qTuKwolOj1P8XbufpXcsydrpKx2yGn2wzAnxCMV86QA== + axios@0.19.0: version "0.19.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" @@ -2333,7 +2506,7 @@ base64-arraybuffer@0.1.5: resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= -base64-js@^1.0.2: +base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -2423,6 +2596,20 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +blob-util@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" + integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== + blob@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" @@ -2435,7 +2622,7 @@ blocking-proxy@^1.0.0: dependencies: minimist "^1.2.0" -bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: +bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5, bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -2690,6 +2877,11 @@ bs-snippet-injector@^2.0.1: resolved "https://registry.yarnpkg.com/bs-snippet-injector/-/bs-snippet-injector-2.0.1.tgz#61b5393f11f52559ed120693100343b6edb04dd5" integrity sha1-YbU5PxH1JVntEgaTEANDtu2wTdU= +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -2714,6 +2906,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -2840,6 +3040,11 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +cachedir@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" + integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== + call-bind@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" @@ -2973,6 +3178,11 @@ charenc@0.0.2: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= +check-more-types@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= + "chokidar@>=2.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.2.2, chokidar@^3.3.0, chokidar@^3.4.1, chokidar@^3.4.2: version "3.4.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" @@ -3029,6 +3239,11 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-info@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" + integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -3094,6 +3309,29 @@ cli-spinners@^2.4.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047" integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ== +cli-spinners@^2.5.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" + integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== + +cli-table3@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.0.tgz#b7b1bc65ca8e7b5cef9124e13dc2b21e2ce4faee" + integrity sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ== + dependencies: + object-assign "^4.1.0" + string-width "^4.2.0" + optionalDependencies: + colors "^1.1.2" + +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" @@ -3257,11 +3495,21 @@ commander@^4.1.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== +commander@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + commander@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75" integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q== +common-tags@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" + integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -3900,6 +4148,58 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +cypress-axe@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/cypress-axe/-/cypress-axe-0.13.0.tgz#3234e1a79a27701f2451fcf2f333eb74204c7966" + integrity sha512-fCIy7RiDCm7t30U3C99gGwQrUO307EYE1QqXNaf9ToK4DVqW8y5on+0a/kUHMrHdlls2rENF6TN9ZPpPpwLrnw== + +cypress@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.3.0.tgz#ba906d2170888073ad94b2be1b994a749bbb7c7d" + integrity sha512-zA5Rcq8AZIfRfPXU0CCcauofF+YpaU9HYbfqkunFTmFV0Kdlo14tNjH2E3++MkjXKFnv3/pXq+HgxWtw8CSe8Q== + dependencies: + "@cypress/request" "^2.88.5" + "@cypress/xvfb" "^1.2.4" + "@types/node" "^14.14.31" + "@types/sinonjs__fake-timers" "^6.0.2" + "@types/sizzle" "^2.3.2" + arch "^2.2.0" + blob-util "^2.0.2" + bluebird "^3.7.2" + cachedir "^2.3.0" + chalk "^4.1.0" + check-more-types "^2.24.0" + cli-cursor "^3.1.0" + cli-table3 "~0.6.0" + commander "^5.1.0" + common-tags "^1.8.0" + dayjs "^1.10.4" + debug "^4.3.2" + enquirer "^2.3.6" + eventemitter2 "^6.4.3" + execa "4.1.0" + executable "^4.1.1" + extract-zip "2.0.1" + figures "^3.2.0" + fs-extra "^9.1.0" + getos "^3.2.1" + is-ci "^3.0.0" + is-installed-globally "~0.4.0" + lazy-ass "^1.6.0" + listr2 "^3.8.3" + lodash "^4.17.21" + log-symbols "^4.0.0" + minimist "^1.2.5" + ospath "^1.2.2" + pretty-bytes "^5.6.0" + ramda "~0.27.1" + request-progress "^3.0.0" + supports-color "^8.1.1" + tmp "~0.2.1" + untildify "^4.0.0" + url "^0.11.0" + yauzl "^2.10.0" + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -3939,6 +4239,11 @@ date-format@^3.0.0: resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== +dayjs@^1.10.4: + version "1.10.6" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.6.tgz#288b2aa82f2d8418a6c9d4df5898c0737ad02a63" + integrity sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw== + debug-loader@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/debug-loader/-/debug-loader-0.0.1.tgz#44dc37e09e3c39e6af334681960f70a534a9d056" @@ -3981,6 +4286,13 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.2.0: dependencies: ms "2.1.2" +debug@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -4722,6 +5034,11 @@ etag@1.8.1, etag@^1.8.1, etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +eventemitter2@^6.4.3: + version "6.4.4" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.4.tgz#aa96e8275c4dbeb017a5d0e03780c65612a1202b" + integrity sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw== + eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -4747,20 +5064,7 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^4.1.0: +execa@4.1.0, execa@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== @@ -4775,6 +5079,26 @@ execa@^4.1.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +executable@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -4884,6 +5208,17 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +extract-zip@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -4952,12 +5287,19 @@ faye-websocket@~0.11.1: dependencies: websocket-driver ">=0.5.1" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: version "3.5.2" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== -figures@^3.0.0: +figures@^3.0.0, figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -5206,6 +5548,16 @@ fs-extra@^9.0.0: jsonfile "^6.0.1" universalify "^1.0.0" +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-minipass@^1.2.5: version "1.2.7" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" @@ -5306,6 +5658,13 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= +getos@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" + integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== + dependencies: + async "^3.2.0" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -5359,6 +5718,13 @@ global-dirs@^2.0.1: dependencies: ini "^1.3.5" +global-dirs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== + dependencies: + ini "2.0.0" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -5810,7 +6176,7 @@ icss-utils@^4.0.0, icss-utils@^4.1.1: dependencies: postcss "^7.0.14" -ieee754@^1.1.4: +ieee754@^1.1.13, ieee754@^1.1.4: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -5970,6 +6336,11 @@ ini@1.3.5: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: version "1.3.7" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" @@ -6106,6 +6477,13 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" +is-ci@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" + integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ== + dependencies: + ci-info "^3.1.1" + is-color-stop@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" @@ -6221,6 +6599,14 @@ is-installed-globally@^0.3.1: global-dirs "^2.0.1" is-path-inside "^3.0.1" +is-installed-globally@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -6303,6 +6689,11 @@ is-path-inside@^3.0.1: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -6361,6 +6752,11 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -6617,6 +7013,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -6656,6 +7057,11 @@ jsonc-parser@2.3.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.0.tgz#7c7fc988ee1486d35734faaaa866fadb00fa91ee" integrity sha512-b0EBt8SWFNnixVdvoR2ZtEGa9ZqLhbJnOjezn+WP+8kspFm+PFYDN8Z4Bc7pRlDjvuVcADSUkroIuTWWn/YiIA== +jsonc-parser@3.0.0, jsonc-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" + integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== + jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" @@ -6862,6 +7268,11 @@ latest-version@^5.0.0: dependencies: package-json "^6.3.0" +lazy-ass@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= + less-loader@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-6.2.0.tgz#8b26f621c155b342eefc24f5bd6e9dc40c42a719" @@ -6932,6 +7343,19 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= +listr2@^3.8.3: + version "3.11.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.11.0.tgz#9771b02407875aa78e73d6e0ff6541bbec0aaee9" + integrity sha512-XLJVe2JgXCyQTa3FbSv11lkKExYmEyA4jltVo8z4FX10Vt1Yj8IMekBfwim0BSOM9uj1QMTJvDQQpHyuPbB/dQ== + dependencies: + cli-truncate "^2.1.0" + colorette "^1.2.2" + log-update "^4.0.0" + p-map "^4.0.0" + rxjs "^6.6.7" + through "^2.3.8" + wrap-ansi "^7.0.0" + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -7049,6 +7473,11 @@ lodash.memoize@^4.1.2: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -7079,6 +7508,11 @@ lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + log-symbols@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" @@ -7093,6 +7527,24 @@ log-symbols@^4.0.0: dependencies: chalk "^4.0.0" +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + log4js@^6.2.1: version "6.3.0" resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.3.0.tgz#10dfafbb434351a3e30277a00b9879446f715bcb" @@ -8115,6 +8567,21 @@ ora@5.0.0: strip-ansi "^6.0.0" wcwidth "^1.0.1" +ora@5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + original@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" @@ -8145,6 +8612,11 @@ osenv@^0.1.5: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +ospath@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -8460,6 +8932,11 @@ pem@1.14.4: os-tmpdir "^1.0.1" which "^2.0.2" +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -8475,7 +8952,7 @@ pidtree@^0.3.0: resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== -pify@^2.0.0, pify@^2.3.0: +pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= @@ -9272,6 +9749,11 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +pretty-bytes@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + pretty-error@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6" @@ -9489,6 +9971,11 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== +ramda@~0.27.1: + version "0.27.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9" + integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw== + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -9600,7 +10087,7 @@ read-pkg@^3.0.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.6.0: +readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -9771,6 +10258,13 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= +request-progress@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4= + dependencies: + throttleit "^1.0.0" + request-promise-core@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" @@ -9818,6 +10312,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -10040,6 +10539,13 @@ rxjs@6.6.2: dependencies: tslib "^1.9.0" +rxjs@6.6.7, rxjs@^6.6.7, rxjs@~6.6.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + rxjs@^5.5.6: version "5.5.12" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" @@ -10434,6 +10940,24 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + smart-buffer@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" @@ -11092,6 +11616,13 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + svgo@^1.0.0: version "1.3.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" @@ -11256,6 +11787,11 @@ tfunk@^4.0.0: chalk "^1.1.3" dlv "^1.1.3" +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= + through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -11264,7 +11800,7 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" -"through@>=2.2.7 <3", through@X.X.X, through@^2.3.6: +"through@>=2.2.7 <3", through@X.X.X, through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -11293,7 +11829,7 @@ tmp@0.0.30: dependencies: os-tmpdir "~1.0.1" -tmp@0.2.1: +tmp@0.2.1, tmp@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== @@ -11514,6 +12050,11 @@ type-fest@^0.11.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -11697,6 +12238,11 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" @@ -11830,6 +12376,11 @@ uuid@^3.0.0, uuid@^3.3.2, uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + v8-compile-cache@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" @@ -12485,6 +13036,14 @@ yargs@^16.0.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" From fbfd4a276995446d0d292af909aef86d89d0ec32 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 27 Aug 2021 09:11:23 -0500 Subject: [PATCH 2/8] Fixes to e2e CI process --- .github/workflows/build.yml | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3f08e6483d..6d8b682976 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -101,23 +101,32 @@ jobs: # Wait until the REST API returns a 200 response (or for a max of 30 seconds) # https://github.com/nev7n/wait_for_response - - name: Wait for DSpace REST Backend to be ready (for e2e tests) - uses: nev7n/wait_for_response@v1 - with: + #- name: Wait for DSpace REST Backend to be ready (for e2e tests) + # uses: nev7n/wait_for_response@v1 + # with: # We use the 'sites' endpoint to also ensure the database is ready - url: 'http://localhost:8080/server/api/core/sites' - responseCode: 200 - timeout: 30000 + # url: 'http://localhost:8080/server/api/core/sites' + # responseCode: 200 + # timeout: 30000 - - name: Get DSpace REST Backend info/properties - run: curl http://localhost:8080/server/api + #- name: Get DSpace REST Backend info/properties + # run: curl http://localhost:8080/server/api # Run integration tests via Cypress.io + # NOTE: to run these e2e tests locally, just use 'ng e2e' - name: Run e2e tests (integration tests) uses: cypress-io/github-action@v2 with: + # Run tests in Chrome, headless mode browser: chrome headless: true + # Start app before running tests (will be stopped automatically after tests finish) + start: yarn run serve:ssr + # Wait for backend & frontend to be available + # NOTE: We use the 'sites' REST endpoint to also ensure the database is ready + wait-on: http://localhost:8080/server/api/core/sites, http://localhost:4000 + # Wait for 2 mins max for everything to respond + wait-on-timeout: 120 # Start up the app with SSR enabled (run in background) - name: Start app in SSR (server-side rendering) mode From 3c3f2251bb94c10488ef3156cde4a1496e963ff2 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 27 Aug 2021 10:07:07 -0500 Subject: [PATCH 3/8] Minor test cleanup. Ensure artifacts are available based on e2e search results --- .github/workflows/build.yml | 33 ++++++++++++--------- cypress/integration/item-statistics.spec.ts | 7 +++-- cypress/integration/pagenotfound.spec.ts | 3 +- cypress/integration/search-navbar.spec.ts | 2 +- cypress/integration/search-page.spec.ts | 21 +++++++------ 5 files changed, 38 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d8b682976..544c56eb8c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -99,21 +99,8 @@ jobs: docker-compose -f ./docker/cli.yml -f ./docker/cli.assetstore.yml run --rm dspace-cli docker container ls - # Wait until the REST API returns a 200 response (or for a max of 30 seconds) - # https://github.com/nev7n/wait_for_response - #- name: Wait for DSpace REST Backend to be ready (for e2e tests) - # uses: nev7n/wait_for_response@v1 - # with: - # We use the 'sites' endpoint to also ensure the database is ready - # url: 'http://localhost:8080/server/api/core/sites' - # responseCode: 200 - # timeout: 30000 - - #- name: Get DSpace REST Backend info/properties - # run: curl http://localhost:8080/server/api - # Run integration tests via Cypress.io - # NOTE: to run these e2e tests locally, just use 'ng e2e' + # (NOTE: to run these e2e tests locally, just use 'ng e2e') - name: Run e2e tests (integration tests) uses: cypress-io/github-action@v2 with: @@ -128,6 +115,24 @@ jobs: # Wait for 2 mins max for everything to respond wait-on-timeout: 120 + # Cypress always creates a video of all e2e tests (whether they succeeded or failed) + # Save those in an Artifact + - name: Upload e2e test videos to Artifacts + uses: actions/upload-artifact@v1 + if: always() + with: + name: e2e-test-videos + path: cypress/videos + + # If e2e tests fail, Cypress creates a screenshot of what happened + # Save those in an Artifact + - name: Upload e2e test failure screenshots to Artifacts + uses: actions/upload-artifact@v1 + if: failure() + with: + name: e2e-test-screenshots + path: cypress/screenshots + # Start up the app with SSR enabled (run in background) - name: Start app in SSR (server-side rendering) mode run: | diff --git a/cypress/integration/item-statistics.spec.ts b/cypress/integration/item-statistics.spec.ts index 6d78e93ca2..f90195c9fa 100644 --- a/cypress/integration/item-statistics.spec.ts +++ b/cypress/integration/item-statistics.spec.ts @@ -1,5 +1,6 @@ describe('Item Statistics Page', () => { - const ITEMSTATISTICSPAGE = '/statistics/items/e98b0f27-5c19-49a0-960d-eb6ad5287067'; + const ITEMUUID = 'e98b0f27-5c19-49a0-960d-eb6ad5287067'; + const ITEMSTATISTICSPAGE = '/statistics/items/' + ITEMUUID; it('should contain element ds-item-statistics-page when navigating to an item statistics page', () => { cy.visit(ITEMSTATISTICSPAGE); @@ -14,11 +15,11 @@ describe('Item Statistics Page', () => { it('should contain a "Total visits" section', () => { cy.visit(ITEMSTATISTICSPAGE); - cy.get('ds-statistics-table h3').contains('Total visits'); + cy.get('.' + ITEMUUID + '_TotalVisits').should('exist'); }); it('should contain a "Total visits per month" section', () => { cy.visit(ITEMSTATISTICSPAGE); - cy.get('ds-statistics-table h3').contains('Total visits per month'); + cy.get('.' + ITEMUUID + '_TotalVisitsPerMonth').should('exist'); }); }); diff --git a/cypress/integration/pagenotfound.spec.ts b/cypress/integration/pagenotfound.spec.ts index dcc38e1ef2..48520bcaa3 100644 --- a/cypress/integration/pagenotfound.spec.ts +++ b/cypress/integration/pagenotfound.spec.ts @@ -1,6 +1,7 @@ describe('PageNotFound', () => { it('should contain element ds-pagenotfound when navigating to page that doesnt exist', () => { - cy.visit('/e9019a69-d4f1-4773-b6a3-bd362caa46f2'); + // request an invalid page (UUIDs at root path aren't valid) + cy.visit('/e9019a69-d4f1-4773-b6a3-bd362caa46f2', { failOnStatusCode: false }); cy.get('ds-pagenotfound').should('exist'); }); diff --git a/cypress/integration/search-navbar.spec.ts b/cypress/integration/search-navbar.spec.ts index e4431b0e62..19a3d56ed4 100644 --- a/cypress/integration/search-navbar.spec.ts +++ b/cypress/integration/search-navbar.spec.ts @@ -2,7 +2,7 @@ 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 + // Fill out a query in input that appears cy.get('.navbar-container #search-navbar-container form input[name = "query"]').type(query); }, submitQueryByPressingEnter() { diff --git a/cypress/integration/search-page.spec.ts b/cypress/integration/search-page.spec.ts index 6ef1633f10..6de32f8c40 100644 --- a/cypress/integration/search-page.spec.ts +++ b/cypress/integration/search-page.spec.ts @@ -1,16 +1,18 @@ describe('Search Page', () => { + // unique ID of the search form (for selecting specific elements below) + const SEARCHFORM_ID = '#search-form'; 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); + cy.get(SEARCHFORM_ID + ' input[name="query"]').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'); + cy.get(SEARCHFORM_ID + ' select[name="scope"] > option').as('options'); // Find length of scope options, select a random index cy.get('@options').its('length') @@ -24,7 +26,7 @@ describe('Search Page', () => { // 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); + cy.get(SEARCHFORM_ID + ' select[name="scope"]').find('option:selected').should('have.value', randomScope); }); }); @@ -32,7 +34,7 @@ describe('Search Page', () => { 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'); + cy.get(SEARCHFORM_ID + ' 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') @@ -44,19 +46,20 @@ describe('Search Page', () => { .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(); + cy.get(SEARCHFORM_ID + ' select[name="scope"]').select(randomScope); + cy.get(SEARCHFORM_ID + ' 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); + cy.get(SEARCHFORM_ID + ' 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(); + // Type query in searchbox & click search button + cy.get(SEARCHFORM_ID + ' input[name="query"]').type(queryString); + cy.get(SEARCHFORM_ID + ' button.search-button').click(); cy.url().should('include', 'query=' + encodeURI(queryString)); }); From 9d118dc2fdcac030e3da5dd40916224e2c87efc1 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 27 Aug 2021 11:08:30 -0500 Subject: [PATCH 4/8] Add docs on writing e2e tests. Minor cleanup to README elsewhere --- README.md | 137 +++++++++++++++++++++++------------------------------- 1 file changed, 59 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index e6060d0241..aad674c162 100644 --- a/README.md +++ b/README.md @@ -61,14 +61,17 @@ Table of Contents - [Introduction to the technology](#introduction-to-the-technology) - [Requirements](#requirements) - [Installing](#installing) - - [Configuring](#configuring) + - [Configuring](#configuring) - [Running the app](#running-the-app) - - [Running in production mode](#running-in-production-mode) + - [Running in production mode](#running-in-production-mode) - [Deploy](#deploy) - [Running the application with Docker](#running-the-application-with-docker) - [Cleaning](#cleaning) - [Testing](#testing) - [Test a Pull Request](#test-a-pull-request) + - [Unit Tests](#unit-tests) + - [E2E Tests](#e2e-tests) + - [Writing E2E Tests](#writing-e2e-tests) - [Documentation](#documentation) - [Other commands](#other-commands) - [Recommended Editors/IDEs](#recommended-editorsides) @@ -217,18 +220,50 @@ Place your tests in the same location of the application source code files that and run: `yarn run test` -### E2E test +### E2E Tests 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. The test files can be found in the `./cypress/integration/` folder. -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. +Before you can run e2e tests, you MUST have a running backend (i.e. REST API). By default, the e2e tests look for this at http://localhost:8080/server/ or whatever `rest` backend is defined in your `environment.prod.ts` or `environment.common.ts`. You may override this using env variables, see [Configuring](#configuring). + +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. Once you click run on test(s), this opens the [Cypress Test Runner](https://docs.cypress.io/guides/core-concepts/test-runner) to run your test(s) and show you the results. + +#### Writing E2E Tests + +All E2E tests must be created under the `./cypress/integration/` folder, and must end in `.spec.ts`. Subfolders are allowed. + +* The easiest way to start creating new tests is by running `ng e2e`. This builds the app and brings up Cypress. +* From here, if you are editing an existing test file, you can either open it in your IDE or run it first to see what it already does. +* To create a new test file, click `+ New Spec File`. Choose a meaningful name ending in `spec.ts` (Please make sure it ends in `.ts` so that it's a Typescript file, and not plain Javascript) +* Start small. Add a basic `describe` and `it` which just [cy.visit](https://docs.cypress.io/api/commands/visit) the page you want to test. For example: + ``` + describe('Community/Collection Browse Page', () => { + it('should exist as a page', () => { + cy.visit('/community-list'); + }); + }); + ``` +* Run your test file from the Cypress window. This starts the [Cypress Test Runner](https://docs.cypress.io/guides/core-concepts/test-runner) in a new browser window. +* In the [Cypress Test Runner](https://docs.cypress.io/guides/core-concepts/test-runner), you'll Cypress automatically visit the page. This first test will succeed, as all you are doing is making sure the _page exists_. +* From here, you can use the [Selector Playground](https://docs.cypress.io/guides/core-concepts/test-runner#Selector-Playground) in the Cypress Test Runner window to determine how to tell Cypress to interact with a specific HTML element on that page. + * Most commands start by telling Cypress to [get()](https://docs.cypress.io/api/commands/get) a specific element, using a CSS or jQuery style selector + * Cypress can then do actions like [click()](https://docs.cypress.io/api/commands/click) an element, or [type()](https://docs.cypress.io/api/commands/type) text in an input field, etc. + * Cypress can also validate that something occurs, using [should()](https://docs.cypress.io/api/commands/should) assertions. +* Any time you save your test file, the Cypress Test Runner will reload & rerun it. This allows you can see your results quickly as you write the tests & correct any broken tests rapidly. + + +_Hint: Creating e2e tests is easiest in an IDE (like Visual Studio), as it can help prompt/autocomplete your Cypress commands._ + +More Information: [docs.cypress.io](https://docs.cypress.io/) has great guides & documentation helping you learn more about writing/debugging e2e tests in Cypress. Documentation -------------- -See [`./docs`](docs) for further documentation. +Official DSpace documentation is available in the DSpace wiki at https://wiki.lyrasis.org/display/DSDOC7x/ + +Some UI specific configuration documentation is also found in the [`./docs`](docs) folder of htis codebase. ### Building code documentation @@ -251,8 +286,6 @@ To get the most out of TypeScript, you'll need a TypeScript-aware editor. We've - Free - [Visual Studio Code](https://code.visualstudio.com/) - [Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) - - [Atom](https://atom.io/) - - [TypeScript plugin](https://atom.io/packages/atom-typescript) - Paid - [Webstorm](https://www.jetbrains.com/webstorm/download/) or [IntelliJ IDEA Ultimate](https://www.jetbrains.com/idea/) - [Sublime Text](http://www.sublimetext.com/3) @@ -274,95 +307,43 @@ dspace-angular │   ├── environment.default.js * Default configuration files │   └── environment.test.js * Test configuration files ├── docs * Folder for documentation -├── e2e * Folder for e2e test files -│   ├── app.e2e-spec.ts * -│   ├── app.po.ts * -│   ├── pagenotfound * -│   │   ├── pagenotfound.e2e-spec.ts * -│   │   └── pagenotfound.po.ts * +├── cypress * Folder for Cypress (https://cypress.io/) / e2e tests +│   ├── integration * Folder for e2e/integration test files +│   ├── fixtures * Folder for any fixtures needed by e2e tests +│   ├── plugins * Folder for Cypress plugins (if any) +│   ├── support * Folder for global e2e test actions/commands (run for all tests) │   └── tsconfig.json * TypeScript configuration file for e2e tests ├── karma.conf.js * Karma configuration file for Unit Test ├── nodemon.json * Nodemon (https://nodemon.io/) configuration ├── package.json * This file describes the npm package for this project, its dependencies, scripts, etc. ├── postcss.config.js * PostCSS (http://postcss.org/) configuration file -├── protractor.conf.js * -├── resources * Folder for static resources -│   ├── data * Folder for static data -│   │   └── en * Folder for i18n English data -│   ├── i18n * Folder for i18n translations -│   │   └── en.json * i18n translations for English -│   └── images * Folder for images -│   ├── dspace-logo-old.png * -│   ├── dspace-logo.png * -│   └── favicon.ico * -├── rollup.config.js * Rollup (http://rollupjs.org/) configuration -├── spec-bundle.js * ├── src * The source of the application -│   ├── app * -│   │   ├── app-routing.module.ts * -│   │   ├── app.component.html * -│   │   ├── app.component.scss * -│   │   ├── app.component.spec.ts * -│   │   ├── app.component.ts * -│   │   ├── app.effects.ts * -│   │   ├── app.module.ts * -│   │   ├── app.reducer.ts * -│   │   ├── browser-app.module.ts * The root module for the client -│   │   ├── +collection-page * Lazily loaded route for collection module -│   │   ├── +community-page * Lazily loaded route for community module -│   │   ├── core * -│   │   ├── header * -│   │   ├── +home * Lazily loaded route for home module -│   │   ├── +item-page * Lazily loaded route for item module -│   │   ├── object-list * -│   │   ├── pagenotfound * -│   │   ├── server-app.module.ts * The root module for the server -│   │   ├── shared * -│   │   ├── store.actions.ts * -│   │   ├── store.effects.ts * -│   │   ├── thumbnail * -│   │   └── typings.d.ts * File that allows you to add custom typings for libraries without TypeScript support +│   ├── app * The source code of the application, subdivided by module/page. +│   ├── assets * Folder for static resources +│   │   ├── fonts * Folder for fonts +│   │   ├── i18n * Folder for i18n translations +│   | └── en.json5 * i18n translations for English +│   │   └── images * Folder for images │   ├── backend * Folder containing a mock of the REST API, hosted by the express server -│   │   ├── api.ts * -│   │   ├── cache.ts * -│   │   ├── data * -│   │   └── db.ts * │   ├── config * -│   │   ├── cache-config.interface.ts * -│   │   ├── config.interface.ts * -│   │   ├── global-config.interface.ts * -│   │   ├── server-config.interface.ts * -│   │   └── universal-config.interface.ts * -│   ├── config.ts * File that loads environmental and shareable settings and makes them available to app components │   ├── index.csr.html * The index file for client side rendering fallback │   ├── index.html * The index file │   ├── main.browser.ts * The bootstrap file for the client │   ├── main.server.ts * The express (http://expressjs.com/) config and bootstrap file for the server +│   ├── robots.txt * The robots.txt file │   ├── modules * -│   │   ├── cookies * -│   │   ├── data-loader * -│   │   ├── transfer-http * -│   │   ├── transfer-state * -│   │   ├── transfer-store * -│   │   └── translate-universal-loader.ts * -│   ├── routes.ts * The routes file for the server │   ├── styles * Folder containing global styles -│   │   ├── _mixins.scss * -│   │   └── variables.scss * Global sass variables file -│   ├── tsconfig.browser.json * TypeScript config for the client build -│   ├── tsconfig.server.json * TypeScript config for the server build -│   └── tsconfig.test.json * TypeScript config for the test build +│   └── themes * Folder containing available themes +│      ├── custom * Template folder for creating a custom theme +│      └── dspace * Default 'dspace' theme ├── tsconfig.json * TypeScript config ├── tslint.json * TSLint (https://palantir.github.io/tslint/) configuration ├── typedoc.json * TYPEDOC configuration ├── webpack * Webpack (https://webpack.github.io/) config directory -│   ├── webpack.aot.js * Webpack (https://webpack.github.io/) config for AoT build -│   ├── webpack.client.js * Webpack (https://webpack.github.io/) config for client build -│   ├── webpack.common.js * -│   ├── webpack.prod.js * Webpack (https://webpack.github.io/) config for production build -│   ├── webpack.server.js * Webpack (https://webpack.github.io/) config for server build -│   └── webpack.test.js * Webpack (https://webpack.github.io/) config for test build -├── webpack.config.ts * +│   ├── webpack.browser.ts * Webpack (https://webpack.github.io/) config for client build +│   ├── webpack.common.ts * +│   ├── webpack.prod.ts * Webpack (https://webpack.github.io/) config for production build +│   └── webpack.test.ts * Webpack (https://webpack.github.io/) config for test build └── yarn.lock * Yarn lockfile (https://yarnpkg.com/en/docs/yarn-lock) ``` From 03a11aeda1b81994868c30a7602de79024ce6b46 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 27 Aug 2021 11:33:06 -0500 Subject: [PATCH 5/8] Minor cleanup to GitHub CI. No longer need chromedriver. Use latest version of Chrome. --- .github/workflows/build.yml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 544c56eb8c..7758020724 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,9 +16,9 @@ jobs: DSPACE_REST_PORT: 8080 DSPACE_REST_NAMESPACE: '/server' DSPACE_REST_SSL: false - # When Chrome version is specified, we pin to a specific version of Chrome & ChromeDriver - # Comment this out to use the latest release of both. - CHROME_VERSION: "90.0.4430.212-1" + # When Chrome version is specified, we pin to a specific version of Chrome + # Comment this out to use the latest release + #CHROME_VERSION: "90.0.4430.212-1" strategy: # Create a matrix of Node versions to test against (in parallel) matrix: @@ -66,12 +66,6 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: ${{ runner.os }}-yarn- - #- 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 - - name: Install Yarn dependencies run: yarn install --frozen-lockfile @@ -100,6 +94,7 @@ jobs: docker container ls # Run integration tests via Cypress.io + # https://github.com/cypress-io/github-action # (NOTE: to run these e2e tests locally, just use 'ng e2e') - name: Run e2e tests (integration tests) uses: cypress-io/github-action@v2 @@ -118,7 +113,7 @@ jobs: # Cypress always creates a video of all e2e tests (whether they succeeded or failed) # Save those in an Artifact - name: Upload e2e test videos to Artifacts - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2 if: always() with: name: e2e-test-videos @@ -127,7 +122,7 @@ jobs: # If e2e tests fail, Cypress creates a screenshot of what happened # Save those in an Artifact - name: Upload e2e test failure screenshots to Artifacts - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2 if: failure() with: name: e2e-test-screenshots From fec71ebf999a7e8e4fe8eaeb82f5b2cf3543af56 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 27 Aug 2021 12:02:23 -0500 Subject: [PATCH 6/8] Add link to Cypress docs on creating first test in our README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aad674c162..0e98bc7cf9 100644 --- a/README.md +++ b/README.md @@ -252,7 +252,7 @@ All E2E tests must be created under the `./cypress/integration/` folder, and mus * Cypress can then do actions like [click()](https://docs.cypress.io/api/commands/click) an element, or [type()](https://docs.cypress.io/api/commands/type) text in an input field, etc. * Cypress can also validate that something occurs, using [should()](https://docs.cypress.io/api/commands/should) assertions. * Any time you save your test file, the Cypress Test Runner will reload & rerun it. This allows you can see your results quickly as you write the tests & correct any broken tests rapidly. - +* Cypress also has a great guide on [writing your first test](https://on.cypress.io/writing-first-test) with much more info. Keep in mind, while the examples in the Cypress docs often involve Javascript files (.js), the same examples will work in our Typescript (.ts) e2e tests. _Hint: Creating e2e tests is easiest in an IDE (like Visual Studio), as it can help prompt/autocomplete your Cypress commands._ From 13d8d75cfa0096f3d82b314279634629ab8bb078 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 27 Aug 2021 12:24:35 -0500 Subject: [PATCH 7/8] Upgrade to latest Cypress --- package.json | 2 +- yarn.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 9c9944c95b..00c1ad7b24 100644 --- a/package.json +++ b/package.json @@ -151,7 +151,7 @@ "copy-webpack-plugin": "^6.4.1", "css-loader": "3.4.0", "cssnano": "^4.1.10", - "cypress": "^8.3.0", + "cypress": "8.3.1", "cypress-axe": "^0.13.0", "deep-freeze": "0.0.1", "dotenv": "^8.2.0", diff --git a/yarn.lock b/yarn.lock index db02d7cbb7..18eb31b8a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1267,7 +1267,7 @@ resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== -"@cypress/request@^2.88.5": +"@cypress/request@^2.88.6": version "2.88.6" resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.6.tgz#a970dd675befc6bdf8a8921576c01f51cc5798e9" integrity sha512-z0UxBE/+qaESAHY9p9sM2h8Y4XqtsbDCt0/DPOrqA/RZgKi4PkxdpXyK4wCCnSk1xHqWHZZAE+gV6aDAR6+caQ== @@ -4153,12 +4153,12 @@ cypress-axe@^0.13.0: resolved "https://registry.yarnpkg.com/cypress-axe/-/cypress-axe-0.13.0.tgz#3234e1a79a27701f2451fcf2f333eb74204c7966" integrity sha512-fCIy7RiDCm7t30U3C99gGwQrUO307EYE1QqXNaf9ToK4DVqW8y5on+0a/kUHMrHdlls2rENF6TN9ZPpPpwLrnw== -cypress@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.3.0.tgz#ba906d2170888073ad94b2be1b994a749bbb7c7d" - integrity sha512-zA5Rcq8AZIfRfPXU0CCcauofF+YpaU9HYbfqkunFTmFV0Kdlo14tNjH2E3++MkjXKFnv3/pXq+HgxWtw8CSe8Q== +cypress@8.3.1: + version "8.3.1" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.3.1.tgz#c6760dbb907df2570b0e1ac235fa31c30f9260a6" + integrity sha512-1v6pfx+/5cXhaT5T6QKOvnkawmEHWHLiVzm3MYMoQN1fkX2Ma1C32STd3jBStE9qT5qPSTILjGzypVRxCBi40g== dependencies: - "@cypress/request" "^2.88.5" + "@cypress/request" "^2.88.6" "@cypress/xvfb" "^1.2.4" "@types/node" "^14.14.31" "@types/sinonjs__fake-timers" "^6.0.2" From 098779a82d477eb86e6646ad85beff6420765341 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 27 Aug 2021 13:09:33 -0500 Subject: [PATCH 8/8] Fix to Cypress tsconfig.json, don't override sourceMap settings --- cypress/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json index a03a5d0871..39f88f8210 100644 --- a/cypress/tsconfig.json +++ b/cypress/tsconfig.json @@ -4,7 +4,6 @@ "**/*.ts" ], "compilerOptions": { - "sourceMap": false, "types": [ "cypress", "cypress-axe"