Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @smartcontractkit/core @smartcontractkit/dapp-team
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ jobs:
unit-tests-matrix:
strategy:
matrix:
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
Comment thread
RensR marked this conversation as resolved.
shardIndex: [1]
shardTotal: [1]

name: Unit Tests ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
runs-on: ubuntu-latest
Expand Down
37 changes: 21 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,18 @@
},
"dependencies": {
"@apollo/client": "^3.14.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@iarna/toml": "3.0.0",
"@material-ui/core": "^3.9.4",
"@material-ui/icons": "4.5.1",
"@mui/icons-material": "^5.17.1",
"@mui/material": "^5.17.1",
"bignumber.js": "^9.3.1",
"buffer": "^6.0.3",
"classnames": "^2.5.1",
"cookie": "^0.7.2",
"d3": "~5.16.0",
"elkjs": "^0.11.1",
"formik": "^2.4.9",
"formik-material-ui": "^3.0.1",
"graphlib-dot": "^0.6.4",
"graphql": "16.13.2",
"isomorphic-unfetch": "^3.1.0",
Expand All @@ -47,10 +48,10 @@
"moment": "^2.30.1",
"path-to-regexp": "^3.3.0",
"prop-types": "^15.8.1",
"react": "^16.14.0",
"react": "^18.3.1",
"react-copy-to-clipboard": "^5.1.1",
"react-d3-graph": "^2.6.0",
"react-dom": "^16.14.0",
"react-dom": "^18.3.1",
"react-redux": "^7.2.9",
"react-resize-detector": "^4.2.3",
"react-router-dom": "^5.3.4",
Expand All @@ -59,6 +60,7 @@
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"stream-browserify": "^3.0.0",
"tss-react": "^4.9.20",
"yup": "^0.32.11"
},
"devDependencies": {
Expand All @@ -73,17 +75,16 @@
"@swc/core": "^1.15.26",
"@swc/jest": "^0.2.39",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^12.1.5",
"@testing-library/react-hooks": "^7.0.2",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/cookie": "^0.6.0",
"@types/d3": "~5.16.7",
"@types/jest": "^30.0.0",
"@types/lodash": "^4.17.24",
"@types/node": "^24.12.2",
"@types/react": "^16.14.69",
"@types/react": "18.3.12",
"@types/react-d3-graph": "^2.6.7",
"@types/react-dom": "^16.9.25",
"@types/react-dom": "18.3.1",
"@types/react-redux": "^7.1.34",
"@types/react-resize-detector": "^4.2.0",
"@types/react-router-dom": "^5.3.3",
Expand Down Expand Up @@ -116,22 +117,26 @@
"terser-webpack-plugin": "^5.4.0",
"ts-node": "^10.9.1",
"tsconfig-paths-webpack-plugin": "^4.2.0",
"typescript": "^4.9.5",
"typescript": "^5.9.3",
"undici": "^8.1.0",
"web-worker": "^1.5.0",
"webpack": "^5.106.2",
"webpack-cli": "^7.0.2",
"webpack-dev-server": "^5.2.3"
},
"resolutions": {
"@types/react": "16.14.69",
"@types/react-dom": "16.9.25",
"lodash": "4.18.1"
"lodash": "4.18.1",
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
"js-yaml": "^3.14.2",
"semver": "^6.3.1"
},
"overrides": {
"@types/react": "^16.14.69",
"@types/react-dom": "^16.9.25",
"lodash": "^4.18.1"
"lodash": "4.18.1",
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
"js-yaml": "^3.14.2",
"semver": "^6.3.1"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
2 changes: 1 addition & 1 deletion src/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
Redirect,
BrowserRouter as Router,
} from 'react-router-dom'
import CssBaseline from '@material-ui/core/CssBaseline'
import CssBaseline from '@mui/material/CssBaseline'
import Private from './Private'
import { useOperatorUiSelector } from 'reducers'
import SignIn from 'pages/SignIn'
Expand Down
8 changes: 4 additions & 4 deletions src/Private.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react'
import { Theme } from '@material-ui/core/styles'
import Grid from '@material-ui/core/Grid'
import { Theme } from '@mui/material/styles'
import Grid from '@mui/material/Grid'

