How can I test a switch case that depends on a parameter? - javascript

I have this:
const renderComponents = () => {
switch (selectedService) {
case 'otherservices':
return <SoftLayerCancellationRequests />;
case 'dedicatedhosts':
return <GetDedicatedHostsCancellations />;
case 'virtualguestsservers':
return <SoftLayerGetVirtualGuests />;
case 'baremetalservers':
return <GetBareMetalServersCancellations />;
default:
return null;
}
};
Which at the end is called on the return statement of the component:
return (
<>
<Header pageTitle={t('cancellations.header')} />
{accountId ? (
<>
<TableToolbarComp />
{renderComponents()}
</>
) : (
<UpgradeMessage />
)}
</>
);
And the selectedService parameter is coming from a store:
export default compose(
connect(store => ({
accountId: store.global.softlayerAccountId,
selectedService: store.cancellations.selectedService,
})),
translate(),
hot(module),
)(Cancellations);
What can I do to test that switch case?

The function under renderComponents should accept selectedService as a parameter:
const renderComponents = (selectedService) => {
switch (selectedService) {
// ...
}
};
By not relying on a closure, the function becomes pure and is way easier to unit test :
it('renders a SoftLayerCancellationRequests when passed "otherservices" as parameter', () => {
const wrapper = shallow(renderComponents('otherservices'));
expect(wrapper.find(SoftLayerCancellationRequests)).toHaveLength(1);
})
Yet, I see little value in such tests. This is because the function basically acts as a simple map :
const serviceToComponent : {
otherservices: SoftLayerCancellationRequests,
dedicatedhosts: GetDedicatedHostsCancellations,
virtualguestsservers: SoftLayerGetVirtualGuests,
baremetalservers: GetBareMetalServersCancellations
}
which seems a bit dull to test.
A more meaningful test would be to test the behaviors of the component that uses such a mapping.

Related

React: Cannot assign to read only property '_status' of object '#<Object>'

I've been struggling over this error for a while now. It happens when I try to open react-bootstrap Modal with dynamically passed lazy component referrence and props to render it inside. It worked with classic import.
First row points to some react's internal lazy handler:
This is how modals are handled inside my ModalProvider:
const ModalList = React.memo(({ modalList, closeModalByIndex, confirmModalExitByIndex }) =>
modalList.map((modalDef, index) => {
const closeModal = () => closeModalByIndex(index);
const onConfirmExitChange = (confirmExit) => confirmModalExitByIndex(index, confirmExit);
const props = { ...modalDef, key: index, closeModal, onConfirmExitChange };
switch (modalDef.type) {
case TYPE_LIST:
return (
<React.Suspense fallback={fallback}>
<ListModal {...props} />
</React.Suspense>
);
case TYPE_FORM:
return (
<React.Suspense fallback={fallback}>
<FormModal {...props} />
</React.Suspense>
);
case TYPE_LIST_MULTI:
return (
<React.Suspense fallback={fallback}>
<ListMultiModal {...props} />
</React.Suspense>
);
default:
return null;
}
})
);
And this is how it is passed:
const openListModal = (Component, componentProps) => openModal(Component, componentProps, TYPE_LIST);
Anyone with deeper understanding what could possibly cause this?
Found out by trial by error. It was caused by immer's produce function which builds read-only deep copy of object.
setModalList(
produce(modalList, (modalList) => {
modalList.push({ Component, componentProps, type, show: true });
})
);

Autosuggest field and variants

There is a problem, when i type anything in search field, suggest variants looks weird ( on the picture) because endings is cut. I want to cut down not a single word, but a sentence( first part of suggest ).
export const SkillAutoSuggestOption = (
props: React.HTMLAttributes<HTMLLIElement>,
option: any,
state: AutocompleteRenderOptionState,
getName: (option: any) => string
): React.ReactNode => {
const matches = match(getName(option), state.inputValue, { insideWords: true })
const parts = parse(getName(option), matches)
const path = () => {
if ('skillPathForAutoSuggest' in option) {
return option.skillPathForAutoSuggest
} else return getNodePath(option)
}
return (
<ListItem {...props} key={option.skill_id}>
{option.isCategory && <SCircleIcon />}
{parts.map((part, index) => (
<SLabel key={index} style={part.highlight ? { color: tannBlue } : {}}>
{part.text}
</SLabel>
))}
<SSkillPath>{path()}</SSkillPath>
</ListItem>
)
}
Can you add the getNodePath, parse and match functions source code/the source library if you use any? The problem is not related to rendering the list part but is related to how these functions work.

