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
5 changes: 4 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
"@troyblank/eslint-config-troyblank/configs/react.js",
"@troyblank/eslint-config-troyblank/configs/jest.js",
"@troyblank/eslint-config-troyblank/configs/typescript.js"
]
],
"rules": {
"react/react-in-jsx-scope": "off"
}
}
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '20'
node-version-file: .nvmrc
- run: yarn install
- run: yarn lint
unit:
Expand All @@ -16,6 +16,6 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '20'
node-version-file: .nvmrc
- run: yarn install
- run: yarn unit:once
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v20.11.0
v24.11.0
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const customJestConfig = {
moduleDirectories: ["node_modules", "<rootDir>/"],
testEnvironment: "jest-environment-jsdom",
moduleNameMapper:{"^uuid$": "uuid"},
setupFiles: ["<rootDir>/jest.polyfills.js"],
setupFilesAfterEnv: ["<rootDir>/config/jest/setup.js"],
collectCoverage: true,
coverageReporters: ['lcov', 'text-summary'],
Expand Down
6 changes: 6 additions & 0 deletions jest.polyfills.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const v8 = require('node:v8')

// jsdom does not provide structuredClone; Amplify adapter-nextjs needs it at import time.
if (typeof globalThis.structuredClone !== 'function') {
globalThis.structuredClone = (value) => v8.deserialize(v8.serialize(value))
}
3 changes: 2 additions & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/dev/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
8 changes: 1 addition & 7 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
eslint: {
ignoreDuringBuilds: true,
},
webpack: (config) => {
config.resolve.fallback = { fs: false };
return config;
}
turbopack: {},
};

module.exports = nextConfig;
46 changes: 23 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "troyblank-admin",
"version": "1.0.0",
"packageManager": "yarn@1.22.21",
"packageManager": "yarn@1.22.22",
"scripts": {
"start": "yarn clean && yarn watch",
"clean": "rm -rf dist && rm -rf coverage",
Expand All @@ -13,32 +13,32 @@
"unit:once": "jest --maxWorkers=25%"
},
"dependencies": {
"@aws-amplify/adapter-nextjs": "1.0.17",
"@aws-amplify/auth": "6.0.17",
"aws-amplify": "6.0.17"
"@aws-amplify/adapter-nextjs": "1.7.3",
"@aws-amplify/auth": "6.20.0",
"aws-amplify": "6.17.0"
},
"devDependencies": {
"@testing-library/dom": "9.3.4",
"@testing-library/jest-dom": "6.3.0",
"@testing-library/react": "14.1.2",
"@testing-library/user-event": "14.5.2",
"@types/node": "20.11.7",
"@types/react": "18.2.48",
"@types/react-dom": "18.2.18",
"@testing-library/dom": "10.4.1",
"@testing-library/jest-dom": "6.9.1",
"@testing-library/react": "16.3.2",
"@testing-library/user-event": "14.6.1",
"@types/node": "25.9.1",
"@types/react": "19.2.15",
"@types/react-dom": "19.2.3",
"@types/react-router-dom": "5.3.3",
"@troyblank/eslint-config-troyblank": "2.4.0",
"@types/chance": "1.1.6",
"@types/jest": "29.5.11",
"chance": "1.1.11",
"eslint-config-next": "14.0.4",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"next": "14.0.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-router-dom": "6.21.3",
"ts-jest": "29.1.2",
"typescript": "5.3.3"
"@types/chance": "1.1.8",
"@types/jest": "30.0.0",
"chance": "1.1.13",
"eslint-config-next": "16.2.6",
"jest": "30.4.2",
"jest-environment-jsdom": "30.4.1",
"next": "16.2.6",
"react": "19.2.6",
"react-dom": "19.2.6",
"react-router-dom": "7.15.1",
"ts-jest": "29.4.11",
"typescript": "6.0.3"
},
"author": "Troy Blank",
"license": "BSD-3-Clause"
Expand Down
5 changes: 3 additions & 2 deletions src/components/changePasswordForm/changePasswordForm.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { fireEvent, render, waitFor } from '@testing-library/react'
import Chance from 'chance'
import { useAuth } from '../../contexts/auth'
Expand Down Expand Up @@ -31,6 +30,8 @@ describe('Change Password Form', () => {
fireEvent.click(getByText(SUBMIT_LABEL))
})

