diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d8b6829766..544c56eb8c6 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 6d78e93ca2b..f90195c9fac 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 dcc38e1ef2a..48520bcaa32 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 e4431b0e620..19a3d56ed4c 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 6ef1633f106..6de32f8c40f 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)); });