GithubHelp home page GithubHelp logo

Comments (4)

teodosii avatar teodosii commented on May 26, 2024 1

^ indeed that's the problem, as long as the root component (aka the Container) doesn't get mounted, then the store doesn't get initialized. In a next version I think I could change the implementation so that it uses portals and gets rid of that container.

Closing the issue for now as it's not a bug on the component's end.

from react-notifications-component.

teodosii avatar teodosii commented on May 26, 2024

I believe that's happening because the Store is not initialized. In order to be initialized it should be called on register method with the needed parameters. Try to log Store to see if it's undefined or if the fields within class are undefined - I suspect the latter to be your issue.

When ran in browser, it calls register on this line - maybe that part is not called in your tests? Hard to say, v1 had plenty of tests but the breaking changes required to discard the old tests and I havent' written any from v2 onwards.

So the solution is to make sure the Container's componentDidMount runs successfully so that you have your store registered - post a more detailed part of the test so I can make an idea over it.

from react-notifications-component.

mdodge-ecgrow avatar mdodge-ecgrow commented on May 26, 2024

Sure, here is my full test with the non-relevant parts removed.

import React from 'react';
import {
	render,
	screen,
	within,
	fireEvent,
	cleanup,
	waitFor,
} from '@testing-library/react';
import '@testing-library/jest-dom';
import '@testing-library/react/dont-cleanup-after-each';
import Modal from 'react-modal';
import userEvent from '@testing-library/user-event';
import { Store } from 'react-notifications-component';
import BagModal from './BagModal';
import * as APIFunctions from '../../utils/APIFunctions';

const mockedEmptyFn = jest.fn();

const mockBaseProps = {
	openBagConsumption: true,
	setOpenBagConsumption: mockedEmptyFn,
	activeOrder: {
		bagID: '265-1514b-19',
		bagsOrdered: 20825,
		bagsPerPallet: 49,
		// ... lots more properties in here that don't matter
	},
	customStyles: {
		content: {
			backgroundColor: 'var(--color-primary)',
			border: '1px solid #ccc',
			boxShadow: '-2rem 2rem 2rem rgba(0, 0, 0, 0.5)',
			color: 'rgba(var(--RGB-text), 0.8)',
			filter: 'blur(0)',
			fontSize: '1.1em',
			fontWeight: 'bold',
			margin: '50px auto',
			opacity: 1,
			outline: 0,
			position: 'relative',
			visibility: 'visible',
			width: '500px',
		},
		overlay: {
			backgroundColor: 'rgba(255, 255, 255, 0.9)',
		},
	},
};

const mockBasePropsHidden = {
	...mockBaseProps,
	openBagConsumption: false,
};

Modal.setAppElement('body');

const Component = (props) => (
	<BagModal {...mockBaseProps} {...props} />
);

const HiddenModal = (props) => (
	<BagModal {...mockBasePropsHidden} {...props} />
);

describe('Bag Modal tests with editable inputs', () => {
	afterAll(() => {
		cleanup();
	});

	// removed all the tests that are passing, this is the only failing test

	test('Submitting a filed form should not show an error message', async (object, method) => {
		render(<Component />);
		jest
			.spyOn(APIFunctions, 'insertBagHistory')
			.mockResolvedValue('success');
		jest.spyOn(Store, 'addNotification').mockResolvedValue('success');
		const bagBatchInput = await screen.findByLabelText(
			/bag batch number/i
		);
		await userEvent.type(bagBatchInput, 'Test');
		const palletNumberInput = await screen.findByLabelText(
			/carton number/i
		);
		await userEvent.type(palletNumberInput, 'Test');

		const submitButton = await screen.getByText(/submit/i);
		await userEvent.click(submitButton);
		expect(screen.queryByTestId('error-msg')).not.toBeInTheDocument();
	});
});

And then here is the BagModal that I am testing:

import ReactModal from 'react-modal';
import React, { useRef, useState } from 'react';
import PropTypes from 'prop-types';
import CloseButton from './CloseButton';
import { Store } from 'react-notifications-component';
import { Scrollbars } from 'rc-scrollbars';
import { insertBagHistory } from '../../utils/APIFunctions';

const initialState = {
	caseID: '',
	palletID: '',
};