expect(getByText('Your password changed successfully!')).toBeInTheDocument()
await waitFor(() => {
expect(getByText('Your password changed successfully!')).toBeInTheDocument()
})
})
})
2 changes: 1 addition & 1 deletion src/components/changePasswordForm/changePasswordForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, type SyntheticEvent } from 'react'
import { useState, type SyntheticEvent } from 'react'
import { useAuth } from '../../contexts/auth'
import { NEW_PASSWORD_ID, OLD_PASSWORD_ID, SUBMIT_LABEL } from './constants'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { fireEvent, render } from '@testing-library/react'
import Chance from 'chance'
import { useRouter } from 'next/navigation'
Expand Down Expand Up @@ -41,7 +40,7 @@ describe('Complete New User Form', () => {

fireEvent.click(getByText(SUBMIT_LABEL))

expect(attemptToCompleteNewUser).toBeCalledWith(password, {
expect(attemptToCompleteNewUser).toHaveBeenCalledWith(password, {
family_name: lastName,
given_name: firstName,
})
Expand Down
2 changes: 1 addition & 1 deletion src/components/completeNewUserForm/completeNewUserForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, Fragment, SyntheticEvent } from 'react'
import { useState, Fragment, SyntheticEvent } from 'react'
import { useRouter } from 'next/navigation'
import { HOME_PATH } from '../../utils'
import { useAuth } from '../../contexts'
Expand Down
13 changes: 9 additions & 4 deletions src/components/forgotPasswordForm/forgotPasswordForm.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { fireEvent, render, waitFor } from '@testing-library/react'
import Chance from 'chance'
import { useSearchParams } from 'next/navigation'
Expand Down Expand Up @@ -38,7 +37,9 @@ describe('Forgot Password Form', () => {
fireEvent.click(getByText(SUBMIT_LABEL))
})

expect(getByText('A reset password confirmation code was just sent to your email.', { exact: false })).toBeInTheDocument()
await waitFor(() => {
expect(getByText('A reset password confirmation code was just sent to your email.', { exact: false })).toBeInTheDocument()
})

await waitFor(() => {
fireEvent.change(container.querySelector(`input[name="${CONFIRMATION_CODE_ID}"]`) as Element, { target: { value: String(chance.guid()) } })
Expand All @@ -47,7 +48,9 @@ describe('Forgot Password Form', () => {
fireEvent.click(getByText(SUBMIT_LABEL))
})

expect(getByText('Your password has been reset.')).toBeInTheDocument()
await waitFor(() => {
expect(getByText('Your password has been reset.')).toBeInTheDocument()
})
})

it('should be able to attempt to get a reset password code and reset a password with no redirect query param', async() => {
Expand Down Expand Up @@ -75,6 +78,8 @@ describe('Forgot Password Form', () => {
fireEvent.click(getByText(SUBMIT_LABEL))
})

expect(getByText('Your password has been reset.')).toBeInTheDocument()
await waitFor(() => {
expect(getByText('Your password has been reset.')).toBeInTheDocument()
})
})
})
1 change: 0 additions & 1 deletion src/components/profileGreeting/profileGreeting.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { render } from '@testing-library/react'
import Chance from 'chance'
import { mockUser } from '../../mocks'
Expand Down
2 changes: 1 addition & 1 deletion src/components/profileGreeting/profileGreeting.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Fragment } from 'react'
import { Fragment } from 'react'
import { useAuth } from '../../contexts'

export const ProfileGreeting = () => {
Expand Down
1 change: 0 additions & 1 deletion src/components/signInForm/signInForm.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { fireEvent, render, screen } from '@testing-library/react'
import Chance from 'chance'
import { useRouter } from 'next/navigation'
Expand Down
1 change: 0 additions & 1 deletion src/components/tokenBox/tokenBox.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { fireEvent, render } from '@testing-library/react'
import Chance from 'chance'
import { mockUser } from '../../mocks'
Expand Down
2 changes: 1 addition & 1 deletion src/components/tokenBox/tokenBox.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, Fragment } from 'react'
import { useState, Fragment } from 'react'
import { useAuth } from '../../contexts'

