diff --git a/cypress/integration/admin.spec.js b/cypress/integration/admin.spec.js index 379d626a..dc4f40af 100644 --- a/cypress/integration/admin.spec.js +++ b/cypress/integration/admin.spec.js @@ -1,214 +1,208 @@ /** * Copyright (C) 2019 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ const $ = Cypress.$; -const username = 'admin'; -const password = 'admin'; const defaultRedirect = '/admin/origin/save/'; let url; -function login(username, password) { - cy.get('input[name="username"]') - .type(username) - .get('input[name="password"]') - .type(password) - .get('form') - .submit(); -} - function logout() { cy.contains('a', 'logout') .click(); } describe('Test Admin Login/logout', function() { before(function() { url = this.Urls.admin(); }); it('should redirect to default page', function() { - cy.visit(url); - login(username, password); + cy.visit(url) + .get('input[name="username"]') + .type('admin') + .get('input[name="password"]') + .type('admin') + .get('form') + .submit(); cy.location('pathname') .should('be.equal', defaultRedirect); logout(); }); it('should display admin-origin-save and deposit in sidebar', function() { + cy.adminLogin(); cy.visit(url); - login(username, password); cy.get(`.sidebar a[href="${this.Urls.admin_origin_save()}"]`) .should('be.visible'); cy.get(`.sidebar a[href="${this.Urls.admin_deposit()}"]`) .should('be.visible'); logout(); }); it('should display username on top-right', function() { + cy.adminLogin(); cy.visit(url); - login(username, password); cy.get('.swh-position-right') - .should('contain', username); + .should('contain', 'admin'); logout(); }); it('should prevent unauthorized access after logout', function() { cy.visit(this.Urls.admin_origin_save()) .location('pathname') .should('be.equal', '/admin/login/'); cy.visit(this.Urls.admin_deposit()) .location('pathname') .should('be.equal', '/admin/login/'); }); it('should redirect to correct page after login', function() { // mock calls to deposit list api to avoid possible errors // while running the test cy.server(); cy.route({ method: 'GET', url: `${this.Urls.admin_deposit_list()}**`, response: { data: [], recordsTotal: 0, recordsFiltered: 0, draw: 1 } }); cy.visit(this.Urls.admin_deposit()) .location('search') .should('contain', `next=${this.Urls.admin_deposit()}`); - login(username, password); + cy.adminLogin(); + cy.visit(this.Urls.admin_deposit()); cy.location('pathname') .should('be.equal', this.Urls.admin_deposit()); logout(); }); }); const existingRowToSelect = 'https://bitbucket.org/'; const originUrlListTestData = [ { listType: 'authorized', originToAdd: 'git://git.archlinux.org/', originToRemove: 'https://github.com/' }, { listType: 'unauthorized', originToAdd: 'https://random.org', originToRemove: 'https://gitlab.com' } ]; const capitalize = s => s.charAt(0).toUpperCase() + s.slice(1); describe('Test Admin Origin Save Urls Filtering', function() { beforeEach(function() { + cy.adminLogin(); cy.visit(this.Urls.admin_origin_save()); - login(username, password); - cy.contains('a', 'Origin urls filtering') .click() .wait(500); }); it(`should select or unselect a table row by clicking on it`, function() { cy.contains(`#swh-authorized-origin-urls tr`, existingRowToSelect) .click() .should('have.class', 'selected') .click() .should('not.have.class', 'selected'); }); originUrlListTestData.forEach(testData => { it(`should add a new origin url prefix in the ${testData.listType} list`, function() { const tabName = capitalize(testData.listType) + ' urls'; cy.contains('a', tabName) .click() .wait(500); cy.get(`#swh-${testData.listType}-origin-urls tr`).each(elt => { if ($(elt).text() === testData.originToAdd) { cy.get(elt).click(); cy.get(`#swh-remove-${testData.listType}-origin-url`).click(); } }); cy.get(`#swh-${testData.listType}-url-prefix`) .type(testData.originToAdd); cy.get(`#swh-add-${testData.listType}-origin-url`) .click(); cy.contains(`#swh-${testData.listType}-origin-urls tr`, testData.originToAdd) .should('be.visible'); cy.contains('.alert-success', `The origin url prefix has been successfully added in the ${testData.listType} list.`) .should('be.visible'); cy.get(`#swh-add-${testData.listType}-origin-url`) .click(); cy.contains('.alert-warning', `The provided origin url prefix is already registered in the ${testData.listType} list.`) .should('be.visible'); }); it(`should remove an origin url prefix from the ${testData.listType} list`, function() { const tabName = capitalize(testData.listType) + ' urls'; cy.contains('a', tabName) .click(); let originUrlMissing = true; cy.get(`#swh-${testData.listType}-origin-urls tr`).each(elt => { if ($(elt).text() === testData.originToRemove) { originUrlMissing = false; } }); if (originUrlMissing) { cy.get(`#swh-${testData.listType}-url-prefix`) .type(testData.originToRemove); cy.get(`#swh-add-${testData.listType}-origin-url`) .click(); cy.get('.alert-dismissible button').click(); } cy.contains(`#swh-${testData.listType}-origin-urls tr`, testData.originToRemove) .click(); cy.get(`#swh-remove-${testData.listType}-origin-url`).click(); cy.contains(`#swh-${testData.listType}-origin-urls tr`, testData.originToRemove) .should('not.exist'); }); }); }); diff --git a/cypress/integration/deposit-admin.spec.js b/cypress/integration/deposit-admin.spec.js index dd28122c..87284a20 100644 --- a/cypress/integration/deposit-admin.spec.js +++ b/cypress/integration/deposit-admin.spec.js @@ -1,316 +1,300 @@ /** * Copyright (C) 2020 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ -const username = 'admin'; -const password = 'admin'; - -function login(username, password) { - cy.get('input[name="username"]') - .type(username) - .get('input[name="password"]') - .type(password) - .get('form') - .submit(); -} - // data to use as request query response let responseDeposits; let expectedOrigins; describe('Test admin deposit page', function() { beforeEach(() => { responseDeposits = [ { 'id': 614, 'external_id': 'ch-de-1', 'reception_date': '2020-05-18T13:48:27Z', 'status': 'done', 'status_detail': null, 'swh_id': 'swh:1:dir:ef04a768', 'swh_id_context': 'swh:1:dir:ef04a768;origin=https://w.s.o/c-d-1;visit=swh:1:snp:b234be1e;anchor=swh:1:rev:d24a75c9;path=/' }, { 'id': 613, 'external_id': 'ch-de-2', 'reception_date': '2020-05-18T11:20:16Z', 'status': 'done', 'status_detail': null, 'swh_id': 'swh:1:dir:181417fb', 'swh_id_context': 'swh:1:dir:181417fb;origin=https://w.s.o/c-d-2;visit=swh:1:snp:8c32a2ef;anchor=swh:1:rev:3d1eba04;path=/' }, { 'id': 612, 'external_id': 'ch-de-3', 'reception_date': '2020-05-18T11:20:16Z', 'status': 'rejected', 'status_detail': 'incomplete deposit!', 'swh_id': null, 'swh_id_context': null } ]; // those are computed from the expectedOrigins = { 614: 'https://w.s.o/c-d-1', 613: 'https://w.s.o/c-d-2', 612: '' }; }); it('Should filter out deposits matching excluding pattern from display', function() { + cy.adminLogin(); cy.visit(this.Urls.admin_deposit()); - // FIXME: cypress anti-pattern, do not use ui to log ¯\_(ツ)_/¯ - // https://docs.cypress.io/guides/getting-started/testing-your-app.html#Logging-in - login(username, password); cy.server(); // entry supposed to be excluded from the display by default let extraDeposit = { 'id': 10, 'external_id': 'check-deposit-3', 'reception_date': '2020-05-18T11:20:16Z', 'status': 'done', 'status_detail': null, 'swh_id': 'swh:1:dir:fb234417', 'swh_id_context': 'swh:1:dir:fb234417;origin=https://w.s.o/c-d-3;visit=swh:1:snp:181417fb;anchor=swh:1:rev:3d166604;path=/' }; // of course, that's how to copy a list (an "array") let testDeposits = responseDeposits.slice(); // and add a new element to that array by mutating it... testDeposits.push(extraDeposit); expectedOrigins[10] = 'https://w.s.o/c-d-3'; // ensure we don't touch the original reference expect(responseDeposits.length).to.be.equal(3); expect(testDeposits.length).to.be.equal(4); cy.route({ method: 'GET', url: `${this.Urls.admin_deposit_list()}**`, response: { 'draw': 10, 'recordsTotal': testDeposits.length, 'recordsFiltered': testDeposits.length, 'data': testDeposits } }).as('listDeposits'); cy.location('pathname') .should('be.equal', this.Urls.admin_deposit()); cy.url().should('include', '/admin/deposit'); cy.get('#swh-admin-deposit-list') .should('exist'); cy.wait('@listDeposits').then((xhr) => { let deposits = xhr.response.body.data; expect(deposits.length).to.equal(testDeposits.length); cy.get('#swh-admin-deposit-list').find('tbody > tr').as('rows'); // only 2 entries cy.get('@rows').each((row, idx, collection) => { let deposit = deposits[idx]; let responseDeposit = testDeposits[idx]; cy.log('deposit', deposit); cy.log('responseDeposit', responseDeposit); expect(deposit.id).to.be.equal(responseDeposit['id']); expect(deposit.external_id).to.be.equal(responseDeposit['external_id']); expect(deposit.status).to.be.equal(responseDeposit['status']); expect(deposit.status_detail).to.be.equal(responseDeposit['status_detail']); expect(deposit.swh_id).to.be.equal(responseDeposit['swh_id']); expect(deposit.swh_id_context).to.be.equal(responseDeposit['swh_id_context']); let expectedOrigin = expectedOrigins[deposit.id]; // part of the data, but it should not be displayed (got filtered out) if (deposit.external_id === 'check-deposit-3') { cy.contains(deposit.status).should('not.be.visible'); cy.contains(deposit.status_detail).should('not.be.visible'); cy.contains(deposit.external_id).should('not.be.visible'); cy.contains(expectedOrigin).should('not.be.visible'); cy.contains(deposit.swh_id).should('not.be.visible'); cy.contains(deposit.swh_id_context).should('not.be.visible'); } else { expect(deposit.external_id).to.be.not.equal('check-deposit-3'); cy.contains(deposit.id).should('be.visible'); if (deposit.status !== 'rejected') { cy.contains(deposit.external_id).should('not.be.visible'); cy.contains(expectedOrigin).should('be.visible'); // ensure it's in the dom } cy.contains(deposit.status).should('be.visible'); // those are hidden by default, so now visible if (deposit.status_detail !== null) { cy.contains(deposit.status_detail).should('not.be.visible'); } // those are hidden by default if (deposit.swh_id !== null) { cy.contains(deposit.swh_id).should('not.be.visible'); cy.contains(deposit.swh_id_context).should('not.be.visible'); } } }); // toggling all links and ensure, the previous checks are inverted cy.get('a.toggle-col').click({'multiple': true}).then(() => { cy.get('#swh-admin-deposit-list').find('tbody > tr').as('rows'); cy.get('@rows').should('have.length', 3); cy.get('@rows').each((row, idx, collection) => { let deposit = deposits[idx]; let expectedOrigin = expectedOrigins[deposit.id]; // filtered out deposit if (deposit.external_id === 'check-deposit-3') { cy.contains(deposit.status).should('not.be.visible'); cy.contains(deposit.status_detail).should('not.be.visible'); cy.contains(deposit.external_id).should('not.be.visible'); cy.contains(expectedOrigin).should('not.be.visible'); cy.contains(deposit.swh_id).should('not.be.visible'); cy.contains(deposit.swh_id_context).should('not.be.visible'); } else { expect(deposit.external_id).to.be.not.equal('check-deposit-3'); // ensure it's in the dom cy.contains(deposit.id).should('not.be.visible'); if (deposit.status !== 'rejected') { cy.contains(deposit.external_id).should('not.be.visible'); expect(row).to.contain(expectedOrigin); } expect(row).to.not.contain(deposit.status); // those are hidden by default, so now visible if (deposit.status_detail !== null) { cy.contains(deposit.status_detail).should('be.visible'); } // those are hidden by default, so now they should be visible if (deposit.swh_id !== null) { cy.contains(deposit.swh_id).should('be.visible'); cy.contains(deposit.swh_id_context).should('be.visible'); } } }); }); cy.get('#swh-admin-deposit-list-error') .should('not.contain', 'An error occurred while retrieving the list of deposits'); }); }); it('Should display properly entries', function() { + cy.adminLogin(); cy.visit(this.Urls.admin_deposit()); - // FIXME: cypress anti-pattern, do not use ui to log ¯\_(ツ)_/¯ - // https://docs.cypress.io/guides/getting-started/testing-your-app.html#Logging-in - login(username, password); let testDeposits = responseDeposits; cy.server(); cy.route({ method: 'GET', url: `${this.Urls.admin_deposit_list()}**`, response: { 'draw': 10, 'recordsTotal': testDeposits.length, 'recordsFiltered': testDeposits.length, 'data': testDeposits } }).as('listDeposits'); cy.location('pathname') .should('be.equal', this.Urls.admin_deposit()); cy.url().should('include', '/admin/deposit'); cy.get('#swh-admin-deposit-list') .should('exist'); cy.wait('@listDeposits').then((xhr) => { cy.log('response:', xhr.response); cy.log(xhr.response.body); let deposits = xhr.response.body.data; cy.log('Deposits: ', deposits); expect(deposits.length).to.equal(testDeposits.length); cy.get('#swh-admin-deposit-list').find('tbody > tr').as('rows'); // only 2 entries cy.get('@rows').each((row, idx, collection) => { let deposit = deposits[idx]; let responseDeposit = testDeposits[idx]; assert.isNotNull(deposit); assert.isNotNull(responseDeposit); expect(deposit.id).to.be.equal(responseDeposit['id']); expect(deposit.external_id).to.be.equal(responseDeposit['external_id']); expect(deposit.status).to.be.equal(responseDeposit['status']); expect(deposit.status_detail).to.be.equal(responseDeposit['status_detail']); expect(deposit.swh_id).to.be.equal(responseDeposit['swh_id']); expect(deposit.swh_id_context).to.be.equal(responseDeposit['swh_id_context']); let expectedOrigin = expectedOrigins[deposit.id]; // ensure it's in the dom cy.contains(deposit.id).should('be.visible'); if (deposit.status !== 'rejected') { expect(row).to.not.contain(deposit.external_id); cy.contains(expectedOrigin).should('be.visible'); } cy.contains(deposit.status).should('be.visible'); // those are hidden by default, so now visible if (deposit.status_detail !== null) { cy.contains(deposit.status_detail).should('not.be.visible'); } // those are hidden by default if (deposit.swh_id !== null) { cy.contains(deposit.swh_id).should('not.be.visible'); cy.contains(deposit.swh_id_context).should('not.be.visible'); } }); // toggling all links and ensure, the previous checks are inverted cy.get('a.toggle-col').click({'multiple': true}).then(() => { cy.get('#swh-admin-deposit-list').find('tbody > tr').as('rows'); cy.get('@rows').each((row, idx, collection) => { let deposit = deposits[idx]; let expectedOrigin = expectedOrigins[deposit.id]; // ensure it's in the dom cy.contains(deposit.id).should('not.be.visible'); if (deposit.status !== 'rejected') { expect(row).to.not.contain(deposit.external_id); expect(row).to.contain(expectedOrigin); } expect(row).to.not.contain(deposit.status); // those are hidden by default, so now visible if (deposit.status_detail !== null) { cy.contains(deposit.status_detail).should('be.visible'); } // those are hidden by default, so now they should be visible if (deposit.swh_id !== null) { cy.contains(deposit.swh_id).should('be.visible'); cy.contains(deposit.swh_id_context).should('be.visible'); } }); }); cy.get('#swh-admin-deposit-list-error') .should('not.contain', 'An error occurred while retrieving the list of deposits'); }); }); }); diff --git a/cypress/integration/vault.spec.js b/cypress/integration/vault.spec.js index f227f5fe..9f5f3c77 100644 --- a/cypress/integration/vault.spec.js +++ b/cypress/integration/vault.spec.js @@ -1,438 +1,428 @@ /** * Copyright (C) 2019-2020 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ let vaultItems = []; const progressbarColors = { 'new': 'rgba(128, 128, 128, 0.5)', 'pending': 'rgba(0, 0, 255, 0.5)', 'done': 'rgb(92, 184, 92)' }; -function adminLogin() { - cy.visit('/admin/') - .get('input[name="username"]') - .type('admin') - .get('input[name="password"]') - .type('admin') - .get('form') - .submit(); -} - function checkVaultCookingTask(objectType) { cy.contains('button', 'Download') .click(); cy.contains('.dropdown-item', objectType) .click(); cy.wait('@checkVaultCookingTask'); } function updateVaultItemList(vaultUrl, vaultItems) { cy.visit(vaultUrl) .then(() => { // Add uncooked task to localStorage // which updates it in vault items list window.localStorage.setItem('swh-vault-cooking-tasks', JSON.stringify(vaultItems)); }); } // Mocks API response : /api/1/vault/(:objectType)/(:hash) // objectType : {'directory', 'revision'} function genVaultCookingResponse(objectType, objectId, status, message, fetchUrl) { return { 'obj_type': objectType, 'id': 1, 'progress_message': message, 'status': status, 'obj_id': objectId, 'fetch_url': fetchUrl }; }; // Tests progressbar color, status // And status in localStorage function testStatus(taskId, color, statusMsg, status) { cy.get(`.swh-vault-table #vault-task-${taskId}`) .should('be.visible') .find('.progress-bar') .should('be.visible') .and('have.css', 'background-color', color) .and('contain', statusMsg) .then(() => { // Vault item with object_id as taskId should exist in localStorage const currentVaultItems = JSON.parse(window.localStorage.getItem('swh-vault-cooking-tasks')); const vaultItem = currentVaultItems.find(obj => obj.object_id === taskId); assert.isNotNull(vaultItem); assert.strictEqual(vaultItem.status, status); }); } describe('Vault Cooking User Interface Tests', function() { before(function() { this.directory = this.origin[0].directory[0].id; this.directoryUrl = this.Urls.browse_directory(this.directory); this.vaultDirectoryUrl = this.Urls.api_1_vault_cook_directory(this.directory); this.vaultFetchDirectoryUrl = this.Urls.api_1_vault_fetch_directory(this.directory); this.revision = this.origin[1].revisions[0]; this.revisionUrl = this.Urls.browse_revision(this.revision); this.vaultRevisionUrl = this.Urls.api_1_vault_cook_revision_gitfast(this.revision); this.vaultFetchRevisionUrl = this.Urls.api_1_vault_fetch_revision_gitfast(this.revision); vaultItems[0] = { 'object_type': 'revision', 'object_id': this.revision, 'email': '', 'status': 'done', 'fetch_url': `/api/1/vault/revision/${this.revision}/gitfast/raw/`, 'progress_message': null }; }); beforeEach(function() { this.genVaultDirCookingResponse = (status, message = null) => { return genVaultCookingResponse('directory', this.directory, status, message, this.vaultFetchDirectoryUrl); }; this.genVaultRevCookingResponse = (status, message = null) => { return genVaultCookingResponse('revision', this.revision, status, message, this.vaultFetchRevisionUrl); }; cy.server(); }); it('should create a directory cooking task and report its status', function() { // Browse a directory cy.visit(this.directoryUrl); // Stub responses when requesting the vault API to simulate // a task has been created cy.route({ method: 'GET', url: this.vaultDirectoryUrl, response: {'exception': 'NotFoundExc'} }).as('checkVaultCookingTask'); cy.route({ method: 'POST', url: this.vaultDirectoryUrl, response: this.genVaultDirCookingResponse('new') }).as('createVaultCookingTask'); cy.contains('button', 'Download') .click(); cy.route({ method: 'GET', url: this.vaultDirectoryUrl, response: this.genVaultDirCookingResponse('new') }).as('checkVaultCookingTask'); // Create a vault cooking task through the GUI cy.get('.modal-dialog') .contains('button:visible', 'Ok') .click(); cy.wait('@createVaultCookingTask'); // Check that a redirection to the vault UI has been performed cy.url().should('eq', Cypress.config().baseUrl + this.Urls.browse_vault()); cy.wait('@checkVaultCookingTask').then(() => { testStatus(this.directory, progressbarColors['new'], 'new', 'new'); }); // Stub response to the vault API indicating the task is processing cy.route({ method: 'GET', url: this.vaultDirectoryUrl, response: this.genVaultDirCookingResponse('pending', 'Processing...') }).as('checkVaultCookingTask'); cy.wait('@checkVaultCookingTask').then(() => { testStatus(this.directory, progressbarColors['pending'], 'Processing...', 'pending'); }); // Stub response to the vault API indicating the task is finished cy.route({ method: 'GET', url: this.vaultDirectoryUrl, response: this.genVaultDirCookingResponse('done') }).as('checkVaultCookingTask'); cy.wait('@checkVaultCookingTask').then(() => { testStatus(this.directory, progressbarColors['done'], 'done', 'done'); }); // Stub response to the vault API to simulate archive download cy.route({ method: 'GET', url: this.vaultFetchDirectoryUrl, response: `fx:${this.directory}.tar.gz,binary`, headers: { 'Content-disposition': `attachment; filename=${this.directory}.tar.gz`, 'Content-Type': 'application/gzip' } }).as('fetchCookedArchive'); cy.get(`#vault-task-${this.directory} .vault-dl-link button`) .click(); cy.wait('@fetchCookedArchive').then((xhr) => { assert.isNotNull(xhr.response.body); }); }); it('should create a revision cooking task and report its status', function() { - adminLogin(); + cy.adminLogin(); // Browse a revision cy.visit(this.revisionUrl); // Stub responses when requesting the vault API to simulate // a task has been created cy.route({ method: 'GET', url: this.vaultRevisionUrl, response: {'exception': 'NotFoundExc'} }).as('checkVaultCookingTask'); cy.route({ method: 'POST', url: this.vaultRevisionUrl, response: this.genVaultRevCookingResponse('new') }).as('createVaultCookingTask'); // Create a vault cooking task through the GUI checkVaultCookingTask('as git'); cy.route({ method: 'GET', url: this.vaultRevisionUrl, response: this.genVaultRevCookingResponse('new') }).as('checkVaultCookingTask'); // Create a vault cooking task through the GUI cy.get('.modal-dialog') .contains('button:visible', 'Ok') .click(); cy.wait('@createVaultCookingTask'); // Check that a redirection to the vault UI has been performed cy.url().should('eq', Cypress.config().baseUrl + this.Urls.browse_vault()); cy.wait('@checkVaultCookingTask').then(() => { testStatus(this.revision, progressbarColors['new'], 'new', 'new'); }); // Stub response to the vault API indicating the task is processing cy.route({ method: 'GET', url: this.vaultRevisionUrl, response: this.genVaultRevCookingResponse('pending', 'Processing...') }).as('checkVaultCookingTask'); cy.wait('@checkVaultCookingTask').then(() => { testStatus(this.revision, progressbarColors['pending'], 'Processing...', 'pending'); }); // Stub response to the vault API indicating the task is finished cy.route({ method: 'GET', url: this.vaultRevisionUrl, response: this.genVaultRevCookingResponse('done') }).as('checkVaultCookingTask'); cy.wait('@checkVaultCookingTask').then(() => { testStatus(this.revision, progressbarColors['done'], 'done', 'done'); }); // Stub response to the vault API indicating to simulate archive // download cy.route({ method: 'GET', url: this.vaultFetchRevisionUrl, response: `fx:${this.revision}.gitfast.gz,binary`, headers: { 'Content-disposition': `attachment; filename=${this.revision}.gitfast.gz`, 'Content-Type': 'application/gzip' } }).as('fetchCookedArchive'); cy.get(`#vault-task-${this.revision} .vault-dl-link button`) .click(); cy.wait('@fetchCookedArchive').then((xhr) => { assert.isNotNull(xhr.response.body); }); }); it('should offer to recook an archive if no more available to download', function() { updateVaultItemList(this.Urls.browse_vault(), vaultItems); // Send 404 when fetching vault item cy.route({ method: 'GET', status: 404, url: this.vaultFetchRevisionUrl, response: { 'exception': 'NotFoundExc', 'reason': `Revision with ID '${this.revision}' not found.` }, headers: { 'Content-Type': 'json' } }).as('fetchCookedArchive'); cy.get(`#vault-task-${this.revision} .vault-dl-link button`) .click(); cy.wait('@fetchCookedArchive').then(() => { cy.route({ method: 'POST', url: this.vaultRevisionUrl, response: this.genVaultRevCookingResponse('new') }).as('createVaultCookingTask'); cy.route({ method: 'GET', url: this.vaultRevisionUrl, response: this.genVaultRevCookingResponse('new') }).as('checkVaultCookingTask'); cy.get('#vault-recook-object-modal > .modal-dialog') .should('be.visible') .contains('button:visible', 'Ok') .click(); cy.wait('@createVaultCookingTask') .wait('@checkVaultCookingTask') .then(() => { testStatus(this.revision, progressbarColors['new'], 'new', 'new'); }); }); }); it('should remove selected vault items', function() { updateVaultItemList(this.Urls.browse_vault(), vaultItems); cy.get(`#vault-task-${this.revision}`) .find('input[type="checkbox"]') .click({force: true}); cy.contains('button', 'Remove selected tasks') .click(); cy.get(`#vault-task-${this.revision}`) .should('not.exist'); }); it('should offer to immediately download a directory tarball if already cooked', function() { // Browse a directory cy.visit(this.directoryUrl); // Stub responses when requesting the vault API to simulate // the directory tarball has already been cooked cy.route({ method: 'GET', url: this.vaultDirectoryUrl, response: this.genVaultDirCookingResponse('done') }).as('checkVaultCookingTask'); // Stub response to the vault API to simulate archive download cy.route({ method: 'GET', url: this.vaultFetchDirectoryUrl, response: `fx:${this.directory}.tar.gz,binary`, headers: { 'Content-disposition': `attachment; filename=${this.directory}.tar.gz`, 'Content-Type': 'application/gzip' } }).as('fetchCookedArchive'); // Create a vault cooking task through the GUI cy.contains('button', 'Download') .click(); // Start archive download through the GUI cy.get('.modal-dialog') .contains('button:visible', 'Ok') .click(); cy.wait('@fetchCookedArchive'); }); it('should offer to immediately download a revision gitfast archive if already cooked', function() { - adminLogin(); + cy.adminLogin(); // Browse a directory cy.visit(this.revisionUrl); // Stub responses when requesting the vault API to simulate // the directory tarball has already been cooked cy.route({ method: 'GET', url: this.vaultRevisionUrl, response: this.genVaultRevCookingResponse('done') }).as('checkVaultCookingTask'); // Stub response to the vault API to simulate archive download cy.route({ method: 'GET', url: this.vaultFetchRevisionUrl, response: `fx:${this.revision}.gitfast.gz,binary`, headers: { 'Content-disposition': `attachment; filename=${this.revision}.gitfast.gz`, 'Content-Type': 'application/gzip' } }).as('fetchCookedArchive'); checkVaultCookingTask('as git'); // Start archive download through the GUI cy.get('.modal-dialog') .contains('button:visible', 'Ok') .click(); cy.wait('@fetchCookedArchive'); }); it('should offer to recook an object if previous vault task failed', function() { cy.visit(this.directoryUrl); // Stub responses when requesting the vault API to simulate // the last cooking of the directory tarball has failed cy.route({ method: 'GET', url: this.vaultDirectoryUrl, response: this.genVaultDirCookingResponse('failed') }).as('checkVaultCookingTask'); cy.contains('button', 'Download') .click(); // Check that recooking the directory is offered to user cy.get('.modal-dialog') .contains('button:visible', 'Ok') .should('be.visible'); }); }); diff --git a/cypress/support/index.js b/cypress/support/index.js index 34364611..5d2b99c5 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -1,122 +1,148 @@ /** * Copyright (C) 2019-2020 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ import '@cypress/code-coverage/support'; import {httpGetJson} from '../utils'; Cypress.Screenshot.defaults({ screenshotOnRunFailure: false }); Cypress.Commands.add('xhrShouldBeCalled', (alias, timesCalled) => { expect( cy.state('requests').filter(call => call.alias === alias), `${alias} should have been called ${timesCalled} times` ).to.have.length(timesCalled); }); +Cypress.Commands.add('adminLogin', () => { + const url = '/admin/login/'; + return cy.request({ + url: url, + method: 'GET' + }).then(() => { + cy.getCookie('sessionid').should('not.exist'); + cy.getCookie('csrftoken').its('value').then((token) => { + cy.request({ + url: url, + method: 'POST', + form: true, + followRedirect: false, + body: { + username: 'admin', + password: 'admin', + csrfmiddlewaretoken: token + } + }).then(() => { + cy.getCookie('sessionid').should('exist'); + return cy.getCookie('csrftoken').its('value'); + }); + }); + }); +}); + before(function() { this.unarchivedRepo = { url: 'https://github.com/SoftwareHeritage/swh-web', type: 'git', revision: '7bf1b2f489f16253527807baead7957ca9e8adde', snapshot: 'd9829223095de4bb529790de8ba4e4813e38672d', rootDirectory: '7d887d96c0047a77e2e8c4ee9bb1528463677663', content: [{ sha1git: 'b203ec39300e5b7e97b6e20986183cbd0b797859' }] }; this.origin = [{ url: 'https://github.com/memononen/libtess2', type: 'git', content: [{ path: 'Source/tess.h' }, { path: 'premake4.lua' }], directory: [{ path: 'Source', id: 'cd19126d815470b28919d64b2a8e6a3e37f900dd' }], revisions: [], invalidSubDir: 'Source1' }, { url: 'https://github.com/wcoder/highlightjs-line-numbers.js', type: 'git', content: [{ path: 'src/highlightjs-line-numbers.js' }], directory: [], revisions: ['1c480a4573d2a003fc2630c21c2b25829de49972'], release: 'v2.6.0' }]; const getMetadataForOrigin = async originUrl => { const originVisitsApiUrl = this.Urls.api_1_origin_visits(originUrl); const originVisits = await httpGetJson(originVisitsApiUrl); const lastVisit = originVisits[0]; const snapshotApiUrl = this.Urls.api_1_snapshot(lastVisit.snapshot); const lastOriginSnapshot = await httpGetJson(snapshotApiUrl); let revision = lastOriginSnapshot.branches.HEAD.target; if (lastOriginSnapshot.branches.HEAD.target_type === 'alias') { revision = lastOriginSnapshot.branches[revision].target; } const revisionApiUrl = this.Urls.api_1_revision(revision); const lastOriginHeadRevision = await httpGetJson(revisionApiUrl); return { 'directory': lastOriginHeadRevision.directory, 'revision': lastOriginHeadRevision.id, 'snapshot': lastOriginSnapshot.id }; }; cy.visit('/').window().then(async win => { this.Urls = win.Urls; for (let origin of this.origin) { const metadata = await getMetadataForOrigin(origin.url); const directoryApiUrl = this.Urls.api_1_directory(metadata.directory); origin.dirContent = await httpGetJson(directoryApiUrl); origin.rootDirectory = metadata.directory; origin.revisions.push(metadata.revision); origin.snapshot = metadata.snapshot; for (let content of origin.content) { const contentPathApiUrl = this.Urls.api_1_directory(origin.rootDirectory, content.path); const contentMetaData = await httpGetJson(contentPathApiUrl); content.name = contentMetaData.name.split('/').slice(-1)[0]; content.sha1git = contentMetaData.target; content.directory = contentMetaData.dir_id; content.rawFilePath = this.Urls.browse_content_raw(`sha1_git:${content.sha1git}`) + `?filename=${encodeURIComponent(content.name)}`; cy.request(content.rawFilePath) .then((response) => { const fileText = response.body; const fileLines = fileText.split('\n'); content.numberLines = fileLines.length; // If last line is empty its not shown if (!fileLines[content.numberLines - 1]) content.numberLines -= 1; }); } } }); }); // force the use of fetch polyfill wrapping XmlHttpRequest // in order for cypress to be able to intercept and stub them Cypress.on('window:before:load', win => { win.fetch = null; });