Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FI-3598: Add title to auth inputs and consolidate input modes #591

Merged
merged 4 commits into from
Jan 17, 2025
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { FC } from 'react';
import { InputOption, TestInput } from '~/models/testSuiteModels';
import InputCombobox from './InputCombobox';
import InputCombobox from '~/components/InputsModal/InputCombobox';

export interface InputAccessProps {
input: TestInput;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,28 @@ import { Card, CardContent, InputLabel, List, ListItem } from '@mui/material';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { Auth, TestInput } from '~/models/testSuiteModels';
import { AuthType, getAccessFields } from '~/components/InputsModal/AuthSettings';
import {
AuthType,
getAccessFields,
getAuthFields,
} from '~/components/InputsModal/Auth/AuthSettings';
import AuthTypeSelector from '~/components/InputsModal/Auth/AuthTypeSelector';
import FieldLabel from '~/components/InputsModal/FieldLabel';
import InputFields from '~/components/InputsModal/InputFields';
import useStyles from './styles';
import AuthTypeSelector from './AuthTypeSelector';
import useStyles from '../styles';

export interface InputAccessProps {
export interface InputAuthProps {
mode: 'access' | 'auth';
input: TestInput;
index: number;
inputsMap: Map<string, unknown>;
setInputsMap: (map: Map<string, unknown>, edited?: boolean) => void;
}

const InputAccess: FC<InputAccessProps> = ({ input, index, inputsMap, setInputsMap }) => {
const InputAuth: FC<InputAuthProps> = ({ mode, input, index, inputsMap, setInputsMap }) => {
const { classes } = useStyles();
const [accessValues, setAccessValues] = React.useState<Map<string, unknown>>(new Map());
const [accessValuesPopulated, setAccessValuesPopulated] = React.useState<boolean>(false);
const [authValues, setAuthValues] = React.useState<Map<string, unknown>>(new Map());
const [authValuesPopulated, setAuthValuesPopulated] = React.useState<boolean>(false);

// Default auth type settings
const authComponent = input.options?.components?.find(
Expand All @@ -35,16 +40,21 @@ const InputAccess: FC<InputAccessProps> = ({ input, index, inputsMap, setInputsM
(authComponent?.default || firstListOption || 'public') as string,
);

const [accessFields, setAccessFields] = React.useState<TestInput[]>(
getAccessFields(authType as AuthType, accessValues, input.options?.components || []),
);
// Set fields depending on mode
let fields: TestInput[] = [];
if (mode === 'access') {
fields = getAccessFields(authType as AuthType, authValues, input.options?.components || []);
} else if (mode === 'auth') {
fields = getAuthFields(authType as AuthType, authValues, input.options?.components || []);
}
const [authFields, setAuthFields] = React.useState<TestInput[]>(fields);

useEffect(() => {
// Set defaults on radio buttons
// This is necessary because radio buttons with no preset defaults will still cause
// missing input errors
setAccessFields(
accessFields.map((field) => {
setAuthFields(
authFields.map((field) => {
if (
field.type === 'radio' &&
!field.default &&
Expand All @@ -59,38 +69,47 @@ const InputAccess: FC<InputAccessProps> = ({ input, index, inputsMap, setInputsM

const combinedStartingValues = getStartingValues();

// Populate accessValues on mount
accessValues.set('auth_type', authType);
accessFields.forEach((field: TestInput) => {
accessValues.set(field.name, combinedStartingValues[field.name as keyof Auth] || '');
// Populate authValues on mount
authValues.set('auth_type', authType);
authFields.forEach((field: TestInput) => {
authValues.set(field.name, combinedStartingValues[field.name as keyof Auth] || '');
});
setAccessValuesPopulated(true);
setAuthValuesPopulated(true);

// Trigger change on mount for default values
const accessValuesCopy = new Map(accessValues);
setAccessValues(accessValuesCopy);
const authValuesCopy = new Map(authValues);
setAuthValues(authValuesCopy);
}, []);

useEffect(() => {
// Recalculate hidden fields
setAccessFields(
getAccessFields(authType as AuthType, accessValues, input.options?.components || []),
);
if (mode === 'access') {
setAuthFields(
getAccessFields(authType as AuthType, authValues, input.options?.components || []),
);
} else if (mode === 'auth') {
setAuthFields(
getAuthFields(authType as AuthType, authValues, input.options?.components || []),
);
}

// Update inputsMap while maintaining hidden values
if (accessValuesPopulated) {
const combinedStartingValues = getStartingValues();
const accessValuesObject = Object.fromEntries(accessValues) as Auth;
const combinedValues = { ...combinedStartingValues, ...accessValuesObject };
const stringifiedAccessValues = JSON.stringify(combinedValues);
inputsMap.set(input.name, stringifiedAccessValues);
if (authValuesPopulated) {
let stringifiedValues = JSON.stringify(Object.fromEntries(authValues));
if (mode === 'access') {
const combinedStartingValues = getStartingValues();
const accessValuesObject = Object.fromEntries(authValues) as Auth;
const combinedValues = { ...combinedStartingValues, ...accessValuesObject };
stringifiedValues = JSON.stringify(combinedValues);
}
inputsMap.set(input.name, stringifiedValues);
setInputsMap(new Map(inputsMap));
}
}, [accessValues]);
}, [authValues]);

const getStartingValues = () => {
// Pre-populate values from AuthFields, input, and inputsMap in order of precedence
const fieldDefaultValues = accessFields.reduce(
const fieldDefaultValues = authFields.reduce(
(acc, field) => ({ ...acc, [field.name]: field.default }),
{},
) as Auth;
Expand All @@ -101,6 +120,7 @@ const InputAccess: FC<InputAccessProps> = ({ input, index, inputsMap, setInputsM
const inputsMapValues = inputsMap.get(input.name)
? (JSON.parse(inputsMap.get(input.name) as string) as Auth)
: {};

return {
...fieldDefaultValues,
...inputDefaultValues,
Expand All @@ -111,7 +131,7 @@ const InputAccess: FC<InputAccessProps> = ({ input, index, inputsMap, setInputsM

const updateAuthType = (map: Map<string, unknown>) => {
setAuthType(map.get('auth_type') as string);
setAccessValues(map);
setAuthValues(map);
};

return (
Expand All @@ -134,20 +154,16 @@ const InputAccess: FC<InputAccessProps> = ({ input, index, inputsMap, setInputsM
<AuthTypeSelector
input={input}
index={index}
inputsMap={accessValues}
inputsMap={authValues}
setInputsMap={updateAuthType}
key={`input-${index}`}
/>
</List>
<InputFields
inputs={accessFields}
inputsMap={accessValues}
setInputsMap={setAccessValues}
/>
<InputFields inputs={authFields} inputsMap={authValues} setInputsMap={setAuthValues} />
</CardContent>
</Card>
</ListItem>
);
};

export default InputAccess;
export default InputAuth;
136 changes: 0 additions & 136 deletions client/src/components/InputsModal/InputAuth.tsx

This file was deleted.

13 changes: 7 additions & 6 deletions client/src/components/InputsModal/InputFields.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import React, { FC } from 'react';
import { List } from '@mui/material';
import { TestInput } from '~/models/testSuiteModels';
import InputOAuthCredentials from '~/components/InputsModal/InputOAuthCredentials';
import InputAuth from '~/components/InputsModal/Auth/InputAuth';
import InputCheckboxGroup from '~/components/InputsModal/InputCheckboxGroup';
import InputCombobox from '~/components/InputsModal/InputCombobox';
import InputOAuthCredentials from '~/components/InputsModal/InputOAuthCredentials';
import InputRadioGroup from '~/components/InputsModal/InputRadioGroup';
import InputTextField from '~/components/InputsModal/InputTextField';
import InputAuth from '~/components/InputsModal/InputAuth';
import InputSingleCheckbox from '~/components/InputsModal/InputSingleCheckbox';
import InputCombobox from '~/components/InputsModal/InputCombobox';
import InputAccess from '~/components/InputsModal/InputAccess';
import InputTextField from '~/components/InputsModal/InputTextField';

export interface InputFieldsProps {
inputs: TestInput[];
Expand All @@ -26,6 +25,7 @@ const InputFields: FC<InputFieldsProps> = ({ inputs, inputsMap, setInputsMap })
if (input.options?.mode === 'auth') {
return (
<InputAuth
mode="auth"
input={input}
index={index}
inputsMap={inputsMap}
Expand All @@ -35,7 +35,8 @@ const InputFields: FC<InputFieldsProps> = ({ inputs, inputsMap, setInputsMap })
);
}
return (
<InputAccess
<InputAuth
mode="access"
input={input}
index={index}
inputsMap={inputsMap}
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/InputsModal/InputHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import YAML from 'js-yaml';
import { enqueueSnackbar } from 'notistack';
import { Auth, OAuthCredentials, TestInput } from '~/models/testSuiteModels';
import { AuthType, getAuthFields, getAccessFields } from './AuthSettings';
import { AuthType, getAuthFields, getAccessFields } from './Auth/AuthSettings';

export const getMissingRequiredInput = (inputs: TestInput[], inputsMap: Map<string, unknown>) => {
return inputs.some((input: TestInput) => {
Expand Down
13 changes: 7 additions & 6 deletions client/src/components/InputsModal/__tests__/Inputs.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { SnackbarProvider } from 'notistack';
import { TestInput } from '~/models/testSuiteModels';
import ThemeProvider from '~/components/ThemeProvider';
import InputAuth from '~/components/InputsModal/Auth/InputAuth';
import InputCheckboxGroup from '~/components/InputsModal/InputCheckboxGroup';
import InputOAuthCredentials from '~/components/InputsModal/InputOAuthCredentials';
import InputRadioGroup from '~/components/InputsModal/InputRadioGroup';
import InputTextField from '~/components/InputsModal/InputTextField';
import InputOAuthCredentials from '~/components/InputsModal/InputOAuthCredentials';
import InputAuth from '~/components/InputsModal/InputAuth';
import InputAccess from '~/components/InputsModal/InputAccess';
import { describe, expect, it } from 'vitest';
import ThemeProvider from '~/components/ThemeProvider';

describe('Input Components', () => {
it('renders InputCheckboxGroup', () => {
Expand Down Expand Up @@ -176,6 +175,7 @@ describe('Input Components', () => {
<ThemeProvider>
<SnackbarProvider>
<InputAuth
mode="auth"
input={authInput}
index={0}
inputsMap={new Map<string, string>()}
Expand Down Expand Up @@ -208,7 +208,8 @@ describe('Input Components', () => {
render(
<ThemeProvider>
<SnackbarProvider>
<InputAccess
<InputAuth
mode="access"
input={accessInput}
index={0}
inputsMap={new Map<string, string>()}
Expand Down
Loading