const BagModal = (props) => {
	const {
		openBagConsumption,
		setOpenBagConsumption,
		activeOrder,
		customStyles,
	} = props;

	const [userInput, setUserInput] = useState(initialState);
	const [inputError, setInputError] = useState('');

	const form = useRef();

	const handleTextChange = (evt) => {
		setInputError('');
		const { name, value } = evt.target;
		setUserInput({ ...userInput, [name]: value });
	};

	const validate = () => {
		return form.current.reportValidity();
	};

	const handleSubmit = async (evt) => {
		evt.preventDefault();

		// validate input fields
		if (!validate()) {
			setInputError('Please enter the Case ID and the Pallet ID.');
			form.current.querySelectorAll('input').forEach((element) => {
				if (element.checkValidity()) {
					if (element.type === 'text') {
						element.classList.remove('error-input');
					} else if (element.type === 'radio') {
						const divEl = element.closest('div');
						divEl.classList.remove('error-input');
					}
				} else {
					if (element.type === 'text') {
						element.classList.add('error-input');
					} else if (element.type === 'radio') {
						const divEl = element.closest('div');
						divEl.classList.add('error-input');
					}
				}
			});
		} else {
			// form is validated!
			// save to database
			const bagHistoryData = {
				orderID: activeOrder.orderNumber,
				caseID: userInput.caseID,
				palletID: userInput.palletID,
			};
			await insertBagHistory(bagHistoryData);
			Store.addNotification({
				type: 'success',
				title: 'Bag consumption added!',
				message: 'A new bag consumption has been added.',
				insert: 'top',
				container: 'top-center',
				animationIn: ['animated', 'bounceIn'],
				animationOut: ['animated', 'fadeOut'],
				dismiss: {
					duration: 4000,
					onScreen: true,
				},
			});
			setOpenBagConsumption(false);
		}
	};

	const cleanUp = () => {
		setUserInput(initialState);
		setInputError('');
	};

	const handleKeyDown = (e) => {
		// if Enter is pressed and the inputs are filled, save the bag consumption
		if (e.key === 'Enter') {
			handleSubmit(e).then(() => {
				// data submitted
			});
		}
	};

	return (
		<ReactModal
			isOpen={openBagConsumption}
			style={customStyles}
			className={'order-details-modal'}
			closeTimeoutMS={1000}
			onAfterClose={cleanUp}
		>
			<CloseButton setOpenModal={setOpenBagConsumption} />
			<h2 className={'title'}>
				Enter Bag Consumption for Item Number: {activeOrder.title}
			</h2>
			<Scrollbars autoHeight autoHeightMin={270} autoHeightMax={400}>
				<form
					className={'form modal-form'}
					ref={form}
					onSubmit={handleSubmit}
					noValidate
				>
					<label htmlFor={'caseID'}>D.O.R / Bag Batch Number:</label>
					<input
						type={'text'}
						className={'form-control mb-4'}
						id={'caseID'}
						name={'caseID'}
						maxLength={50}
						placeholder={'DOR / Bag Batch Number'}
						value={userInput.caseID}
						onChange={handleTextChange}
						autoFocus
						required
					/>
					<label htmlFor={'palletID'}>Pallet / Carton Number:</label>
					<input
						type={'text'}
						className={'form-control mb-3'}
						id={'palletID'}
						name={'palletID'}
						maxLength={50}
						placeholder={'Pallet / Carton Number'}
						value={userInput.palletID}
						onChange={handleTextChange}
						onKeyDown={handleKeyDown}
						required
					/>
					{inputError && (
						<p className={'text-warning'} data-testid={'error-msg'}>
							{inputError}
						</p>
					)}
					<br />
					<input
						className={'btn btn-primary d-block mx-auto mb-2'}
						type={'submit'}
						value={'Submit'}
					/>
				</form>
			</Scrollbars>
		</ReactModal>
	);
};

BagModal.propTypes = {
	openBagConsumption: PropTypes.bool.isRequired,
	setOpenBagConsumption: PropTypes.func.isRequired,
	activeOrder: PropTypes.object.isRequired,
	customStyles: PropTypes.object.isRequired,
};

export default BagModal;

The handleSubmit function is where the notification happens.

from react-notifications-component.

glennsayers avatar glennsayers commented on May 26, 2024

@mdodge-ecgrow I came across this same issue in my tests. For me, the issue was that I wasn't mounting the <ReactNotifications> component. During typical use this is mounted at the top level of the application, however when testing a component this isn't the case as only the component you're testing is being mounted, in your case BagModal. Easy to overlook.

A quick fix is to place your test component alongside the ReactNotification component:

render(
        <>
            <ReactNotifications />
            <EntryForm />
        </>
    );

from react-notifications-component.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.