fix install types and test ids

This commit is contained in:
Ildar Kamalov
2025-01-24 15:15:07 +03:00
parent 681cdb023e
commit 9d5042b1f0
11 changed files with 97 additions and 86 deletions

View File

@@ -28,7 +28,7 @@ import {
THEMES, THEMES,
} from './constants'; } from './constants';
import { LOCAL_STORAGE_KEYS, LocalStorageHelper } from './localStorageHelper'; import { LOCAL_STORAGE_KEYS, LocalStorageHelper } from './localStorageHelper';
import { DhcpInterface } from '../initialState'; import { DhcpInterface, InstallInterface } from '../initialState';
/** /**
* @param time {string} The time to format * @param time {string} The time to format
@@ -217,9 +217,9 @@ export const getInterfaceIp = (option: any) => {
return interfaceIP; return interfaceIP;
}; };
export const getIpList = (interfaces: DhcpInterface[]) => export const getIpList = (interfaces: InstallInterface[]) =>
Object.values(interfaces) Object.values(interfaces)
.reduce((acc: string[], curr: DhcpInterface) => acc.concat(curr.ip_addresses), [] as string[]) .reduce((acc: string[], curr: InstallInterface) => acc.concat(curr.ip_addresses), [] as string[])
.sort(); .sort();
/** /**

View File

@@ -9,6 +9,14 @@ import {
import { DEFAULT_BLOCKING_IPV4, DEFAULT_BLOCKING_IPV6 } from './reducers/dnsConfig'; import { DEFAULT_BLOCKING_IPV4, DEFAULT_BLOCKING_IPV6 } from './reducers/dnsConfig';
import { Filter } from './helpers/helpers'; import { Filter } from './helpers/helpers';
export type InstallInterface = {
flags: string;
hardware_address: string;
ip_addresses: string[];
mtu: number;
name: string;
};
export type InstallData = { export type InstallData = {
step: number; step: number;
processingDefault: boolean; processingDefault: boolean;
@@ -31,13 +39,7 @@ export type InstallData = {
ip: string; ip: string;
error: string; error: string;
}; };
interfaces: { interfaces: InstallInterface[];
flags: string;
hardware_address: string;
ip_addresses: string[];
mtu: number;
name: string;
}[];
dnsVersion: string; dnsVersion: string;
}; };

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { getIpList, getDnsAddress, getWebAddress } from '../../helpers/helpers'; import { getIpList, getDnsAddress, getWebAddress } from '../../helpers/helpers';
import { ALL_INTERFACES_IP } from '../../helpers/constants'; import { ALL_INTERFACES_IP } from '../../helpers/constants';
import { DhcpInterface } from '../../initialState'; import { InstallInterface } from '../../initialState';
interface renderItemProps { interface renderItemProps {
ip: string; ip: string;
@@ -28,7 +28,7 @@ const renderItem = ({ ip, port, isDns }: renderItemProps) => {
}; };
interface AddressListProps { interface AddressListProps {
interfaces: DhcpInterface[]; interfaces: InstallInterface[];
address: string; address: string;
port: number; port: number;
isDns?: boolean; isDns?: boolean;

View File

@@ -60,6 +60,7 @@ export const Auth = ({ onAuthSubmit }: Props) => {
<Input <Input
{...field} {...field}
type="text" type="text"
data-testid="install_username"
label={t('install_auth_username')} label={t('install_auth_username')}
placeholder={t('install_auth_username_enter')} placeholder={t('install_auth_username_enter')}
error={fieldState.error?.message} error={fieldState.error?.message}
@@ -83,6 +84,7 @@ export const Auth = ({ onAuthSubmit }: Props) => {
<Input <Input
{...field} {...field}
type="password" type="password"
data-testid="install_password"
label={t('install_auth_password')} label={t('install_auth_password')}
placeholder={t('install_auth_password_enter')} placeholder={t('install_auth_password_enter')}
error={fieldState.error?.message} error={fieldState.error?.message}
@@ -106,6 +108,7 @@ export const Auth = ({ onAuthSubmit }: Props) => {
<Input <Input
{...field} {...field}
type="password" type="password"
data-testid="install_confirm_password"
label={t('install_auth_confirm')} label={t('install_auth_confirm')}
placeholder={t('install_auth_confirm')} placeholder={t('install_auth_confirm')}
error={fieldState.error?.message} error={fieldState.error?.message}

View File

@@ -32,6 +32,7 @@ class Controls extends Component<ControlsProps> {
case 3: case 3:
return ( return (
<button <button
data-testid="install_back"
type="button" type="button"
className="btn btn-secondary btn-lg setup__button" className="btn btn-secondary btn-lg setup__button"
onClick={this.props.prevStep}> onClick={this.props.prevStep}>
@@ -44,24 +45,16 @@ class Controls extends Component<ControlsProps> {
} }
renderNextButton(step: any) { renderNextButton(step: any) {
const { const { nextStep, invalid, pristine, install, ip, port } = this.props;
nextStep,
invalid,
pristine,
install,
ip,
port,
} = this.props;
switch (step) { switch (step) {
case 1: case 1:
return ( return (
<button type="button" className="btn btn-success btn-lg setup__button" onClick={nextStep}> <button
data-testid="install_get_started"
type="button"
className="btn btn-success btn-lg setup__button"
onClick={nextStep}>
<Trans>get_started</Trans> <Trans>get_started</Trans>
</button> </button>
); );
@@ -69,6 +62,7 @@ class Controls extends Component<ControlsProps> {
case 3: case 3:
return ( return (
<button <button
data-testid="install_next"
type="submit" type="submit"
className="btn btn-success btn-lg setup__button" className="btn btn-success btn-lg setup__button"
disabled={invalid || pristine || install.processingSubmit}> disabled={invalid || pristine || install.processingSubmit}>
@@ -77,13 +71,18 @@ class Controls extends Component<ControlsProps> {
); );
case 4: case 4:
return ( return (
<button type="button" className="btn btn-success btn-lg setup__button" onClick={nextStep}> <button
data-testid="install_next"
type="button"
className="btn btn-success btn-lg setup__button"
onClick={nextStep}>
<Trans>next</Trans> <Trans>next</Trans>
</button> </button>
); );
case 5: case 5:
return ( return (
<button <button
data-testid="install_open_dashboard"
type="button" type="button"
className="btn btn-success btn-lg setup__button" className="btn btn-success btn-lg setup__button"
onClick={() => this.props.openDashboard(ip, port)}> onClick={() => this.props.openDashboard(ip, port)}>

View File

@@ -1,22 +1,21 @@
import React from 'react'; import React from 'react';
import { Trans, withTranslation } from 'react-i18next'; import { Trans } from 'react-i18next';
import flow from 'lodash/flow';
import Guide from '../../components/ui/Guide'; import Guide from '../../components/ui/Guide';
import Controls from './Controls'; import Controls from './Controls';
import AddressList from './AddressList'; import AddressList from './AddressList';
import { DhcpInterface } from '../../initialState'; import { InstallInterface } from '../../initialState';
import { DnsConfig } from './Settings';
interface DevicesProps { type Props = {
interfaces: DhcpInterface[]; interfaces: InstallInterface[];
dnsIp: string; dnsConfig: DnsConfig;
dnsPort: number; };
}
const Devices = ({ interfaces, dnsIp, dnsPort }: DevicesProps) => ( export const Devices = ({ interfaces, dnsConfig }: Props) => (
<div className="setup__step"> <div className="setup__step">
<div className="setup__group"> <div className="setup__group">
<div className="setup__subtitle"> <div className="setup__subtitle">
@@ -31,7 +30,7 @@ const Devices = ({ interfaces, dnsIp, dnsPort }: DevicesProps) => (
</div> </div>
<div className="mt-1"> <div className="mt-1">
<AddressList interfaces={interfaces} address={dnsIp} port={dnsPort} isDns /> <AddressList interfaces={interfaces} address={dnsConfig.ip} port={dnsConfig.port} isDns />
</div> </div>
</div> </div>
@@ -41,5 +40,3 @@ const Devices = ({ interfaces, dnsIp, dnsPort }: DevicesProps) => (
<Controls /> <Controls />
</div> </div>
); );
export default flow([withTranslation()])(Devices);

View File

@@ -1,21 +1,19 @@
import React from 'react'; import React from 'react';
import { Trans, withTranslation } from 'react-i18next'; import { Trans } from 'react-i18next';
import { INSTALL_TOTAL_STEPS } from '../../helpers/constants'; import { INSTALL_TOTAL_STEPS } from '../../helpers/constants';
const getProgressPercent = (step: any) => (step / INSTALL_TOTAL_STEPS) * 100; const getProgressPercent = (step: number) => (step / INSTALL_TOTAL_STEPS) * 100;
type Props = { type Props = {
step: number; step: number;
}; };
const Progress = (props: Props) => ( export const Progress = ({ step }: Props) => (
<div className="setup__progress"> <div className="setup__progress">
<Trans>install_step</Trans> {props.step}/{INSTALL_TOTAL_STEPS} <Trans>install_step</Trans> {step}/{INSTALL_TOTAL_STEPS}
<div className="setup__progress-wrap"> <div className="setup__progress-wrap">
<div className="setup__progress-inner" style={{ width: `${getProgressPercent(props.step)}%` }} /> <div className="setup__progress-inner" style={{ width: `${getProgressPercent(step)}%` }} />
</div> </div>
</div> </div>
); );
export default withTranslation()(Progress);

View File

@@ -19,7 +19,7 @@ import {
} from '../../helpers/constants'; } from '../../helpers/constants';
import { validateRequiredValue } from '../../helpers/validators'; import { validateRequiredValue } from '../../helpers/validators';
import { DhcpInterface } from '../../initialState'; import { InstallInterface } from '../../initialState';
import { Input } from '../../components/ui/Controls/Input'; import { Input } from '../../components/ui/Controls/Input';
import { Select } from '../../components/ui/Controls/Select'; import { Select } from '../../components/ui/Controls/Select';
import { toNumber } from '../../helpers/form'; import { toNumber } from '../../helpers/form';
@@ -31,12 +31,27 @@ const validateInstallPort = (value: number) => {
return undefined; return undefined;
}; };
export type WebConfig = {
ip: string;
port: number;
};
export type DnsConfig = {
ip: string;
port: number;
};
export type SettingsFormValues = {
web: WebConfig;
dns: DnsConfig;
};
type StaticIpType = { type StaticIpType = {
ip: string; ip: string;
static: string; static: string;
}; };
type ConfigType = { export type ConfigType = {
web: { web: {
ip: string; ip: string;
port?: number; port?: number;
@@ -53,17 +68,17 @@ type ConfigType = {
}; };
type Props = { type Props = {
handleSubmit: (data: any) => void; handleSubmit: (data: SettingsFormValues) => void;
handleChange?: (...args: unknown[]) => unknown; handleChange?: (data: SettingsFormValues) => unknown;
handleFix: (web: any, dns: any, set_static_ip: boolean) => void; handleFix: (web: WebConfig, dns: DnsConfig, set_static_ip: boolean) => void;
validateForm: (data: any) => void; validateForm: (data: SettingsFormValues) => void;
config: ConfigType; config: ConfigType;
interfaces: DhcpInterface[]; interfaces: InstallInterface[];
initialValues?: object; initialValues?: object;
}; };
const renderInterfaces = (interfaces: DhcpInterface[]) => const renderInterfaces = (interfaces: InstallInterface[]) =>
Object.values(interfaces).map((option: DhcpInterface) => { Object.values(interfaces).map((option: InstallInterface) => {
const { name, ip_addresses, flags } = option; const { name, ip_addresses, flags } = option;
if (option && ip_addresses?.length > 0) { if (option && ip_addresses?.length > 0) {
@@ -99,7 +114,7 @@ export const Settings = ({ handleSubmit, handleFix, validateForm, config, interf
watch, watch,
handleSubmit: reactHookFormSubmit, handleSubmit: reactHookFormSubmit,
formState: { isValid }, formState: { isValid },
} = useForm({ } = useForm<SettingsFormValues>({
defaultValues, defaultValues,
mode: 'onBlur', mode: 'onBlur',
}); });
@@ -235,7 +250,7 @@ export const Settings = ({ handleSubmit, handleFix, validateForm, config, interf
name="web.ip" name="web.ip"
control={control} control={control}
render={({ field }) => ( render={({ field }) => (
<Select {...field}> <Select {...field} data-testid="install_web_ip">
<option value={ALL_INTERFACES_IP}> <option value={ALL_INTERFACES_IP}>
{t('install_settings_all_interfaces')} {t('install_settings_all_interfaces')}
</option> </option>
@@ -264,6 +279,7 @@ export const Settings = ({ handleSubmit, handleFix, validateForm, config, interf
<Input <Input
{...field} {...field}
type="number" type="number"
data-testid="install_web_port"
placeholder={STANDARD_WEB_PORT.toString()} placeholder={STANDARD_WEB_PORT.toString()}
error={fieldState.error?.message} error={fieldState.error?.message}
onChange={(e) => { onChange={(e) => {
@@ -283,6 +299,7 @@ export const Settings = ({ handleSubmit, handleFix, validateForm, config, interf
{isWebFixAvailable && ( {isWebFixAvailable && (
<button <button
type="button" type="button"
data-testid="install_web_fix"
className="btn btn-secondary btn-sm ml-2" className="btn btn-secondary btn-sm ml-2"
onClick={() => handleAutofix('web')}> onClick={() => handleAutofix('web')}>
<Trans>fix</Trans> <Trans>fix</Trans>
@@ -323,7 +340,7 @@ export const Settings = ({ handleSubmit, handleFix, validateForm, config, interf
name="dns.ip" name="dns.ip"
control={control} control={control}
render={({ field }) => ( render={({ field }) => (
<Select {...field}> <Select {...field} data-testid="install_dns_ip">
<option value={ALL_INTERFACES_IP}> <option value={ALL_INTERFACES_IP}>
{t('install_settings_all_interfaces')} {t('install_settings_all_interfaces')}
</option> </option>
@@ -353,6 +370,7 @@ export const Settings = ({ handleSubmit, handleFix, validateForm, config, interf
<Input <Input
{...field} {...field}
type="number" type="number"
data-testid="install_dns_port"
error={fieldState.error?.message} error={fieldState.error?.message}
placeholder={STANDARD_WEB_PORT.toString()} placeholder={STANDARD_WEB_PORT.toString()}
onChange={(e) => { onChange={(e) => {
@@ -373,6 +391,7 @@ export const Settings = ({ handleSubmit, handleFix, validateForm, config, interf
{isDnsFixAvailable && ( {isDnsFixAvailable && (
<button <button
type="button" type="button"
data-testid="install_dns_fix"
className="btn btn-secondary btn-sm ml-2" className="btn btn-secondary btn-sm ml-2"
onClick={() => handleAutofix('dns')}> onClick={() => handleAutofix('dns')}>
<Trans>fix</Trans> <Trans>fix</Trans>

View File

@@ -1,20 +1,16 @@
import React from 'react'; import React from 'react';
import { Trans, withTranslation } from 'react-i18next'; import { Trans } from 'react-i18next';
import flow from 'lodash/flow';
import Controls from './Controls'; import Controls from './Controls';
import { WebConfig } from './Settings';
interface SubmitProps { type Props = {
webIp: string; webConfig: WebConfig;
webPort: number; openDashboard: (ip: string, port: number) => void;
handleSubmit: (...args: unknown[]) => string; };
pristine: boolean;
submitting: boolean;
openDashboard: (...args: unknown[]) => unknown;
}
const Submit = (props: SubmitProps) => ( export const Submit = ({ openDashboard, webConfig }: Props) => (
<div className="setup__step"> <div className="setup__step">
<div className="setup__group"> <div className="setup__group">
<h1 className="setup__title"> <h1 className="setup__title">
@@ -26,8 +22,6 @@ const Submit = (props: SubmitProps) => (
</p> </p>
</div> </div>
<Controls openDashboard={props.openDashboard} ip={props.webIp} port={props.webPort} /> <Controls openDashboard={openDashboard} ip={webConfig.ip} port={webConfig.port} />
</div> </div>
); );
export default flow([withTranslation()])(Submit);

View File

@@ -9,10 +9,10 @@ import { INSTALL_TOTAL_STEPS, ALL_INTERFACES_IP, DEBOUNCE_TIMEOUT } from '../../
import Loading from '../../components/ui/Loading'; import Loading from '../../components/ui/Loading';
import Greeting from './Greeting'; import Greeting from './Greeting';
import { Settings } from './Settings'; import { ConfigType, DnsConfig, Settings, WebConfig } from './Settings';
import Devices from './Devices'; import { Devices } from './Devices';
import Submit from './Submit'; import { Submit } from './Submit';
import Progress from './Progress'; import { Progress } from './Progress';
import { Auth } from './Auth'; import { Auth } from './Auth';
import Toasts from '../../components/Toasts'; import Toasts from '../../components/Toasts';
import Footer from '../../components/ui/Footer'; import Footer from '../../components/ui/Footer';
@@ -21,11 +21,12 @@ import { Logo } from '../../components/ui/svg/logo';
import './Setup.css'; import './Setup.css';
import '../../components/ui/Tabler.css'; import '../../components/ui/Tabler.css';
import { InstallInterface, InstallState } from '../../initialState';
const Setup = () => { export const Setup = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const install = useSelector((state: any) => state.install); const install = useSelector((state: InstallState) => state.install);
const { processingDefault, step, web, dns, staticIp, interfaces } = install; const { processingDefault, step, web, dns, staticIp, interfaces } = install;
useEffect(() => { useEffect(() => {
@@ -55,11 +56,11 @@ const Setup = () => {
} }
}, DEBOUNCE_TIMEOUT); }, DEBOUNCE_TIMEOUT);
const handleFix = (web: any, dns: any, set_static_ip: any) => { const handleFix = (web: WebConfig, dns: DnsConfig, set_static_ip: boolean) => {
dispatch(actionCreators.checkConfig({ web, dns, set_static_ip })); dispatch(actionCreators.checkConfig({ web, dns, set_static_ip }));
}; };
const openDashboard = (ip: any, port: any) => { const openDashboard = (ip: string, port: number) => {
let address = getWebAddress(ip, port); let address = getWebAddress(ip, port);
if (ip === ALL_INTERFACES_IP) { if (ip === ALL_INTERFACES_IP) {
address = getWebAddress(window.location.hostname, port); address = getWebAddress(window.location.hostname, port);
@@ -73,7 +74,7 @@ const Setup = () => {
} }
}; };
const renderPage = (step: any, config: any, interfaces: any) => { const renderPage = (step: number, config: ConfigType, interfaces: InstallInterface[]) => {
switch (step) { switch (step) {
case 1: case 1:
return <Greeting />; return <Greeting />;
@@ -91,9 +92,9 @@ const Setup = () => {
case 3: case 3:
return <Auth onAuthSubmit={handleFormSubmit} />; return <Auth onAuthSubmit={handleFormSubmit} />;
case 4: case 4:
return <Devices interfaces={interfaces} dnsIp={dns.ip} dnsPort={dns.port} />; return <Devices interfaces={interfaces} dnsConfig={dns} />;
case 5: case 5:
return <Submit openDashboard={openDashboard} webIp={web.ip} webPort={web.port} />; return <Submit openDashboard={openDashboard} webConfig={web} />;
default: default:
return false; return false;
} }
@@ -121,5 +122,3 @@ const Setup = () => {
</> </>
); );
}; };
export default Setup;

View File

@@ -8,7 +8,7 @@ import configureStore from '../configureStore';
import reducers from '../reducers/install'; import reducers from '../reducers/install';
import '../i18n'; import '../i18n';
import Setup from './Setup'; import { Setup } from './Setup';
import { InstallState } from '../initialState'; import { InstallState } from '../initialState';
const store = configureStore<InstallState>(reducers, {}); const store = configureStore<InstallState>(reducers, {});