diff --git a/src/features/login/EnforcePolicies.test.tsx b/src/features/login/EnforcePolicies.test.tsx
new file mode 100644
index 00000000..e4d2c059
--- /dev/null
+++ b/src/features/login/EnforcePolicies.test.tsx
@@ -0,0 +1,111 @@
+import { ThemeProvider } from '@emotion/react';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { Provider } from 'react-redux';
+import { BrowserRouter } from 'react-router-dom';
+import { createTestStore } from '../../app/store';
+import { theme } from '../../theme';
+import { noOp } from '../common';
+import { EnforcePolicies } from './EnforcePolicies';
+
+jest.mock('./Policies', () => ({
+ ...jest.requireActual('./Policies'),
+ kbasePolicies: {
+ 'kbase-user': {
+ raw: '---\ntitle: KBase Terms and Conditions\nid: kbase-user\nversion: 1\nequivalentVersions: []\n---\nsome content',
+ markdown: 'some content',
+ title: 'KBase Terms and Conditions',
+ id: 'kbase-user',
+ version: '2',
+ equivalentVersions: [],
+ },
+ 'test-policy': {
+ raw: '---\ntitle: Test Policy\nid: test-policy\nversion: 1\nequivalentVersions: []\n---\ntest content',
+ markdown: 'test content',
+ title: 'Test Policy',
+ id: 'test-policy',
+ version: '1',
+ equivalentVersions: [],
+ },
+ },
+}));
+
+const renderWithProviders = (
+ ui: React.ReactElement,
+ { store = createTestStore() } = {}
+) => {
+ return render(
+
+
+
+ {ui}
+
+
+
+ );
+};
+
+describe('EnforcePolicies', () => {
+ it('renders default message', () => {
+ renderWithProviders(
+
+ );
+ expect(
+ screen.getByText(
+ 'To continue to your account, you must agree to the following KBase use policies.'
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('renders special v2 policy message', () => {
+ renderWithProviders(
+
+ );
+ expect(
+ screen.getByText(
+ "KBase's recent renewal (Oct '2024) has prompted an update and version 2 release to our Terms and Conditions. Please review and agree to these policies changes to continue using this free resource."
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('disables accept button until all policies are accepted', async () => {
+ const mockAccept = jest.fn();
+ renderWithProviders(
+
+ );
+
+ const acceptButton = screen.getByRole('button', {
+ name: /agree and continue/i,
+ });
+ expect(acceptButton).toBeDisabled();
+
+ const checkbox = screen.getByTestId('policy-checkbox');
+ await userEvent.click(checkbox);
+
+ expect(acceptButton).toBeEnabled();
+ });
+
+ it('calls onAccept when accept button clicked', async () => {
+ const mockAccept = jest.fn();
+ renderWithProviders(
+
+ );
+
+ const checkbox = screen.getByTestId('policy-checkbox');
+ await userEvent.click(checkbox);
+
+ const acceptButton = screen.getByRole('button', {
+ name: /agree and continue/i,
+ });
+ await userEvent.click(acceptButton);
+
+ expect(mockAccept).toHaveBeenCalledWith(['kbase-user.2']);
+ });
+ it('throws error when policy does not exist', () => {
+ expect(() =>
+ renderWithProviders(
+
+ )
+ ).toThrow('Required policy "non-existent-policy" cannot be loaded');
+ });
+});
diff --git a/src/features/login/EnforcePolicies.tsx b/src/features/login/EnforcePolicies.tsx
index de196255..2c779b88 100644
--- a/src/features/login/EnforcePolicies.tsx
+++ b/src/features/login/EnforcePolicies.tsx
@@ -1,10 +1,22 @@
import { faArrowRight } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Alert, Button, Container, Paper } from '@mui/material';
+import {
+ Alert,
+ Button,
+ Container,
+ Paper,
+ Box,
+ Checkbox,
+ FormControl,
+ FormControlLabel,
+ Typography,
+} from '@mui/material';
import { Stack } from '@mui/system';
import { useState } from 'react';
import classes from '../signup/SignUp.module.scss';
-import { kbasePolicies, PolicyViewer } from './Policies';
+import { kbasePolicies } from './Policies';
+import createDOMPurify from 'dompurify';
+import { marked } from 'marked';
export const EnforcePolicies = ({
policyIds,
@@ -14,13 +26,17 @@ export const EnforcePolicies = ({
onAccept: (versionedPolicyIds: string[]) => void;
}) => {
// Get policy information
- const targetPolicies = policyIds.map((id) => kbasePolicies[id]);
+ const targetPolicies = policyIds.map((id) => {
+ if (!kbasePolicies[id])
+ throw new Error(`Required policy "${id}" cannot be loaded`);
+ return kbasePolicies[id];
+ });
const [accepted, setAccepted] = useState<{
[k in typeof targetPolicies[number]['id']]?: boolean;
}>({});
- const allAccepted = targetPolicies.every(
- (policy) => accepted[policy.id] === true
- );
+ const allAccepted = targetPolicies.every((policy) => {
+ return accepted[policy.id] === true;
+ });
// Message to user, uses a special message when agreeing to kbase-user.2
let message =
@@ -77,3 +93,47 @@ export const EnforcePolicies = ({
);
};
+
+const purify = createDOMPurify(window);
+
+export const PolicyViewer = ({
+ policyId,
+ setAccept,
+ accepted = false,
+}: {
+ policyId: string;
+ setAccept: (accepted: boolean) => void;
+ accepted?: boolean;
+}) => {
+ const policy = kbasePolicies[policyId];
+ if (!policy)
+ throw new Error(`Required policy "${policyId}" cannot be loaded`);
+ return (
+
+ {policy.title}
+
+
+
+
+
+ {
+ setAccept(e.currentTarget.checked);
+ }}
+ />
+ }
+ label="I have read and agree to this policy"
+ />
+
+
+
+ );
+};
diff --git a/src/features/login/LogInContinue.test.tsx b/src/features/login/LogInContinue.test.tsx
index e0a8b2a6..7a09ae38 100644
--- a/src/features/login/LogInContinue.test.tsx
+++ b/src/features/login/LogInContinue.test.tsx
@@ -245,4 +245,52 @@ describe('Login Continue', () => {
expect(Login).toHaveBeenCalled();
});
});
+
+ it('handles new user signup flow', async () => {
+ // getLoginChoice - return create data instead of login data
+ fetchMock.mockResponseOnce(
+ JSON.stringify({
+ login: [],
+ create: [
+ {
+ id: 'newuserid',
+ provider: 'google',
+ username: 'newuser@google.com',
+ },
+ ],
+ })
+ );
+
+ const Signup = jest.fn(() => <>>);
+ const store = createTestStore();
+ render(
+
+
+
+
+ } />
+
+
+
+
+
+ );
+
+ await waitFor(() => {
+ // Check that login data was set in store
+ expect(store.getState().signup.loginData).toEqual({
+ login: [],
+ create: [
+ {
+ id: 'newuserid',
+ provider: 'google',
+ username: 'newuser@google.com',
+ },
+ ],
+ });
+ });
+ await waitFor(() => {
+ expect(window.location.pathname === '/signup/2');
+ });
+ });
});
diff --git a/src/features/login/Policies.tsx b/src/features/login/Policies.tsx
index 8d6bbeee..07196036 100644
--- a/src/features/login/Policies.tsx
+++ b/src/features/login/Policies.tsx
@@ -1,21 +1,8 @@
import policyStrings from 'kbase-policies';
import frontmatter from 'front-matter';
-import {
- Box,
- Checkbox,
- FormControl,
- FormControlLabel,
- Paper,
- Typography,
-} from '@mui/material';
-import classes from './PolicyViewer.module.scss';
-import createDOMPurify from 'dompurify';
-import { marked } from 'marked';
export const ENFORCED_POLICIES = ['kbase-user'];
-const purify = createDOMPurify(window);
-
interface PolicyMeta {
title: string;
id: string;
@@ -46,44 +33,3 @@ export const kbasePolicies = policyStrings.reduce(
}
>
);
-
-export const PolicyViewer = ({
- policyId,
- setAccept,
- accepted = false,
-}: {
- policyId: string;
- setAccept: (accepted: boolean) => void;
- accepted?: boolean;
-}) => {
- const policy = kbasePolicies[policyId];
- if (!policy)
- throw new Error(`Required policy "${policyId}" cannot be loaded`);
- return (
-
- {policy.title}
-
-
-
-
-
- {
- setAccept(e.currentTarget.checked);
- }}
- />
- }
- label="I have read and agree to this policy"
- />
-
-
-
- );
-};
diff --git a/src/features/login/PolicyViewer.module.scss b/src/features/login/PolicyViewer.module.scss
deleted file mode 100644
index c55bbe92..00000000
--- a/src/features/login/PolicyViewer.module.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-.agreement-box {
- border: 1px solid use-color("base");
- border-radius: 4px;
- display: inline-block;
- margin-top: 1rem;
- padding: 1rem;
-
-}
diff --git a/src/features/signup/AccountInformation.test.tsx b/src/features/signup/AccountInformation.test.tsx
new file mode 100644
index 00000000..d491669b
--- /dev/null
+++ b/src/features/signup/AccountInformation.test.tsx
@@ -0,0 +1,138 @@
+import { fireEvent, screen } from '@testing-library/react';
+import { createTestStore } from '../../app/store';
+import { Provider } from 'react-redux';
+import { ThemeProvider } from '@mui/material';
+import { BrowserRouter, useNavigate } from 'react-router-dom';
+import { render } from '@testing-library/react';
+import { AccountInformation } from './AccountInformation';
+import { setLoginData } from './SignupSlice';
+import { theme } from '../../theme';
+import { act } from 'react-dom/test-utils';
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useNavigate: jest.fn(),
+}));
+jest.mock('../../common/api/authService', () => ({
+ ...jest.requireActual('../../common/api/authService'),
+ loginUsernameSuggest: {
+ useQuery: jest.fn().mockReturnValue({
+ currentData: { availablename: 'testuser' },
+ isFetching: false,
+ }),
+ },
+}));
+
+const renderWithProviders = (
+ ui: React.ReactElement,
+ { store = createTestStore() } = {}
+) => {
+ return render(
+
+
+
+ {ui}
+
+
+
+ );
+};
+
+describe('AccountInformation', () => {
+ const mockNavigate = jest.fn();
+
+ beforeEach(() => {
+ (useNavigate as jest.Mock).mockImplementation(() => mockNavigate);
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('redirects to step 1 if no login data', () => {
+ const store = createTestStore();
+ renderWithProviders(, { store });
+ expect(mockNavigate).toHaveBeenCalledWith('/signup/1');
+ });
+
+ test('displays login data from provider', () => {
+ const store = createTestStore();
+ store.dispatch(
+ setLoginData({
+ creationallowed: true,
+ expires: 0,
+ login: [],
+ provider: 'Google',
+ create: [
+ {
+ provemail: 'test@test.com',
+ provfullname: 'Test User',
+ availablename: 'testuser',
+ id: '123',
+ provusername: 'testuser',
+ },
+ ],
+ })
+ );
+ renderWithProviders(, { store });
+ expect(screen.getAllByText(/Google/)[0]).toBeInTheDocument();
+ expect(screen.getAllByText(/test@test.com/)[0]).toBeInTheDocument();
+ });
+
+ test('form submission with valid data', async () => {
+ const store = createTestStore();
+ store.dispatch(
+ setLoginData({
+ creationallowed: true,
+ expires: 0,
+ login: [],
+ provider: 'Google',
+ create: [
+ {
+ provemail: 'test@test.com',
+ provfullname: 'Test User',
+ availablename: 'testuser',
+ id: '123',
+ provusername: 'testuser',
+ },
+ ],
+ })
+ );
+ renderWithProviders(, { store });
+
+ await act(() => {
+ fireEvent.change(screen.getByRole('textbox', { name: /Full Name/i }), {
+ target: { value: 'Test User' },
+ });
+ });
+ await act(() => {
+ fireEvent.change(screen.getByRole('textbox', { name: /Email/i }), {
+ target: { value: 'test@test.com' },
+ });
+ });
+ await act(() => {
+ fireEvent.change(
+ screen.getByRole('textbox', { name: /KBase Username/i }),
+ {
+ target: { value: 'testuser' },
+ }
+ );
+ });
+ await act(() => {
+ fireEvent.change(screen.getByRole('textbox', { name: /Organization/i }), {
+ target: { value: 'Test Org' },
+ });
+ });
+ await act(() => {
+ fireEvent.change(screen.getByRole('textbox', { name: /Department/i }), {
+ target: { value: 'Test Dept' },
+ });
+ });
+
+ await act(() => {
+ fireEvent.submit(screen.getByTestId('accountinfoform'));
+ });
+
+ expect(mockNavigate).toHaveBeenCalledWith('/signup/3');
+ });
+});
diff --git a/src/features/signup/AccountInformation.tsx b/src/features/signup/AccountInformation.tsx
index 28895be7..7e92c0e4 100644
--- a/src/features/signup/AccountInformation.tsx
+++ b/src/features/signup/AccountInformation.tsx
@@ -17,7 +17,7 @@ import {
TextField,
Typography,
} from '@mui/material';
-import { FC, useEffect, useState } from 'react';
+import { FC, useEffect, useState, Fragment } from 'react';
import { toast } from 'react-hot-toast';
import { useNavigate } from 'react-router-dom';
import { useAppDispatch, useAppSelector } from '../../common/hooks';
@@ -158,7 +158,7 @@ export const AccountInformation: FC<{}> = () => {
-
- Already have an account? Log in
+ Already have an account? Log in
({
+ ...jest.requireActual('react-router-dom'),
+ useNavigate: jest.fn(),
+ useParams: jest.fn(),
+}));
+
+const mockNavigate = jest.fn();
+const mockScrollTo = jest.fn();
+
+const renderWithProviders = (
+ ui: React.ReactElement,
+ { store = createTestStore() } = {}
+) => {
+ return render(
+
+
+
+ {ui}
+
+
+
+ );
+};
+
+describe('SignUp', () => {
+ beforeEach(() => {
+ (useNavigate as jest.Mock).mockReturnValue(mockNavigate);
+ (useParams as jest.Mock).mockReturnValue({ step: '1' });
+ Element.prototype.scrollTo = mockScrollTo;
+ });
+
+ it('renders signup steps', () => {
+ renderWithProviders();
+ expect(screen.getByText('Sign up for KBase')).toBeInTheDocument();
+ expect(
+ screen.getByText('Sign up with a supported provider')
+ ).toBeInTheDocument();
+ expect(screen.getByText('Account information')).toBeInTheDocument();
+ expect(screen.getByText('KBase use policies')).toBeInTheDocument();
+ });
+
+ it('navigates between steps when clicking previous steps', async () => {
+ (useParams as jest.Mock).mockReturnValue({ step: '3' });
+ renderWithProviders();
+
+ const step1 = screen.getByText('Sign up with a supported provider');
+ await userEvent.click(step1);
+ expect(mockNavigate).toHaveBeenCalledWith('/signup/1');
+ expect(mockScrollTo).toHaveBeenCalledWith(0, 0);
+ });
+});
+
+describe('useDoSignup', () => {
+ const mockLoginCreateMutation = jest.fn();
+ const mockSetUserProfileMutation = jest.fn();
+
+ beforeEach(() => {
+ jest.spyOn(loginCreate, 'useMutation').mockReturnValue([
+ mockLoginCreateMutation,
+ {
+ isUninitialized: false,
+ isSuccess: true,
+ data: { token: { token: 'someToken' } },
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ } as any,
+ ]);
+ jest.spyOn(setUserProfile, 'useMutation').mockReturnValue([
+ mockSetUserProfileMutation,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ { isUninitialized: true } as any,
+ ]);
+ (useNavigate as jest.Mock).mockReturnValue(mockNavigate);
+ });
+
+ it('calls login create and set user profile mutations', async () => {
+ const mockStore = createTestStore({
+ signup: {
+ loginData: {
+ create: [
+ {
+ id: '123',
+ availablename: '',
+ provemail: '',
+ provfullname: '',
+ provusername: '',
+ },
+ ],
+ creationallowed: true,
+ expires: 0,
+ login: [],
+ provider: 'Google',
+ },
+ account: {
+ username: 'testuser',
+ display: 'Test User',
+ email: 'test@test.com',
+ policyids: [],
+ },
+ profile: {
+ userdata: {
+ avatarOption: 'gravatar',
+ department: '',
+ gravatarDefault: 'identicon',
+ organization: '',
+ },
+ surveydata: {
+ referralSources: {
+ question: '',
+ response: {},
+ },
+ },
+ },
+ },
+ });
+
+ let doSignup: (policyIds: string[]) => void;
+ act(() => {
+ const TestComponent = () => {
+ [doSignup] = useDoSignup();
+ return null;
+ };
+ renderWithProviders(, {
+ store: mockStore,
+ });
+ });
+
+ await act(async () => {
+ doSignup(['policy1']);
+ });
+
+ expect(mockLoginCreateMutation).toHaveBeenCalledWith({
+ id: '123',
+ user: 'testuser',
+ display: 'Test User',
+ email: 'test@test.com',
+ policyids: ['policy1'],
+ linkall: false,
+ });
+
+ expect(mockSetUserProfileMutation).toHaveBeenCalledWith([
+ {
+ profile: {
+ user: {
+ username: 'testuser',
+ realname: 'Test User',
+ },
+ profile: {
+ metadata: expect.any(Object),
+ preferences: {},
+ synced: {
+ gravatarHash: gravatarHash('test@test.com'),
+ },
+ userdata: {
+ avatarOption: 'gravatar',
+ department: '',
+ gravatarDefault: 'identicon',
+ organization: '',
+ },
+ surveydata: {
+ referralSources: {
+ question: '',
+ response: {},
+ },
+ },
+ },
+ },
+ },
+ 'someToken',
+ ]);
+ });
+});
diff --git a/src/features/signup/SignUp.tsx b/src/features/signup/SignUp.tsx
index fe62cf90..1fdc9e2e 100644
--- a/src/features/signup/SignUp.tsx
+++ b/src/features/signup/SignUp.tsx
@@ -39,7 +39,7 @@ export const SignUp: FC = () => {
};
useEffect(() => {
- document.querySelector('main')?.scrollTo(0, 0);
+ document.querySelector('main')?.scrollTo?.(0, 0);
}, [activeStep]);
return (
@@ -148,6 +148,6 @@ export const useDoSignup = () => {
return [doSignup, loading, complete, error] as const;
};
-const gravatarHash = (email: string) => {
+export const gravatarHash = (email: string) => {
return md5.create().update(email.trim().toLowerCase()).hex();
};
diff --git a/src/features/signup/SignupPolicies.test.tsx b/src/features/signup/SignupPolicies.test.tsx
new file mode 100644
index 00000000..c301e33d
--- /dev/null
+++ b/src/features/signup/SignupPolicies.test.tsx
@@ -0,0 +1,158 @@
+import { fireEvent, render, screen } from '@testing-library/react';
+import { Provider } from 'react-redux';
+import { MemoryRouter } from 'react-router-dom';
+import { configureStore } from '@reduxjs/toolkit';
+import { KBasePolicies } from './SignupPolicies';
+import { toast } from 'react-hot-toast';
+import * as SignUp from './SignUp';
+
+// Mock dependencies
+jest.mock('react-hot-toast');
+jest.mock('./AccountInformation', () => ({
+ useCheckLoginDataOk: jest.fn(),
+}));
+jest.mock('./SignUp', () => ({
+ useDoSignup: jest.fn(),
+}));
+jest.mock('../login/Policies', () => ({
+ kbasePolicies: {
+ termsOfService: {
+ id: 'termsOfService',
+ version: '1',
+ title: 'Terms of Service',
+ },
+ privacyPolicy: {
+ id: 'privacyPolicy',
+ version: '1',
+ title: 'Privacy Policy',
+ },
+ },
+ PolicyViewer: ({
+ policyId,
+ accepted,
+ setAccept,
+ }: {
+ policyId: string;
+ accepted: boolean;
+ setAccept: (checked: boolean) => void;
+ }) => (
+
+ setAccept(e.target.checked)}
+ data-testid={`checkbox-${policyId}`}
+ />
+
+ ),
+}));
+const mockScrollTo = jest.fn();
+Element.prototype.scrollTo = mockScrollTo;
+
+// Create a mock store
+const createMockStore = (initialState = {}) => {
+ return configureStore({
+ reducer: {
+ signup: (state = { account: {} }, action) => state,
+ },
+ preloadedState: {
+ signup: {
+ account: {
+ username: 'testuser',
+ email: 'test@test.com',
+ ...initialState,
+ },
+ },
+ },
+ });
+};
+
+describe('KBasePolicies', () => {
+ const mockNavigate = jest.fn();
+ const mockDoSignup = jest.fn();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ (SignUp.useDoSignup as jest.Mock).mockReturnValue([mockDoSignup, false]);
+ });
+
+ const renderComponent = (store = createMockStore()) => {
+ return render(
+
+
+
+
+
+ );
+ };
+
+ it('should render all policies', () => {
+ renderComponent();
+ expect(screen.getByTestId('policy-termsOfService')).toBeInTheDocument();
+ expect(screen.getByTestId('policy-privacyPolicy')).toBeInTheDocument();
+ });
+
+ it('should not call doSignup when policies are not accepted', () => {
+ renderComponent();
+ // Try to mock the button as enabled to ensure signup doesn't happen
+ const submitButton = screen.getByText('Create KBase account');
+ Object.defineProperty(submitButton, 'disabled', { value: false });
+ fireEvent.click(submitButton);
+
+ // Verify doSignup was not called
+ expect(mockDoSignup).not.toHaveBeenCalled();
+ });
+
+ it('should handle policy acceptance', () => {
+ renderComponent();
+ const tosCheckbox = screen.getByTestId('checkbox-termsOfService');
+ const privacyCheckbox = screen.getByTestId('checkbox-privacyPolicy');
+
+ // Initially, submit button should be disabled
+ const submitButton = screen.getByText('Create KBase account');
+ expect(submitButton).toBeDisabled();
+
+ // Accept policies
+ fireEvent.click(tosCheckbox);
+ fireEvent.click(privacyCheckbox);
+
+ // Submit button should be enabled
+ expect(submitButton).not.toBeDisabled();
+ });
+
+ it('should call doSignup when all policies are accepted and form is submitted', () => {
+ renderComponent();
+
+ // Accept all policies
+ fireEvent.click(screen.getByTestId('checkbox-termsOfService'));
+ fireEvent.click(screen.getByTestId('checkbox-privacyPolicy'));
+
+ // Submit form
+ fireEvent.click(screen.getByText('Create KBase account'));
+
+ // Verify doSignup was called with correct parameters
+ expect(mockDoSignup).toHaveBeenCalledWith([
+ 'termsOfService.1',
+ 'privacyPolicy.1',
+ ]);
+ });
+
+ it('should show warning toast if account information is missing', () => {
+ const store = createMockStore({ username: undefined });
+ renderComponent(store);
+ expect(toast).toHaveBeenCalledWith(
+ 'You must fill out your account information to sign up!'
+ );
+ });
+ it('should navigate when cancel button is clicked', () => {
+ renderComponent();
+ fireEvent.click(screen.getByText('Cancel sign up'));
+ expect(mockNavigate).toHaveBeenCalledWith('/signup/1');
+ });
+
+ it('should navigate when back button is clicked', () => {
+ renderComponent();
+ fireEvent.click(screen.getByText('Back to account information'));
+ expect(mockNavigate).toHaveBeenCalledWith('/signup/2');
+ });
+});
diff --git a/src/features/signup/SignupPolicies.tsx b/src/features/signup/SignupPolicies.tsx
index 0666bd0a..2d205725 100644
--- a/src/features/signup/SignupPolicies.tsx
+++ b/src/features/signup/SignupPolicies.tsx
@@ -6,7 +6,8 @@ import { toast } from 'react-hot-toast';
import { useNavigate } from 'react-router-dom';
import { Loader } from '../../common/components';
import { useAppDispatch, useAppSelector } from '../../common/hooks';
-import { kbasePolicies, PolicyViewer } from '../login/Policies';
+import { PolicyViewer } from '../login/EnforcePolicies';
+import { kbasePolicies } from '../login/Policies';
import { useCheckLoginDataOk } from './AccountInformation';
import { useDoSignup } from './SignUp';
import classes from './SignUp.module.scss';