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 },
);
});
});