Skip to content

Commit 2c26dfd

Browse files
committed
feat: merge Upstream/main
2 parents 4a2a297 + 94d807d commit 2c26dfd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+592
-328
lines changed

cypress.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,13 @@ module.exports = defineConfig({
44
e2e: {
55
baseUrl: process.env.CYPRESS_BASE_URL || 'http://localhost:3000',
66
chromeWebSecurity: false, // Required for OIDC testing
7+
setupNodeEvents(on, config) {
8+
on('task', {
9+
log(message) {
10+
console.log(message);
11+
return null;
12+
},
13+
});
14+
},
715
},
816
});

cypress/e2e/repo.cy.js

Lines changed: 73 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,83 @@
11
describe('Repo', () => {
2-
beforeEach(() => {
2+
let repoName;
3+
let cloneURL;
4+
let csrfToken;
5+
let cookies;
6+
let repoId;
7+
8+
before(() => {
39
cy.login('admin', 'admin');
410

5-
cy.visit('/dashboard/repo');
11+
// Create a new repo
12+
cy.getCSRFToken().then((csrfToken) => {
13+
repoName = `${Date.now()}`;
14+
cloneURL = `http://localhost:8000/github.com/cypress-test/${repoName}.git`;
15+
16+
cy.request({
17+
method: 'POST',
18+
url: 'http://localhost:8080/api/v1/repo',
19+
body: {
20+
project: 'cypress-test',
21+
name: repoName,
22+
url: `https://github.com/cypress-test/${repoName}.git`,
23+
},
24+
headers: {
25+
cookie: cookies?.join('; ') || '',
26+
'X-CSRF-TOKEN': csrfToken,
27+
},
28+
}).then((res) => {
29+
expect(res.status).to.eq(200);
30+
repoId = res.body._id;
31+
});
32+
});
33+
});
634

7-
// prevent failures on 404 request and uncaught promises
35+
it('Opens tooltip with correct content and can copy', () => {
36+
cy.visit('/dashboard/repo');
837
cy.on('uncaught:exception', () => false);
38+
39+
const tooltipQuery = 'div[role="tooltip"]';
40+
41+
// Check the tooltip isn't open to start with
42+
cy.get(tooltipQuery).should('not.exist');
43+
44+
// Find the repo's Code button and click it
45+
cy.get(`a[href="/dashboard/repo/${repoId}"]`)
46+
.closest('tr')
47+
.find('span')
48+
.contains('Code')
49+
.should('exist')
50+
.click();
51+
52+
// Check tooltip is open and contains the correct clone URL
53+
cy.get(tooltipQuery)
54+
.should('exist')
55+
.find('span')
56+
.contains(cloneURL)
57+
.should('exist')
58+
.parent()
59+
.find('span')
60+
.next()
61+
.get('svg.octicon-copy')
62+
.should('exist')
63+
.click()
64+
.get('svg.octicon-copy')
65+
.should('not.exist')
66+
.get('svg.octicon-check')
67+
.should('exist');
968
});
1069

11-
describe('Code button for repo row', () => {
12-
it('Opens tooltip with correct content and can copy', () => {
13-
const cloneURLRegex = /http:\/\/localhost:8000\/(?:[^\/]+\/).+\.git/;
14-
const tooltipQuery = 'div[role="tooltip"]';
15-
16-
cy
17-
// tooltip isn't open to start with
18-
.get(tooltipQuery)
19-
.should('not.exist');
20-
21-
cy
22-
// find a table row for a repo (any will do)
23-
.get('table#RepoListTable>tbody>tr')
24-
// find the nearby span containing Code we can click to open the tooltip
25-
.find('span')
26-
.contains('Code')
27-
.should('exist')
28-
.click();
29-
30-
cy
31-
// find the newly opened tooltip
32-
.get(tooltipQuery)
33-
.should('exist')
34-
.find('span')
35-
// check it contains the url we expect
36-
.contains(cloneURLRegex)
37-
.should('exist')
38-
.parent()
39-
// find the adjacent span that contains the svg
40-
.find('span')
41-
.next()
42-
// check it has the copy icon first and click it
43-
.get('svg.octicon-copy')
44-
.should('exist')
45-
.click()
46-
// check the icon has changed to the check icon
47-
.get('svg.octicon-copy')
48-
.should('not.exist')
49-
.get('svg.octicon-check')
50-
.should('exist');
51-
52-
// failed to successfully check the clipboard
70+
after(() => {
71+
// Delete the repo
72+
cy.getCSRFToken().then((csrfToken) => {
73+
cy.request({
74+
method: 'DELETE',
75+
url: `http://localhost:8080/api/v1/repo/${repoName}/delete`,
76+
headers: {
77+
cookie: cookies?.join('; ') || '',
78+
'X-CSRF-TOKEN': csrfToken,
79+
},
80+
});
5381
});
5482
});
5583
});

cypress/support/commands.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,25 @@ Cypress.Commands.add('login', (username, password) => {
3939
cy.url().should('include', '/dashboard/repo');
4040
});
4141
});
42+
43+
Cypress.Commands.add('getCSRFToken', () => {
44+
return cy.request('GET', 'http://localhost:8080/api/v1/repo').then((res) => {
45+
let cookies = res.headers['set-cookie'];
46+
47+
if (typeof cookies === 'string') {
48+
cookies = [cookies];
49+
}
50+
51+
if (!cookies) {
52+
throw new Error('No cookies found in response');
53+
}
54+
55+
const csrfCookie = cookies.find((c) => c.startsWith('csrf='));
56+
if (!csrfCookie) {
57+
throw new Error('No CSRF cookie found in response headers');
58+
}
59+
60+
const token = csrfCookie.split('=')[1].split(';')[0];
61+
return cy.wrap(decodeURIComponent(token));
62+
});
63+
});

package-lock.json

Lines changed: 50 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"axios": "^1.11.0",
4848
"bcryptjs": "^3.0.2",
4949
"bit-mask": "^1.0.2",
50-
"classnames": "2.5.1",
50+
"clsx": "^2.1.1",
5151
"concurrently": "^9.2.0",
5252
"connect-mongo": "^5.1.0",
5353
"cors": "^2.8.5",

src/constants/languageColors.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// src/constants/languageColors.ts
2-
31
export const languageColors: Record<string, string> = {
42
'1C Enterprise': '#814CCC',
53
'2-Dimensional Array': '#38761D',

src/routes.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ import User from './ui/views/User/User';
2525
import UserList from './ui/views/UserList/UserList';
2626
import RepoDetails from './ui/views/RepoDetails/RepoDetails';
2727
import RepoList from './ui/views/RepoList/RepoList';
28+
import SettingsView from './ui/views/Settings/Settings';
2829

2930
import { RepoIcon } from '@primer/octicons-react';
30-
import { Group, AccountCircle, Dashboard } from '@material-ui/icons';
31+
import { Group, AccountCircle, Dashboard, Settings } from '@material-ui/icons';
32+
3133
import { Route } from './types/models';
3234

3335
const dashboardRoutes: Route[] = [
@@ -97,6 +99,16 @@ const dashboardRoutes: Route[] = [
9799
layout: '/dashboard',
98100
visible: false,
99101
},
102+
{
103+
path: '/admin/settings',
104+
name: 'Settings',
105+
icon: Settings,
106+
component: (props) => (
107+
<RouteGuard component={SettingsView} fullRoutePath={`/dashboard/admin/settings`} />
108+
),
109+
layout: '/dashboard',
110+
visible: true,
111+
},
100112
];
101113

102114
export default dashboardRoutes;

src/service/passport/jwtAuthHandler.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ const jwtAuthHandler = (overrideConfig = null) => {
1616
return next();
1717
}
1818

19-
if (req.isAuthenticated()) {
20-
return next();
21-
}
22-
2319
const token = req.header('Authorization');
2420
if (!token) {
2521
return res.status(401).send('No token provided\n');
@@ -29,11 +25,15 @@ const jwtAuthHandler = (overrideConfig = null) => {
2925
const audience = expectedAudience || clientID;
3026

3127
if (!authorityURL) {
32-
return res.status(500).send('OIDC authority URL is not configured\n');
28+
return res.status(500).send({
29+
message: 'JWT handler: authority URL is not configured\n',
30+
});
3331
}
3432

3533
if (!clientID) {
36-
return res.status(500).send('OIDC client ID is not configured\n');
34+
return res.status(500).send({
35+
message: 'JWT handler: client ID is not configured\n',
36+
});
3737
}
3838

3939
const tokenParts = token.split(' ');

src/ui/components/Card/Card.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import classNames from 'classnames';
2+
import clsx from 'clsx';
33
import { makeStyles } from '@material-ui/core/styles';
44
import styles from '../../assets/jss/material-dashboard-react/components/cardStyle';
55

@@ -23,7 +23,7 @@ const Card: React.FC<CardProps> = ({
2323
}) => {
2424
const classes = useStyles();
2525

26-
const cardClasses = classNames({
26+
const cardClasses = clsx({
2727
[classes.card]: true,
2828
[classes.cardPlain]: plain,
2929
[classes.cardProfile]: profile,

0 commit comments

Comments
 (0)