I am trying to pass my unitTestData into the addtoproduction function however the console log is returning with null and I am not to sure why. The addtoproduction function is highlighted to asteriks
const unitTests = props => {
const autoComplete = useContext(AutoCompleteContext)
const loading = useContext(LoadingContext)
const snackbar = useContext(SnackbarContext)
const user = useContext(UserContext)
const query = new URLSearchParams(props.location.search)
const targetObjectFromURL = query.get('object')
const cookies = new Cookies()
const [unitTestsData, setUnitTestsData] = useState(null)
const [database, setDatabase] = useState('')
const [targetObject, setTargetObject] = useState(null)
const [unitTestsMode, setUnitTestsMode] = useState('Object')
useEffect(() => {
async function onLoadUnitTests() {
loading.setLoading(true)
const [objectsAutoCompleteResults, databaseAutoCompleteResults] = await Promise.all([
get('get_objects_autocomplete/all/all/b', user.user),
get('get_databases_autocomplete/a', user.user)
])
autoComplete.setObjectsAutoComplete(objectsAutoCompleteResults)
autoComplete.setDatabasesAutoComplete(databaseAutoCompleteResults)
if(targetObjectFromURL) setTargetObject(targetObjectFromURL)
else if(cookies.get('table')) setTargetObject(cookies.get('table'))
loading.setLoading(false)
}
onLoadUnitTests()
}, [])
useEffect(() => {
async function getUnitTests() {
loading.setLoading(true)
const optionalParams = unitTestsMode === 'Object'
? `?type=object&target=${targetObject}`
: `?type=database&target=${database}`
const url = `get_unit_tests${optionalParams}`
const results = await verifiedGet(url, user.user)
if(results.status === 0) {
setUnitTestsData(results.data)
} else if(results.status >= 20 && results.status <= 30) {
snackbar.statusCheck(results)
user.setSessionTokenMatches(false)
} else snackbar.statusCheck(results)
loading.setLoading(false)
}
if(targetObject || database) getUnitTests()
}, [targetObject,database])
const onObjectSelect = (e, value) => {
props.history.push(`/unittests?object=${value}`)
setTargetObject(value)
cookies.set('table', value, { path: '/'})
}
const onNewTestCaseClick = () => {
props.history.push(`/createtest?object=${targetObject}`)
}
const onDatabaseSelect = (e, value) => setDatabase(value)
const onChangeUnitTestsMode = (e) => setUnitTestsMode(e.target.getAttribute('value'))
** const addtoproduction = () => {
console.log(unitTestsData)
}**
return (
<PageWrapper title='View Unit Tests'>
<div className='Main UnitTestsAutoCompleteWrapper' style={{display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
<div style={{width: '30%', marginRight: '10px'}}>
<DropdownSingle
options={['Object', 'Database']}
value='Object'
name='Search unit tests'
onChange={onChangeUnitTestsMode}
/>
</div>
{unitTestsMode === 'Object' ?
<div>
<AutoCompleteSingle
name='Table'
label='Table'
options={autoComplete.objectsAutoComplete}
onChange={autoComplete.onObjectAutoCompleteFieldUpdate}
onSelect={onObjectSelect}
uniqueIdentifier={0}
initialValue={targetObject}
/>
</div> :
<div>
<AutoCompleteSingle
name='Database'
label='Database'
options={autoComplete.databasesAutoComplete}
onChange={autoComplete.onDatabaseAutoCompleteFieldUpdate}
onSelect={onDatabaseSelect}
uniqueIdentifier={1}
/>
<div>
<Button onClick={addtoproduction} />
</div>
</div>
}
</div>
{!unitTestsData ? null :
!unitTestsData.length ?
<div>
This table has no associated unit tests
<div style={{marginTop: '20px'}}>
<Button
text='New Test Case'
icon={<AddIcon className='ButtonIcon' />}
onClick={onNewTestCaseClick}
className='Button Dark Main'
style={{width: '200px'}}
/>
</div>
</div>
:
unitTestsMode === 'Object' ?
<UnitTestsObjectView unitTestsData={unitTestsData}/>
: <UnitTestsDatabaseView unitTestsData={unitTestsData} />
}
{!targetObject ? null :
<div style={{position: 'fixed', bottom: '80px', right: '40px'}}>
<Button
text='New Test Case'
icon={<AddIcon className='ButtonIcon' />}
onClick={onNewTestCaseClick}
className='Button Dark Main'
/>
</div>
}
</PageWrapper>
)
}
export default withRouter(unitTests)
Related
This error is shown in the browser console when Dialog (modal) is open:
Warning: Failed prop type: The prop onSubmit is marked as required
in ProjectWizardForm, but its value is undefined.
Code:
const UstHomeToolbar = ({ destroyProjectWizardForm, handleOpenAddPattern }) => {
const [open, setOpen] = useState(false);
const [openUploadForm, setOpenUploadForm] = useState(false);
const [projectWizardModalTitle, setProjectWizardModalTitle] = useState("");
const classes = useStyles();
const [selectedFile, setSelectedFile] = useState();
const [isFilePicked, setIsFilePicked] = useState(false);
const [statusText, setStatusText] = useState({
type: null,
msg: null,
});
const [userData, setUserData] = useState({});
useEffect(() => {
const userDataForPermissions = localStorage.getItem(LOGGED_IN_USER);
setUserData(JSON.parse(userDataForPermissions));
}, []);
const handleProjectDialogClickOpen = () => {
setOpen(true);
};
const handleProjectDialogClose = async () => {
setOpen(false);
};
const changeUploadHandler = (event) => {
setSelectedFile(event.target.files[0]);
setIsFilePicked(true);
};
const clearUploadHandler = (event) => {
setSelectedFile(null);
setIsFilePicked(false);
};
const handleUploadSubmission = () => {
const formData = new FormData();
formData.append("excel", selectedFile);
axios
.post("/project/excel", formData, {
headers: { "Content-Type": "multipart/form-data" },
})
.then((res) => {
clearUploadHandler();
setStatusText({
type: "success",
msg: "Excel file is uploaded successfully and taking you to the design....",
});
const design = res.data;
window.location.href = `/project/${design.project_id}/design/${design.id}`;
})
.catch((err) => {
console.error("Error:", err?.response?.data?.message);
clearUploadHandler();
setStatusText({
type: "error",
msg: err?.response?.data?.message,
});
});
};
const handleProjectDialogExited = () => {
destroyProjectWizardForm();
};
const handleProjectWizardModalTitle = (newModalTitle) =>
setProjectWizardModalTitle(newModalTitle);
return (
<Toolbar className={classes.toolbarHome}>
<Grid container justify="space-between">
<Grid item>
<Box className={classes.projectTitleStyle}>Projects</Box>
<Box className={classes.projectTitleDetailsStyle}>
List of ongoing and finished projects
</Box>
</Grid>
<Grid item>
<div className={classes.addProjectButtonStyle}>
<IxButton
onClick={() => setOpenUploadForm(true)}
handleClose={() => setOpenUploadForm(false)}
customClassName={TOOLBAR_BUTTON}
text="UPLOAD EXCEL"
inline={true}
disabled={!getPermission(userData, PROJECT).can_edit}
/>
<IxButton
onClick={handleProjectDialogClickOpen}
customClassName={TOOLBAR_BUTTON}
text="ADD PROJECT"
inline={true}
disabled={!getPermission(userData, PROJECT).can_edit}
/>
</div>
</Grid>
</Grid>
<IxDialog
handleClose={() => setOpenUploadForm(false)}
open={openUploadForm}
modalTitle={"Upload Design Excel"}
>
<div style={{ minHeight: "50px" }}>
Select an excel (*.xls) file:
<input
type="file"
onChange={changeUploadHandler}
accept=".xls"
style={{ marginLeft: "10px" }}
/>
</div>
<div style={{ marginBottom: "5px" }}>
{statusText.type && (
<Alert className={classes.statusText} severity={statusText.type}>
{statusText.msg}{" "}
</Alert>
)}
</div>
<div>
<Button
variant="contained"
component="label"
onClick={handleUploadSubmission}
disabled={!isFilePicked}
>
Upload File
</Button>
</div>
</IxDialog>
<IxDialog
handleClose={handleProjectDialogClose}
onExited={handleProjectDialogExited}
open={open}
modalTitle={projectWizardModalTitle}
>
<ProjectWizardForm
handleOpenAddPattern={handleOpenAddPattern}
handleClose={handleProjectDialogClose}
handleProjectWizardModalTitle={handleProjectWizardModalTitle}
/>
</IxDialog>
</Toolbar>
);
};
export default UstHomeToolbar;
ProjectWizardForm component:
const ProjectWizardForm = props => {
const [page, setPage] = useState(1);
const [touchedOnLoad, setTouchedOnLoad] = useState(false);
const history = useHistory();
const {
handleClose,
dispatch,
handleOpenAddPattern,
handleProjectWizardModalTitle
} = props;
useEffect(() => {
switch (page) {
case 1:
handleProjectWizardModalTitle('Add project');
break;
case 2:
handleProjectWizardModalTitle('Add target specs');
break;
case 3:
handleProjectWizardModalTitle('Add version details');
break;
default:
return;
}
}, [page, handleProjectWizardModalTitle]);
const saveData = values => {
const { submitButtonType } = values;
var errors = ProjectValidate(values);
if (Object.entries(errors).length > 0) {
if (errors?.number || errors?.drawn_date) {
setTouchedOnLoad(true);
setPage(1);
return;
} else if (errors.mandrel_id) {
setTouchedOnLoad(true);
setPage(3);
return;
}
}
const openAddPatternForm = newProject => {
const openAddPatternForm = true;
handleOpenAddPattern(openAddPatternForm, newProject);
};
dispatch(
addDesign(
formatProjectWizardSubmittedValue(values),
(err, newProject) => {
if (!err) {
dispatch(fetchProjects());
submitButtonType === ADD_PATTERN_NEXT_SUBMIT_BUTTON_TYPE
? openAddPatternForm(newProject)
: history.push(
`/project/${newProject.project_id}/details/${newProject.id}`
);
} else {
dispatch(setError(getFormatError(err)));
}
}
)
);
handleClose();
};
return (
<div>
{page === 1 && (
<UstProjectDetailForm
getDialogActionButtons={getAddProjectDetailsDialogActionButtons}
{...props}
touchedOnLoad={touchedOnLoad}
onSubmit={saveData}
nextPage={() => setPage(page + 1)}
/>
)}
{page === 2 && (
<UstProjectTargetSpecForm
{...props}
getDialogActionButtons={getAddTargetSpecDialogActionButtons}
onSubmit={saveData}
previousPage={() => setPage(page - 1)}
nextPage={() => setPage(page + 1)}
/>
)}
{page === 3 && (
<UstDesignDetailForm
{...props}
getDialogActionButtons={getAddDesignDetailsDialogActionButtons}
touchedOnLoad={touchedOnLoad}
onSubmit={saveData}
previousPage={() => setPage(page - 1)}
nextPage={() => setPage(page + 1)}
/>
)}
</div>
);
};
ProjectWizardForm.propTypes = {
onSubmit: PropTypes.func.isRequired
};
How can I fix this issue?
I'm new to React and I want to use Antdesign for image upload, but the component class component given in ant design's site. How can I convert the code below into a functional component?
in what way can i do this? Or is there any online tool that can do this?
here is my code :
function getBase64(img, callback) {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
function beforeUpload(file) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('You can only upload JPG/PNG file!');
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
}
class Avatar extends React.Component {
state = {
loading: false,
};
handleChange = info => {
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
getBase64(info.file.originFileObj, imageUrl =>
this.setState({
imageUrl,
loading: false,
}),
);
}
};
render() {
const { loading, imageUrl } = this.state;
const uploadButton = (
<div>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}>Upload</div>
</div>
);
return (
<Upload
name="avatar"
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
beforeUpload={beforeUpload}
onChange={this.handleChange}
>
{imageUrl ? <img src={imageUrl} alt="avatar" style={{ width: '100%' }} /> : uploadButton}
</Upload>
);
}
}
ReactDOM.render(<Avatar />, mountNode);
You will need to replace your class with a function that returns a JSX element.
You'll need to refactor your state to use the useState hook: https://reactjs.org/docs/hooks-state.html
DigitalOcean has a nice guide here: https://www.digitalocean.com/community/tutorials/five-ways-to-convert-react-class-components-to-functional-components-with-react-hooks
It will end up looking something like the following:
function getBase64(img, callback) {
const reader = new FileReader();
reader.addEventListener("load", () => callback(reader.result));
reader.readAsDataURL(img);
}
function beforeUpload(file) {
const isJpgOrPng = file.type === "image/jpeg" || file.type === "image/png";
if (!isJpgOrPng) {
message.error("You can only upload JPG/PNG file!");
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error("Image must smaller than 2MB!");
}
return isJpgOrPng && isLt2M;
}
function Avatar() {
const [imageUrl, setImageUrl] = useState(null);
const [loading, setLoading] = useState(false);
const handleChange = (info) => {
if (info.file.status === "uploading") {
setLoading(true)
return;
}
if (info.file.status === "done") {
// Get this url from response in real world.
getBase64(info.file.originFileObj, (imageUrl) => {
setImageUrl(imageUrl);
setLoading(false);
});
}
};
const uploadButton = (
<div>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}>Upload</div>
</div>
);
return (
<Upload
name="avatar"
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
beforeUpload={beforeUpload}
onChange={handleChange}
>
{imageUrl ? (
<img src={imageUrl} alt="avatar" style={{ width: "100%" }} />
) : (
uploadButton
)}
</Upload>
);
};
I fire an onclick called onEditScheduleClick this sets an object which is all fine, however when I click on an edit button on a different row data is overwritten in the props.setScheduleFieldData.
when I click the editButton the 70 will display too 70 and it will have the same dataabase name as ID 156.
As you can see, when I hit the edit button the data is logged fine. I am thinking I may need to create a unique instance for each row? Could someone help me out please!
Schedules
const schedules = () => {
const autoComplete = useContext(AutoCompleteContext)
const snackbar = useContext(SnackbarContext)
const loading = useContext(LoadingContext)
const user = useContext(UserContext)
const [scheduleData, setScheduleData] = useState([])
const [showScheduleModal, setShowScheduleModal] = useState(false)
const [scheduleFieldData, setScheduleFieldData] = useState({})
const [database, setDatabase] = useState('')
useEffect(() => {
async function onLoadScheduleData(){
loading.setLoading(true)
const [results, projectResults,databaseAutoCompleteResults] = await Promise.all([
get('get_testing_schedules', user.user),
get('get_projects_autocomplete/b', user.user),
get('get_databases_autocomplete/a', user.user)
])
autoComplete.setProjectsAutoComplete(projectResults)
autoComplete.setDatabasesAutoComplete(databaseAutoCompleteResults)
setScheduleData(results.data)
loading.setLoading(false)
}
onLoadScheduleData()
}, [])
const sortedScheduleData = [].concat(scheduleData)
.sort((a, b) => a.id < b.id ? 1 : -1)
const showModal = () => {
setShowScheduleModal(true)
setScheduleFieldData({})
}
const onCloseScheduleModal = () => setShowScheduleModal(false)
const onScheduleFieldUpdate = (e, valueFromAutoComplete, nameFromAutoComplete) => {
const name = nameFromAutoComplete ? nameFromAutoComplete
: e.target.name || e.target.getAttribute('name')
const value = valueFromAutoComplete ? valueFromAutoComplete.map(val => val.value).join(',')
: e.target.innerText ? e.target.innerText
: e.target.value
setScheduleFieldData({...scheduleFieldData, ...{[name]: value}})
console.log(name)
}
const onDatabaseSelect = async (e, value) => {
autoComplete.onDatabaseAutoCompleteFieldUpdate(e)
const name = e.target.getAttribute('name')
console.log(name)
setDatabase(value)
setScheduleFieldData({...scheduleFieldData, ...{[name]: value}})
}
console.log(scheduleFieldData, 'scheduleFieldData');
const onSubmitTestingScheduleClick = async () => {
loading.setLoading(true)
const results = await verifiedPost('post_testing_schedule', scheduleFieldData, user.user)
if (results.status === 0) {
setShowScheduleModal(false)
setScheduleFieldData({})
setScheduleData(results.data)
} else if(results.status >= 20 && results.status <= 30) {
user.setSessionTokenMatches(false)
}
snackbar.statusCheck(results)
loading.setLoading(false)
}
return (!scheduleData ? null :
<PageWrapper title='Schedules'>
<div style={{marginBottom: '20px'}}>
<Button
onClick={showModal}
className='Button Dark Main'
text='CREATE SCHEDULE'
/>
</div>
<div className='Card'>
<div className='TableTopbar ScheduleGrid'>
<div>ID</div>
<div>Interval</div>
<div>Project ID</div>
<div>Database</div>
<div>Create Timestamp</div>
<div>Create User Id</div>
<div>Edit</div>
</div>
{sortedScheduleData.map(schedule =>
<ScheduleDataRow
key={schedule.id}
schedule={schedule}
setScheduleData={setScheduleData}
onScheduleFieldUpdate={onScheduleFieldUpdate}
setScheduleFieldData={setScheduleFieldData}
scheduleFieldData={scheduleFieldData}
onDatabaseSelect={onDatabaseSelect}
/>
)}
</div>
<ScheduleModal
showScheduleModal={showScheduleModal}
onCloseScheduleModal={onCloseScheduleModal}
onScheduleFieldUpdate={onScheduleFieldUpdate}
scheduleFieldData={scheduleFieldData}
onSubmitTestingScheduleClick={onSubmitTestingScheduleClick}
scheduleFieldData={scheduleFieldData}
// onFieldUpdate={onFieldUpdate}
onDatabaseSelect={onDatabaseSelect}
database={database}
/>
</PageWrapper>
)
}
export default schedules
ScheduleDataRow:
const scheduleDataRow = props => {
const testing = useContext(TestingFrameworkContext)
const autoComplete = useContext(AutoCompleteContext)
const snackbar = useContext(SnackbarContext)
const loading = useContext(LoadingContext)
const user = useContext(UserContext)
const [usageMode, setUsageMode] = useState('Read')
const onEditScheduleClick = () => {
props.setScheduleFieldData({
'id': props.schedule.id,
'interval': props.schedule.interval,
'project_id': props.schedule.project_id,
'database': props.schedule.database.toString(),
})
setUsageMode('Edit')
}
const onSubmitTestingScheduleClick = async () => {
loading.setLoading(true)
const results = await verifiedPost('post_testing_schedule', props.scheduleFieldData, user.user)
if (results.status === 0) {
props.setScheduleData(results.data)
setUsageMode('Read')
} else if(results.status >= 20 && results.status <= 30) {
user.setSessionTokenMatches(false)
}
snackbar.statusCheck(results)
loading.setLoading(false)
}
const onCancelEditScheduleClick = () => setUsageMode('Read')
const getPartialDatabase = (database) => {
const split = database.split('::')
return split[split.length - 1]
}
return (
<div className='Table ScheduleGrid'>
{usageMode === 'Read' ?
<React.Fragment>
<div>{props.schedule.id}</div>
<div>{props.schedule.interval}</div>
<div>{props.schedule.project_id}</div>
<div>{getPartialDatabase(props.schedule.database)}</div>
<div>{props.schedule.create_timestamp}</div>
<div>{props.schedule.create_user_id}</div>
<div>
<EditIcon
style={{padding: '2px', width: '0.8em', height: '0.8em', marginRight: '5px'}}
className='CircleButton'
onClick={onEditScheduleClick}
/>
</div>
</React.Fragment>
:
<React.Fragment>
<div>{props.schedule.id}</div>
{configs.map((config, k) => {
const Field = config.field
return (
<div key={config.name} className='Main'>
<Field
uniqueIdentifier={config.name}
placeholder={config.label}
name={config.name}
uniqueIdentifier={k}
onChange={props.onScheduleFieldUpdate}
onSelect={props.onScheduleFieldUpdate}
onAutoCompleteOnChange={autoComplete.onProjectAutoCompleteFieldUpdate}
options={config.name === 'project_id' ? autoComplete.projectsAutoComplete : config.name === 'database' ? autoComplete.databasesAutoComplete : config.options}
value={props.scheduleFieldData[config.name]}
initialValues={
props.scheduleFieldData[config.name].split(',').filter(x => x).map(x => ({label: x, value: x}))
}
/>
</div>
)
})}
<div><AutoCompleteSingle
name='database'
label='Database'
options={autoComplete.databasesAutoComplete}
onChange={autoComplete.onDatabaseAutoCompleteFieldUpdate}
onSelect={props.onDatabaseSelect}
initialValue={props.scheduleFieldData['database']}
uniqueIdentifier={1}
triggerOnSelectOnBackspace
/></div>
<div>{props.schedule.create_timestamp}</div>
<div>{props.schedule.create_user_id}</div>
<div style={{display: 'flex', alignItems: 'flex-start'}}>
<div>
<Button
style={{marginRight: '10px'}}
text='Submit'
disabled={false}
className='Button Dark Main'
onClick={onSubmitTestingScheduleClick}
/>
</div>
<div>
<Button
text='Cancel'
className='Button Dark Main'
onClick={onCancelEditScheduleClick}
/>
</div>
</div>
</React.Fragment>
}
</div>
)
}
scheduleDataRow.propTypes = {
schedule: PropTypes.object,
setScheduleData: PropTypes.func
}
export default scheduleDataRow
In this react component, I want to render the data into my console when the 'TO FROM SELECTED UNITS' button is clicked by the user, but the button has to be pressed twice for the data to show in the console.
Please ignore:
It looks like your post is mostly code; please add some more details.
It looks like your post is mostly code; please add some more details.
It looks like your post is mostly code; please add some more details.
It looks like your post is mostly code; please add some more details.
Here is my code:
const unitTests = props => {
const autoComplete = useContext(AutoCompleteContext)
const loading = useContext(LoadingContext)
const snackbar = useContext(SnackbarContext)
const user = useContext(UserContext)
const query = new URLSearchParams(props.location.search)
const targetObjectFromURL = query.get('object')
const cookies = new Cookies()
const [unitTestsData, setUnitTestsData] = useState(null)
const [database, setDatabase] = useState('')
const [targetObject, setTargetObject] = useState(null)
const [unitTestsMode, setUnitTestsMode] = useState('Object')
const [AddRemoveDatabaseChanges, setAddRemoveDatabaseChanges] = useState(null)
const [AddRemoveDatabaseMode, setAddRemoveDatabaseMode] = useState('ADD')
console.log(unitTestsData);
useEffect(() => {
async function onLoadUnitTests() {
loading.setLoading(true)
const [objectsAutoCompleteResults, databaseAutoCompleteResults] = await Promise.all([
get('get_objects_autocomplete/all/all/b', user.user),
get('get_databases_autocomplete/a', user.user)
])
autoComplete.setObjectsAutoComplete(objectsAutoCompleteResults)
autoComplete.setDatabasesAutoComplete(databaseAutoCompleteResults)
if(targetObjectFromURL) setTargetObject(targetObjectFromURL)
else if(cookies.get('table')) setTargetObject(cookies.get('table'))
loading.setLoading(false)
}
onLoadUnitTests()
}, [])
useEffect(() => {
async function getUnitTests() {
loading.setLoading(true)
const optionalParams = unitTestsMode === 'Object'
? `?type=object&target=${targetObject}`
: `?type=database&target=${database}`
const url = `get_unit_tests${optionalParams}`
const results = await verifiedGet(url, user.user)
if(results.status === 0) {
setUnitTestsData(results.data)
} else if(results.status >= 20 && results.status <= 30) {
snackbar.statusCheck(results)
user.setSessionTokenMatches(false)
} else snackbar.statusCheck(results)
loading.setLoading(false)
}
if(targetObject || database) getUnitTests()
}, [targetObject,database])
const onObjectSelect = (e, value) => {
props.history.push(`/unittests?object=${value}`)
setTargetObject(value)
cookies.set('table', value, { path: '/'})
}
const onNewTestCaseClick = () => {
props.history.push(`/createtest?object=${targetObject}`)
}
const onDatabaseSelect = (e, value) => setDatabase(value)
const onChangeUnitTestsMode = (e) => setUnitTestsMode(e.target.getAttribute('value'))
const onChangeAddRemoveMode = (e) => setAddRemoveDatabaseMode(e.target.getAttribute('value'))
console.log(AddRemoveDatabaseMode);
const addtoproduction = () => {
let databaseChanges = unitTestsData.map(tests => {
return {
"unit_test_id": tests.unit_test_template_id,
"databases": tests.databases
}
})
setAddRemoveDatabaseChanges(databaseChanges)
if(AddRemoveDatabaseChanges != null && AddRemoveDatabaseMode === 'ADD'){
console.log('added data', AddRemoveDatabaseChanges);
} else if (AddRemoveDatabaseChanges != null && AddRemoveDatabaseMode === 'REMOVE') {
console.log('removed data', AddRemoveDatabaseChanges )
}
}
return (
<PageWrapper title='View Unit Tests'>
<div className='Main UnitTestsAutoCompleteWrapper' style={{display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
<div style={{width: '30%', marginRight: '10px'}}>
<DropdownSingle
options={['Object', 'Database']}
value='Object'
name='Search unit tests'
onChange={onChangeUnitTestsMode}
/>
</div>
{unitTestsMode === 'Object' ?
<div>
<AutoCompleteSingle
name='Table'
label='Table'
options={autoComplete.objectsAutoComplete}
onChange={autoComplete.onObjectAutoCompleteFieldUpdate}
onSelect={onObjectSelect}
uniqueIdentifier={0}
initialValue={targetObject}
/>
</div> :
<div style={{display: 'flex', justifyContent: 'space-between' }}>
<div style={{width: '100%'}}>
<DropdownSingle
options={['ADD', 'REMOVE']}
value='ADD'
name='Search unit tests'
onChange={onChangeAddRemoveMode}
/>
</div>
<AutoCompleteSingle
name='Database'
label='Database'
options={autoComplete.databasesAutoComplete}
onChange={autoComplete.onDatabaseAutoCompleteFieldUpdate}
onSelect={onDatabaseSelect}
uniqueIdentifier={1}
style={{width: '1000%'}}
/>
<div>
<Button
text='TO/FROM SELECTED UNIT TESTS'
onClick={addtoproduction}
/>
</div>
</div>
}
</div>
{!unitTestsData ? null :
!unitTestsData.length ?
<div>
This table has no associated unit tests
<div style={{marginTop: '20px'}}>
<Button
text='New Test Case'
icon={<AddIcon className='ButtonIcon' />}
onClick={onNewTestCaseClick}
className='Button Dark Main'
style={{width: '200px'}}
/>
</div>
</div>
:
unitTestsMode === 'Object' ?
<UnitTestsObjectView unitTestsData={unitTestsData}/>
: <UnitTestsDatabaseView unitTestsData={unitTestsData} />
}
{!targetObject ? null :
<div style={{position: 'fixed', bottom: '80px', right: '40px'}}>
<Button
text='New Test Case'
icon={<AddIcon className='ButtonIcon' />}
onClick={onNewTestCaseClick}
className='Button Dark Main'
/>
</div>
}
</PageWrapper>
)
}
export default withRouter(unitTests)
I have been unable to figure out how to test the event listeners and methods from the audio element here. I cannot get into either functions set called by the event handler (setDuration() or updateProgress). And, I'm not sure how to mock audioRef.current.play() audioRef.current.duration, etc. I've attached images showing the code coverage for tests. Currently, there's only a test to render.
I've tried something like this, but I get audioPlayer.timeupdate is not a function
const map = {};
audioPlayer.addEventListener = jest.fn((event, callback) => {
map[event] = callback;
});
act(() => {
audioPlayer.timeupdate();
});
From Audio.js
import PropTypes from "prop-types";
import get from "lodash.get";
import throttle from "lodash.throttle";
import Icon from "../../../components/core/elements/icon/default.jsx";
const AudioFlexPlayer = props => {
const {
podcastData,
overrides: { audioUrl, showPodcastSubLink }
} = props;
const audioRef = useRef();
const barWrapperRef = useRef();
const [canPlay, setCanPlay] = useState(false);
const [mouseDown, setMouseDown] = useState(false);
const [playState, setPlayState] = useState("paused");
const [progress, setProgress] = useState("");
const [timeStamp, setTimestamp] = useState("");
const audioSrc = audioUrl || get(podcastData, "audio.url");
const handleButtonClick = () => {
if (canPlay && audioRef.current.paused) {
audioRef.current.play();
setPlayState("playing");
} else if (canPlay && !audioRef.current.paused) {
audioRef.current.pause();
setPlayState("paused");
}
};
const handleMouseDown = () => {
setMouseDown(true);
};
const formatTime = duration => {
const minutes = Math.floor(duration / 60);
const seconds =
duration % 60 < 10
? `0${Math.round(duration % 60).toString()}`
: Math.round(duration % 60);
return `${minutes}:${seconds}`;
};
const subLinks = get(podcastData, "seriesMeta.subscriptionLinks");
const updatePlayhead = event => {
if (audioRef && mouseDown && barWrapperRef) {
const boundingRect = barWrapperRef.current.getBoundingClientRect();
const xPixelsFromEdgeOfelement = event.clientX - boundingRect.left;
setProgress(
Math.round(xPixelsFromEdgeOfelement / barWrapperRef.current.offsetWidth)
);
audioRef.current.currentTime =
(xPixelsFromEdgeOfelement / barWrapperRef.current.offsetWidth) *
audioRef.current.duration;
}
if (event.type !== "mousemove") setMouseDown(false);
};
useEffect(() => {
const audioPlayer = audioRef.current;
const setDuration = () => {
setTimestamp(formatTime(audioRef.current.duration));
setCanPlay(true);
};
const updateProgress = () => {
const elapsedTime = Math.round(audioRef.current.currentTime);
const elapsedTimeStamp = formatTime(elapsedTime);
setTimestamp(
`${elapsedTimeStamp}/${formatTime(audioRef.current.duration)}`
);
setProgress(
(elapsedTime / audioRef.current.duration) *
barWrapperRef.current.offsetWidth
);
};
if (audioPlayer) {
audioPlayer.addEventListener("timeupdate", throttle(updateProgress, 500));
audioPlayer.addEventListener("canplay", setDuration);
}
return () => {
audioPlayer.removeEventListener("timeupdate", updateProgress);
audioPlayer.removeEventListener("canplay", setDuration);
};
}, [progress]);
return (
<Fragment>
<div className="flex items-center justify-start">
<div
className="af-button brad-50 shadow flex items-center justify-center pointer mr-xs"
onClick={handleButtonClick}
style={{ minWidth: "32px", height: "32px" }}
>
{playState === "paused" && (
<Icon
className="fill-white"
name="play"
size="16"
aria-label="play"
/>
)}
{playState === "playing" && (
<Icon
className="fill-white"
name="pause"
size="16"
aria-label="pause"
/>
)}
</div>
<div
ref={barWrapperRef}
className="mr-xs pointer pad-top-xs pad-bottom-xs w-100"
style={{
width: "calc(100% - 120px)"
}}
onMouseDown={handleMouseDown}
onMouseMove={updatePlayhead}
onMouseUp={updatePlayhead}
onMouseLeave={updatePlayhead}
>
<div
className="mr-xs brad-2 bg-gray-lighter relative"
style={{
height: "4px",
minWidth: "25px"
}}
>
<div
className="progress-bar absolute brad-2 bg-blue left-0 pointer"
id="progress"
style={{
width: progress,
height: "4px",
top: "25%",
transform: "translateY(-25%)"
}}
>
<div
className="podcast-progress-point bg-white right-0 brad-50 b bc-gray-lighter absolute left-0"
style={{
width: "16px",
height: "16px",
top: "25%",
transform: "translateY(-47%)",
marginLeft: progress
}}
/>
</div>
</div>
</div>
<div className="gray-darkest">{timeStamp}</div>
</div>
{showPodcastSubLink && subLinks && (
<div>
<span className="gray-dark pad-right-xxs">Add to:</span>
{subLinks && subLinks.applePodcasts && (
<a
className="blue pad-right-xxs"
id="applePodcasts"
href={podcastData.seriesMeta.subscriptionLinks.applePodcasts}
>
Apple Podcasts,
</a>
)}
{subLinks && subLinks.googlePlay && (
<a
className="blue pad-right-xxs"
id="googlePlay"
href={podcastData.seriesMeta.subscriptionLinks.googlePlay}
>
Google Podcasts,
</a>
)}
{subLinks && subLinks.stitcher && (
<a
className="blue"
id="stitcher"
href={podcastData.seriesMeta.subscriptionLinks.stitcher}
>
Stitcher
</a>
)}
</div>
)}
<audio
ref={audioRef}
src={audioSrc}
preload="metadata"
type="audio/mp3"
/>
</Fragment>
);
};
AudioFlexPlayer.propTypes = {
podcastData: PropTypes.object,
overrides: PropTypes.object
};
export default AudioFlexPlayer;```
[screenshot of coverage][1]
[screenshot of coverage][2]
[1]: https://i.stack.imgur.com/7NsZK.png
[2]: https://i.stack.imgur.com/9CUFt.png