import { Switch } from 'react-router-dom'
import { withStyles } from '@material-ui/core/styles'
import { withStyles } from 'src/utils/withStyles'
import Header from 'pages/Header'
import Notifications from 'pages/Notifications'
import PrivateRoute from './PrivateRoute'
Expand All @@ -27,7 +27,7 @@ const styles = (theme: Theme) => {
return {
content: {
marginTop: 0,
marginBottom: theme.spacing.unit * 5,
marginBottom: theme.spacing(5),
},
}
}
Expand Down
39 changes: 30 additions & 9 deletions src/actionCreators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ const curryErrorHandler =
}
}

export const notifySuccess = (component: React.ReactNode, props: object) => {
export const notifySuccess = (
component: (props: any) => React.ReactNode,
props: object,
) => {
return {
type: NotifyActionType.NOTIFY_SUCCESS,
component,
Expand All @@ -70,7 +73,10 @@ export const notifySuccessMsg = (msg: string) => ({
msg,
})

export const notifyError = (component: React.ReactNode, error: Error) =>
export const notifyError = (
component: (props: any) => React.ReactNode,
error: Error,
) =>
({
type: NotifyActionType.NOTIFY_ERROR,
component,
Expand Down Expand Up @@ -109,7 +115,9 @@ const signInSuccessAction = (doc: UnboxApi<Sessions['createSession']>) => {
}

const signInFailAction = () =>
({ type: AuthActionType.RECEIVE_SIGNIN_FAIL }) as const
({
type: AuthActionType.RECEIVE_SIGNIN_FAIL,
}) as const

function sendSignIn(data: Parameter<Sessions['createSession']>) {
return (dispatch: StoreDispatch) => {
Expand Down Expand Up @@ -247,9 +255,15 @@ function sendSignOut(dispatch: Dispatch) {
})
}

// Base64 to ArrayBuffer
function bufferDecode(value: any) {
return Uint8Array.from(atob(value), (c) => c.charCodeAt(0))
// Base64URL to ArrayBuffer
// atob() requires standard Base64; the server sends Base64URL (- and _ instead
// of + and /) without padding, so we must normalise before decoding.
function bufferDecode(value: string) {
const base64 = value
.replace(/-/g, '+')
.replace(/_/g, '/')
.padEnd(value.length + ((4 - (value.length % 4)) % 4), '=')
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0))
}

// ArrayBuffer to URLBase64
Expand Down Expand Up @@ -338,13 +352,20 @@ export const submitSignIn = (data: Parameter<Sessions['createSession']>) =>

export const submitSignOut = () => sendSignOut

export const beginRegistration = () => sendBeginRegistration()
export const beginRegistration = (): ThunkAction<
ReturnType<typeof sendBeginRegistration>,
AppState,
void,
Action<string>
> => {
return () => sendBeginRegistration()
}

export const createJobRunV2 = (
id: string,
pipelineInput: string,
successCallback: React.ReactNode,
errorCallback: React.ReactNode,
successCallback: (props: any) => React.ReactNode,
errorCallback: (props: any) => React.ReactNode,
): ThunkAction<Promise<void>, AppState, void, Action<string>> => {
return (dispatch: Dispatch) => {
dispatch({ type: ResourceActionType.REQUEST_CREATE })
Expand Down
2 changes: 1 addition & 1 deletion src/api/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class Sessions {
}

public destroySession = (): Promise<
jsonapi.ApiResponse<sessionsController.Session>
jsonapi.PaginatedApiResponse<sessionsController.Session>
> => {
return this.destroy()
}
Expand Down
58 changes: 58 additions & 0 deletions src/components/AccountMenu.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react'
import userEvent from '@testing-library/user-event'
import { render, screen, waitFor } from 'support/test-utils'

jest.mock('../api', () => ({
__esModule: true,
v2: {
webauthn: {
beginKeyRegistration: jest.fn(),
finishKeyRegistration: jest.fn(),
},
},
sessions: {
destroySession: jest.fn(),
},
}))

import * as api from '../api'
import { AccountMenu } from './AccountMenu'

describe('AccountMenu', () => {
const alertSpy = jest.spyOn(window, 'alert').mockImplementation(() => {})

beforeEach(() => {
jest.clearAllMocks()
Object.defineProperty(window.navigator, 'credentials', {
configurable: true,
value: {},
})
})

afterAll(() => {
alertSpy.mockRestore()
})

it('keeps the UI responsive when MFA registration fails on the server', async () => {
;(
api.v2.webauthn.beginKeyRegistration as jest.MockedFunction<
typeof api.v2.webauthn.beginKeyRegistration
>
).mockRejectedValue(new Error('server error'))

render(<AccountMenu />)

userEvent.click(screen.getByRole('button'))
userEvent.click(
screen.getByRole('menuitem', { name: /register mfa token/i }),
)

await waitFor(() => {
expect(alertSpy).toHaveBeenCalledWith(
expect.stringContaining('Key registration error'),
)
})

expect(screen.getByRole('button')).toBeInTheDocument()
})
})
28 changes: 18 additions & 10 deletions src/components/AccountMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import React from 'react'
import { useDispatch } from 'react-redux'

import AccountCircleIcon from '@material-ui/icons/AccountCircle'
import IconButton from '@material-ui/core/IconButton'
import Menu from '@material-ui/core/Menu'
import MenuItem from '@material-ui/core/MenuItem'
import { Theme, withStyles, WithStyles } from '@material-ui/core/styles'
import AccountCircleIcon from '@mui/icons-material/AccountCircle'
import IconButton from '@mui/material/IconButton'
import Menu from '@mui/material/Menu'
import MenuItem from '@mui/material/MenuItem'
import { Theme } from '@mui/material/styles'

import { WithStyles } from 'src/utils/withStyles'
import { withStyles } from 'src/utils/withStyles'

import { beginRegistration, submitSignOut } from 'actionCreators'

Expand All @@ -19,6 +22,9 @@ const styles = (theme: Theme) => {
paddingTop: 0,
paddingBottom: 0,
},
menuItem: {
minHeight: theme.spacing(6),
},
}
}

Expand Down Expand Up @@ -48,14 +54,12 @@ export const AccountMenu = withStyles(styles)(({ classes }: Props) => {

return (
<React.Fragment>
<IconButton disableRipple onClick={handleOpen}>
<IconButton disableRipple onClick={handleOpen} size="large">
<AccountCircleIcon className={classes.accountButton} />
</IconButton>

<Menu
id="account-menu"
anchorEl={anchorEl}
getContentAnchorEl={null}
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
transformOrigin={{ vertical: 8, horizontal: 64 }}
open={Boolean(anchorEl)}
Expand All @@ -65,9 +69,13 @@ export const AccountMenu = withStyles(styles)(({ classes }: Props) => {
className: classes.menuList,
}}
>
<MenuItem onClick={handleRegisterMFA}>Register MFA Token</MenuItem>
<MenuItem className={classes.menuItem} onClick={handleRegisterMFA}>
Register MFA Token
</MenuItem>

<MenuItem onClick={handleLogOut}>Log out</MenuItem>
<MenuItem className={classes.menuItem} onClick={handleLogOut}>
Log out
</MenuItem>
</Menu>
</React.Fragment>
)
Expand Down
3 changes: 2 additions & 1 deletion src/components/BaseLink.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { render, screen } from '@testing-library/react'
import BaseLink from '../../src/components/BaseLink'

const { getByRole, getByText } = screen
const Router = MemoryRouter as any

const renderBaseLink = (link: React.ReactNode) =>
render(<MemoryRouter>{link}</MemoryRouter>)
render(<Router>{link}</Router>)

describe('components/BaseLink', () => {
it('renders an anchor', () => {
Expand Down
13 changes: 9 additions & 4 deletions src/components/BaseLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ interface Props extends Omit<LinkProps, 'to'> {
href: string
}

const BaseLink = ({ href, children, ...rest }: Props) => (
<Link to={href} {...rest}>
{children}
</Link>
// forwardRef is required so MUI v5's ButtonBase can attach a ref when this
// component is passed as the `component` prop.
const BaseLink = React.forwardRef<HTMLAnchorElement, Props>(
({ href, children, ...rest }, ref) => (
<Link to={href} ref={ref} {...rest}>
{children}
</Link>
),
)
BaseLink.displayName = 'BaseLink'

export default BaseLink
Loading
Loading