Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
148 changes: 105 additions & 43 deletions cypress/e2e/repo.cy.js
Original file line number Diff line number Diff line change
@@ -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!');
});
});
});
4 changes: 4 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ Cypress.Commands.add('login', (username, password) => {
cy.url().should('include', '/dashboard/repo');
});
});

Cypress.Commands.add('logout', () => {
Cypress.session.clearAllSavedSessions();
});
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
15 changes: 10 additions & 5 deletions src/service/passport/local.js
Original file line number Diff line number Diff line change
Expand Up @@ -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", "[email protected]", "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', '[email protected]', 'none', true);
await createIfNotExists('user', 'user', '[email protected]', 'none', false);
};

module.exports = { configure, createDefaultAdmin, type };
20 changes: 10 additions & 10 deletions src/ui/services/repo.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
1 change: 1 addition & 0 deletions src/ui/views/RepoDetails/RepoDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ const RepoDetails: React.FC = () => {
<Button
variant='contained'
color='secondary'
data-testid='delete-repo-button'
onClick={() => removeRepository(data.name)}
>
<Delete />
Expand Down
23 changes: 11 additions & 12 deletions src/ui/views/RepoList/Components/NewRepo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,12 @@ const AddRepositoryDialog: React.FC<AddRepositoryDialogProps> = ({ open, onClose
return;
}

try {
await addRepo(onClose, setError, data);
const result = await addRepo(data);
if (result.success) {
handleSuccess(data);
handleClose();
} catch (e) {
if (e instanceof Error) {
setError(e.message);
} else {
setError('An unexpected error occurred');
}
} else {
setError(result.message || 'Failed to add repository');
}
};

Expand Down Expand Up @@ -126,7 +122,7 @@ const AddRepositoryDialog: React.FC<AddRepositoryDialogProps> = ({ open, onClose
maxWidth='md'
>
{error && (
<DialogTitle style={{ color: 'red' }} id='simple-dialog-title'>
<DialogTitle style={{ color: 'red' }} data-testid='repo-error'>
{error}
</DialogTitle>
)}
Expand All @@ -135,13 +131,14 @@ const AddRepositoryDialog: React.FC<AddRepositoryDialogProps> = ({ open, onClose
</DialogTitle>
<Card>
<CardBody>
<GridContainer>
<GridContainer data-testid='add-repo-dialog'>
<GridItem xs={12} sm={12} md={12}>
<FormControl style={inputStyle}>
<InputLabel htmlFor='project'>Organization</InputLabel>
<Input
id='project'
inputProps={{ maxLength: 200, minLength: 3 }}
data-testid='repo-project-input'
aria-describedby='project-helper-text'
onChange={(e) => setProject(e.target.value)}
value={project}
Expand All @@ -155,6 +152,7 @@ const AddRepositoryDialog: React.FC<AddRepositoryDialogProps> = ({ open, onClose
<Input
inputProps={{ maxLength: 200, minLength: 3 }}
id='name'
data-testid='repo-name-input'
aria-describedby='name-helper-text'
onChange={(e) => setName(e.target.value)}
value={name}
Expand All @@ -170,6 +168,7 @@ const AddRepositoryDialog: React.FC<AddRepositoryDialogProps> = ({ open, onClose
type='url'
id='url'
aria-describedby='url-helper-text'
data-testid='repo-url-input'
onChange={(e) => setUrl(e.target.value)}
value={url}
/>
Expand All @@ -181,7 +180,7 @@ const AddRepositoryDialog: React.FC<AddRepositoryDialogProps> = ({ open, onClose
<Button variant='outlined' color='warning' onClick={handleClose}>
Cancel
</Button>
<Button variant='outlined' color='success' onClick={add}>
<Button variant='outlined' color='success' onClick={add} data-testid='add-repo-button'>
Add
</Button>
</div>
Expand All @@ -207,7 +206,7 @@ const NewRepo: React.FC<NewRepoProps> = ({ onSuccess }) => {

return (
<div>
<Button color='success' onClick={handleClickOpen}>
<Button color='success' onClick={handleClickOpen} data-testid='add-repo-button'>
<RepoIcon /> Add repository
</Button>
<AddRepositoryDialog open={open} onClose={handleClose} onSuccess={onSuccess} />
Expand Down
2 changes: 1 addition & 1 deletion src/ui/views/RepoList/Components/TabList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Repositories from './Repositories';

export default function Dashboard(): React.ReactElement {
return (
<div>
<div data-testid="repo-list-view">
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<Repositories />
Expand Down
Loading