export const TokenBox = () => {
Expand Down
46 changes: 45 additions & 1 deletion src/contexts/auth.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import Chance from 'chance'
import { type UserAttributes } from '../types'
// import { mockRequiredNewUserAttributes } from '../mocks'
import { AuthProvider, useAuth } from './auth'
import { AuthProvider, DEFAULT_ERROR_MESSAGE, useAuth } from './auth'

jest.mock('aws-amplify/auth')

Expand Down Expand Up @@ -130,6 +130,17 @@ describe('Use Auth', () => {
expect(async() => await attemptToGetResetPasswordCode(chance.email())).rejects.toThrow(error)
})

it('should use the default error message when reset password code errors are not Error instances', async () => {
jest.mocked(resetPassword).mockRejectedValue('not an Error')
jest.spyOn(window, 'alert')

const { result } = renderHook(() => useAuth(), { wrapper: AuthProvider as any })

const { attemptToGetResetPasswordCode } = result.current

expect(async() => await attemptToGetResetPasswordCode(chance.email())).rejects.toThrow(DEFAULT_ERROR_MESSAGE)
})

it('should handle any errors with attempts to get a reset password code', async () => {
jest.mocked(resetPassword).mockResolvedValue({
isPasswordReset: true,
Expand Down Expand Up @@ -195,6 +206,23 @@ describe('Use Auth', () => {
expect(async() => await attemptToResetPassword(confirmResetPasswordInput)).rejects.toThrow(error)
})

it('should use the default error message when reset password errors are not Error instances', async () => {
const confirmResetPasswordInput: ConfirmResetPasswordInput = {
username: chance.name(),
confirmationCode: chance.guid(),
newPassword: chance.word(),
}

jest.mocked(confirmResetPassword).mockRejectedValue('not an Error')
jest.spyOn(window, 'alert')

const { result } = renderHook(() => useAuth(), { wrapper: AuthProvider as any })

const { attemptToResetPassword } = result.current

expect(async() => await attemptToResetPassword(confirmResetPasswordInput)).rejects.toThrow(DEFAULT_ERROR_MESSAGE)
})

it('should an attempt to change a password', async () => {
const updatePasswordInput: UpdatePasswordInput = {
oldPassword: chance.word(),
Expand Down Expand Up @@ -228,4 +256,20 @@ describe('Use Auth', () => {

expect(async() => await attemptToChangePassword(updatePasswordInput)).rejects.toThrow(error)
})

it('should use the default error message when change password errors are not Error instances', async () => {
const updatePasswordInput: UpdatePasswordInput = {
oldPassword: chance.word(),
newPassword: chance.word(),
}

jest.mocked(updatePassword).mockRejectedValue('not an Error')
jest.spyOn(window, 'alert')

const { result } = renderHook(() => useAuth(), { wrapper: AuthProvider as any })

const { attemptToChangePassword } = result.current

expect(async() => await attemptToChangePassword(updatePasswordInput)).rejects.toThrow(DEFAULT_ERROR_MESSAGE)
})
})
1 change: 0 additions & 1 deletion src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { AppProps } from 'next/app'

export const App = ({
Expand Down
2 changes: 1 addition & 1 deletion src/pages/completeNewUser.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Fragment } from 'react'
import { Fragment } from 'react'
import { GetServerSidePropsContext } from 'next'
import { type User } from '../types'
import { AuthProvider } from '../contexts'
Expand Down
1 change: 0 additions & 1 deletion src/pages/forgotPassword.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { AuthProvider } from '../contexts'
import { ForgotPasswordForm } from '../components'

Expand Down
2 changes: 1 addition & 1 deletion src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Fragment } from 'react'
import { Fragment } from 'react'
import { GetServerSidePropsContext } from 'next'
import { type User } from '../types'
import { AuthProvider } from '../contexts'
Expand Down
1 change: 0 additions & 1 deletion src/pages/signIn.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react'
import { type User } from '../types'
import { AuthProvider } from '../contexts'
import { SignInForm } from '../components'
Expand Down
7 changes: 3 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"compilerOptions": {
"target": "ES2020",
"baseUrl": "./",
"outDir": "./dist",
"module": "esnext",
"allowJs": true,
Expand All @@ -13,10 +12,10 @@
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"moduleResolution": "node",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsx": "react-jsx",
"lib": [
"dom",
"dom.iterable",
Expand All @@ -30,7 +29,7 @@
"types": [
"node",
"jest",
"@testing-library/jest-dom"
"@testing-library/jest-dom"
]
},
"include": [
Expand Down
Loading
Loading