diff --git a/cypress.config.js b/cypress.config.js index f2d91e0a3..db3329e81 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -4,5 +4,13 @@ module.exports = defineConfig({ e2e: { baseUrl: process.env.CYPRESS_BASE_URL || 'http://localhost:3000', chromeWebSecurity: false, // Required for OIDC testing + setupNodeEvents(on, config) { + on("task", { + log(message) { + console.log(message); + return null; + } + }) + } }, }); diff --git a/cypress/e2e/repo.cy.js b/cypress/e2e/repo.cy.js index 92d02734e..996a9a2c1 100644 --- a/cypress/e2e/repo.cy.js +++ b/cypress/e2e/repo.cy.js @@ -1,55 +1,84 @@ describe('Repo', () => { - beforeEach(() => { + let repoName; + let cloneURL; + let csrfToken; + let cookies; + let repoId; + + before(() => { cy.login('admin', 'admin'); - cy.visit('/dashboard/repo'); + // Create a new repo + cy.getCSRFToken().then((csrfToken) => { + repoName = `${Date.now()}`; + cloneURL = `http://localhost:8000/github.com/cypress-test/${repoName}.git`; + + cy.request({ + method: 'POST', + url: 'http://localhost:8080/api/v1/repo', + body: { + project: 'cypress-test', + name: repoName, + url: `https://github.com/cypress-test/${repoName}.git` + }, + headers: { + cookie: cookies?.join('; ') || '', + 'X-CSRF-TOKEN': csrfToken + } + }).then((res) => { + expect(res.status).to.eq(200); + repoId = res.body._id; + }); + }); + }); - // prevent failures on 404 request and uncaught promises + it('Opens tooltip with correct content and can copy', () => { + cy.visit('/dashboard/repo'); cy.on('uncaught:exception', () => false); + + const tooltipQuery = 'div[role="tooltip"]'; + + // Check the tooltip isn't open to start with + cy.get(tooltipQuery) + .should('not.exist'); + + // Find the repo's Code button and click it + cy.get(`a[href="/dashboard/repo/${repoId}"]`) + .closest('tr') + .find('span') + .contains('Code') + .should('exist') + .click(); + + // Check tooltip is open and contains the correct clone URL + cy.get(tooltipQuery) + .should('exist') + .find('span') + .contains(cloneURL) + .should('exist') + .parent() + .find('span') + .next() + .get('svg.octicon-copy') + .should('exist') + .click() + .get('svg.octicon-copy') + .should('not.exist') + .get('svg.octicon-check') + .should('exist'); }); - describe('Code button for repo row', () => { - it('Opens tooltip with correct content and can copy', () => { - const cloneURLRegex = /http:\/\/localhost:8000\/(?:[^\/]+\/).+\.git/; - const tooltipQuery = 'div[role="tooltip"]'; - - cy - // tooltip isn't open to start with - .get(tooltipQuery) - .should('not.exist'); - - cy - // find a table row for a repo (any will do) - .get('table#RepoListTable>tbody>tr') - // find the nearby span containing Code we can click to open the tooltip - .find('span') - .contains('Code') - .should('exist') - .click(); - - cy - // find the newly opened tooltip - .get(tooltipQuery) - .should('exist') - .find('span') - // check it contains the url we expect - .contains(cloneURLRegex) - .should('exist') - .parent() - // find the adjacent span that contains the svg - .find('span') - .next() - // check it has the copy icon first and click it - .get('svg.octicon-copy') - .should('exist') - .click() - // check the icon has changed to the check icon - .get('svg.octicon-copy') - .should('not.exist') - .get('svg.octicon-check') - .should('exist'); - - // failed to successfully check the clipboard + after(() => { + // Delete the repo + cy.getCSRFToken().then((csrfToken) => { + cy.request({ + method: 'DELETE', + url: `http://localhost:8080/api/v1/repo/${repoName}/delete`, + headers: { + cookie: cookies?.join('; ') || '', + 'X-CSRF-TOKEN': csrfToken + } + }); }); }); }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 751eabdfa..6618b1d53 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -39,3 +39,25 @@ Cypress.Commands.add('login', (username, password) => { cy.url().should('include', '/dashboard/repo'); }); }); + +Cypress.Commands.add('getCSRFToken', () => { + return cy.request('GET', 'http://localhost:8080/api/v1/repo').then((res) => { + let cookies = res.headers['set-cookie']; + + if (typeof cookies === 'string') { + cookies = [cookies]; + } + + if (!cookies) { + throw new Error('No cookies found in response'); + } + + const csrfCookie = cookies.find(c => c.startsWith('csrf=')); + if (!csrfCookie) { + throw new Error('No CSRF cookie found in response headers'); + } + + const token = csrfCookie.split('=')[1].split(';')[0]; + return cy.wrap(decodeURIComponent(token)); + }); +}); diff --git a/src/ui/components/Tasks/Tasks.jsx b/src/ui/components/Tasks/Tasks.jsx index 84951c265..44fe0c5c4 100644 --- a/src/ui/components/Tasks/Tasks.jsx +++ b/src/ui/components/Tasks/Tasks.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import classnames from 'classnames'; +import clsx from 'clsx'; import { makeStyles } from '@material-ui/core/styles'; import Checkbox from '@material-ui/core/Checkbox'; import Tooltip from '@material-ui/core/Tooltip'; @@ -30,7 +30,7 @@ export default function Tasks(props) { setChecked(newChecked); }; const { tasksIndexes, tasks, rtlActive } = props; - const tableCellClasses = classnames(classes.tableCell, { + const tableCellClasses = clsx(classes.tableCell, { [classes.tableCellRTL]: rtlActive, }); return ( @@ -60,7 +60,7 @@ export default function Tasks(props) { classes={{ tooltip: classes.tooltip }} > - + - + diff --git a/src/ui/services/repo.js b/src/ui/services/repo.js index cdbfe3c84..66c117361 100644 --- a/src/ui/services/repo.js +++ b/src/ui/services/repo.js @@ -42,8 +42,8 @@ const getRepos = async ( setIsLoading(true); await axios(url.toString(), getAxiosConfig()) .then((response) => { - const data = response.data; - setData(data); + const sortedRepos = response.data.sort((a, b) => a.name.localeCompare(b.name)); + setData(sortedRepos); }) .catch((error) => { setIsError(true); diff --git a/test/processors/blockForAuth.test.js b/test/processors/blockForAuth.test.js index 77bd75871..0e572904d 100644 --- a/test/processors/blockForAuth.test.js +++ b/test/processors/blockForAuth.test.js @@ -125,7 +125,7 @@ describe('blockForAuth', () => { expect(result).to.equal(action); }), { - numRuns: 100 + numRuns: 1000 } ); }); diff --git a/test/testCheckRepoInAuthList.test.js b/test/testCheckRepoInAuthList.test.js index 885243c8c..206f4460f 100644 --- a/test/testCheckRepoInAuthList.test.js +++ b/test/testCheckRepoInAuthList.test.js @@ -45,7 +45,7 @@ describe('Check a Repo is in the authorised list', async () => { const result = await processor.exec(null, action); expect(result.error).to.be.true; }), - { numRuns: 100 }, + { numRuns: 1000 }, ); }); });