Elegant ways of handling conditional rendering of multiple components

Say I have a wizard-like view with an arbitrary number of steps:
const StepsComponent = () => {
const [stage, setStage] = useState(1);
const stageProps = {stage, setStage};
const stageMachine = () => {
switch (stage) {
case 1:
return <One {...stageProps} />;
case 2:
return <Two {...stageProps} />;
case 3:
return <Three {...stageProps} />;
default:
return <One {...stageProps} />;
}
};
return (
<>
{stageMachine()}
</>
);
}
Are there more elegant ways of handling such cases, other than switch statements or ternary expressions?
If I would have wizards with 10+ steps, then it'd be a real mess to manage it.
Probably I could do something like this but this seems hacky, doesn't it?
const stageMachine = Object.freeze({
1: <One {...stageProps} />,
2: <Two {...stageProps} />,
3: <Three {...stageProps} />
});
Also I don't like the idea of invoking stageMachine function in return, it is considered a bad practice?
You can try this
const StepsComponent = () => {
const [stage, setStage] = useState(1);
return (
<>
{stage === 1 && <One />}
{stage === 2 && <Thow />}
{stage === 3 && <Three />}
</>
);
}

What Javascript feature is being used here?

I'm studying React and Apollo JS and came across this syntax.
What JS feature is being used where Launches is declared below?
const Launches: React.FC<LaunchesProps> = () => {
const {
data,
loading,
error
} = useQuery<
GetLaunchListTypes.GetLaunchList,
GetLaunchListTypes.GetLaunchListVariables
>(GET_LAUNCHES);
if (loading) return <Loading />;
if (error) return <p>ERROR</p>;
if (!data) return <p>Not found</p>;
return (
<Fragment>
<Header />
{data.launches &&
data.launches.launches &&
data.launches.launches.map((launch: any) => (
<LaunchTile key={launch.id} launch={launch} />
))}
</Fragment>
);
}
That's Typescript not Javascript, and it's a static type annotation.
It types that function as a React Function Component with the type LaunchesProps as the type of the props it receives.

How to pass extra props down to children of arry.map

I have been trying to pass extra props own to the children being created with the .map function but I have not been able to succeed in passing them succesfully.
This is my code:
export const CommentsListShanghai = (props) => {
const newTimestamp = props.timestamp;
console.log(newTimestamp);
const comments = props.comments;
if (comments.length > 0 ) {
return (
<ButtonToolbar className="comment-list">
{comments.map((com) => {
return (
com.adminSpark ?
<CommentsModal
className="comments-modal"
data-comments-modal={props.newTimestamp}
key={ com._id }
comment={ com }
city={com.city}
person={com.person}
location={com.location}
title={com.title}
content={com.content}
fileLink={com.fileLink}
timestamp={com.timestamp}
createdBy={com.createdBy}
/> :
<CommentsModal
key={ com._id }
comment={ com }
city={com.city}
person={com.person}
location={com.location}
title={com.title}
content={com.content}
fileLink={com.fileLink}
timestamp={com.timestamp}
createdBy={com.createdBy} />
)
})}
</ButtonToolbar>
);
} else {
return (
<Alert bsStyle="warning">No sparks yet. Please add some!</Alert>
);
}
};
CommentsListShanghai.propTypes = {
comments: React.PropTypes.array,
};
I am able to pass all the props of the comments const that I created, the problem is that besides these props I also need to pass an extra prop which is available in the CommentsListShanghai. How am I able to pass an extra props to this array?
I am able to console.log(newTimestamp) without a problem but don't understand how I can pass it down to the .map function.
Instead of
data-comments-modal={props.newTimestamp}
just use
data-comments-modal={props.timestamp}
The props here is still referring to the context of CommentsListShanghai.

Categories