diff --git a/.gitignore b/.gitignore index 747f84c76..ea4f36546 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* -package-lock.json # Diagnostic reports (https://nodejs.org/api/report.html) diff --git a/cypress/e2e/repo.cy.js b/cypress/e2e/repo.cy.js index b627412d0..348345995 100644 --- a/cypress/e2e/repo.cy.js +++ b/cypress/e2e/repo.cy.js @@ -1,57 +1,119 @@ describe('Repo', () => { beforeEach(() => { - cy.login('admin', 'admin'); - cy.visit('/dashboard/repo'); // prevent failures on 404 request and uncaught promises cy.on('uncaught:exception', () => false); }); - describe('Code button for repo row', () => { - it('Opens tooltip with correct content and can copy', () => { - const cloneURL = 'http://localhost:8000/finos/git-proxy.git'; - const tooltipQuery = 'div[role="tooltip"]'; + describe('Anonymous users', () => { + it('Prevents anonymous users from adding repos', () => { + cy.get('[data-testid="repo-list-view"]') + .find('[data-testid="add-repo-button"]') + .should('not.exist'); + }); + + describe('Code button for repo row', () => { + it('Opens tooltip with correct content and can copy', () => { + const cloneURL = 'http://localhost:8000/finos/git-proxy.git'; + const tooltipQuery = 'div[role="tooltip"]'; + + cy + // tooltip isn't open to start with + .get(tooltipQuery) + .should('not.exist'); + + cy + // find the entry for finos/git-proxy + .get('a[href="/dashboard/repo/git-proxy"]') + // take it's parent row + .closest('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(cloneURL) + .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 + }); + }); + }); + + describe('Regular users', () => { + beforeEach(() => { + cy.login('user', 'user'); - cy - // tooltip isn't open to start with - .get(tooltipQuery) + cy.visit('/dashboard/repo'); + }); + + after(() => { + cy.logout(); + }); + + it('Prevents regular users from adding repos', () => { + cy.get('[data-testid="repo-list-view"]') + .find('[data-testid="add-repo-button"]') .should('not.exist'); + }); + }); + + describe('Admin users', () => { + beforeEach(() => { + cy.login('admin', 'admin'); + + cy.visit('/dashboard/repo'); + }); + + it('Admin users can add repos', () => { + cy.get('[data-testid="repo-list-view"]').find('[data-testid="add-repo-button"]').click(); + + cy.get('[data-testid="add-repo-dialog"]').within(() => { + cy.get('[data-testid="repo-project-input"]').type('uuidjs'); + cy.get('[data-testid="repo-name-input"]').type('uuidjs'); + cy.get('[data-testid="repo-url-input"]').type('https://github.com/uuidjs/uuid.git'); + cy.get('[data-testid="add-repo-button"]').click(); + }); + + cy.get('a[href="/dashboard/repo/uuidjs"]', { timeout: 10000 }).click(); + + cy.get('[data-testid="delete-repo-button"]').click(); + }); + + it('Prevents adding an existing repo', () => { + cy.get('[data-testid="repo-list-view"]').find('[data-testid="add-repo-button"]').click(); + + cy.get('[data-testid="add-repo-dialog"]').within(() => { + cy.get('[data-testid="repo-project-input"]').type('finos'); + cy.get('[data-testid="repo-name-input"]').type('git-proxy'); + cy.get('[data-testid="repo-url-input"]').type('https://github.com/finos/git-proxy.git'); + cy.get('[data-testid="add-repo-button"]').click(); + }); - cy - // find the entry for finos/git-proxy - .get('a[href="/dashboard/repo/git-proxy"]') - // take it's parent row - .closest('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(cloneURL) - .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 + cy.get('[data-testid="repo-error"]') + .should('be.visible') + .and('contain.text', 'Repository already exists!'); }); }); }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 751eabdfa..43fa0a66a 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -39,3 +39,7 @@ Cypress.Commands.add('login', (username, password) => { cy.url().should('include', '/dashboard/repo'); }); }); + +Cypress.Commands.add('logout', () => { + Cypress.session.clearAllSavedSessions(); +}); diff --git a/package-lock.json b/package-lock.json index d5c11c799..78b74dada 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@finos/git-proxy", - "version": "1.19.2", + "version": "2.0.0-rc.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@finos/git-proxy", - "version": "1.19.2", + "version": "2.0.0-rc.1", "license": "Apache-2.0", "workspaces": [ "./packages/git-proxy-cli" @@ -13267,7 +13267,7 @@ "license": "Apache-2.0", "dependencies": { "@finos/git-proxy": "file:../..", - "axios": "^1.10.0", + "axios": "^1.11.0", "yargs": "^17.7.2" }, "bin": { diff --git a/package.json b/package.json index 0ea35d187..1d11abd94 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "lint:fix": "eslint --fix \"src/**/*.{js,jsx,ts,tsx,json}\" \"test/**/*.{js,jsx,ts,tsx,json}\"", "format": "prettier --write src/**/*.{js,jsx,ts,tsx,css,md,json,scss} test/**/*.{js,jsx,ts,tsx,json} packages/git-proxy-cli/test/**/*.{js,jsx,ts,tsx,json} packages/git-proxy-cli/index.js --config ./.prettierrc", "gen-schema-doc": "node ./scripts/doc-schema.js", - "cypress:run": "cypress run" + "cypress:run": "cypress run", + "cypress:open": "cypress open" }, "bin": { "git-proxy": "./index.js", diff --git a/src/service/passport/local.js b/src/service/passport/local.js index 579d47234..46d2175e2 100644 --- a/src/service/passport/local.js +++ b/src/service/passport/local.js @@ -42,13 +42,18 @@ const configure = async (passport) => { }; /** - * Create the default admin user if it doesn't exist + * Create the default admin and regular test users. */ const createDefaultAdmin = async () => { - const admin = await db.findUser("admin"); - if (!admin) { - await db.createUser("admin", "admin", "admin@place.com", "none", true); - } + const createIfNotExists = async (username, password, email, type, isAdmin) => { + const user = await db.findUser(username); + if (!user) { + await db.createUser(username, password, email, type, isAdmin); + } + }; + + await createIfNotExists('admin', 'admin', 'admin@place.com', 'none', true); + await createIfNotExists('user', 'user', 'user@place.com', 'none', false); }; module.exports = { configure, createDefaultAdmin, type }; diff --git a/src/ui/services/repo.js b/src/ui/services/repo.js index 6bf9e9357..51202a384 100644 --- a/src/ui/services/repo.js +++ b/src/ui/services/repo.js @@ -81,17 +81,17 @@ const getRepo = async (setIsLoading, setData, setAuth, setIsError, id) => { }); }; -const addRepo = async (onClose, setError, data) => { +const addRepo = async (data) => { const url = new URL(`${baseUrl}/repo`); - axios - .post(url, data, { withCredentials: true, headers: { 'X-CSRF-TOKEN': getCookie('csrf') } }) - .then(() => { - onClose(); - }) - .catch((error) => { - console.log(error.response.data.message); - setError(error.response.data.message); - }); + try { + await axios.post(url, data, { withCredentials: true, headers: { 'X-CSRF-TOKEN': getCookie('csrf') } }); + return { success: true }; + } catch (error) { + return { + success: false, + message: error.response?.data?.message || error.message + }; + } }; const addUser = async (repoName, user, action) => { diff --git a/src/ui/views/RepoDetails/RepoDetails.tsx b/src/ui/views/RepoDetails/RepoDetails.tsx index a036d172d..5da82e0ad 100644 --- a/src/ui/views/RepoDetails/RepoDetails.tsx +++ b/src/ui/views/RepoDetails/RepoDetails.tsx @@ -101,6 +101,7 @@ const RepoDetails: React.FC = () => { - @@ -207,7 +206,7 @@ const NewRepo: React.FC = ({ onSuccess }) => { return (
- diff --git a/src/ui/views/RepoList/Components/TabList.tsx b/src/ui/views/RepoList/Components/TabList.tsx index 3f1fdea5e..e864c3cc2 100644 --- a/src/ui/views/RepoList/Components/TabList.tsx +++ b/src/ui/views/RepoList/Components/TabList.tsx @@ -5,7 +5,7 @@ import Repositories from './Repositories'; export default function Dashboard(): React.ReactElement { return ( -
+