Test Webcam Video using React Testing Library RTL and JEST

212 Views Asked by At

I working on app which records video using webcam and saves, well this feature is working perfectly, but i also want to write test for it whether its saving successfully or not. For this I am using RTL / Jest for it and i am quite struggling to write, i have write tests: -> user open recording , start recording and close recording (Tests Passed) -> But not unable to write test to check save recording, i have check after save recording whether blob state is set or not, if set then pass the test, but the state is not updating, its getting initialized value -> component structure , parent component Template form which is using react-hook-form, and in it child component , i send props including blob/setchange function to webcam and then webcam returns the blob file to template form.

export const Webcam = function ({propBlob,propFile,onUpdate,featureFlags}){

  const recordWebcam = useRecordWebcam(OPTIONS);
  const [recordingDuration, setRecordingDuration] = useState(0);
  const [videoBlob, setVideoBlob] = useState(propBlob);
  const [start, setStart] = useState(false);
 
                       
  const handleStartRecord = () => {
    setStart(true);
    setTimeout(() => {
      setStart(false);
      recordWebcam.start();
    }, 3000);
  };

  useEffect(() => {
    if (recordWebcam.status === "RECORDING") {
      const interval = setInterval(() => {
        setRecordingDuration((prevDuration) => prevDuration + 1);
      }, 1000);
      return () => clearInterval(interval);
    }
  }, [recordWebcam.status === "RECORDING"]);

  useEffect(() => {
    if (recordWebcam.status === "RECORDING") {
      return () => setRecordingDuration(0);
    }
  }, [recordWebcam.status !== "RECORDING"]);

  const setBlob = async (recordWebcam) => {
    recordWebcam.download();
    const blob = await recordWebcam.getRecording();
    setVideoBlob(blob);
    onUpdate(blob)  // calling update prop function


  };

  const handleOpen = () => {
    recordWebcam.open();

  }



  const minutes = Math.floor(recordingDuration / 60);
  const seconds = Math.floor(recordingDuration % 60);
  const formattedTime = `${minutes}:${seconds.toString().padStart(2, "0")}`;

  const handleOutsideClick = (e) => {
    if (recordWebcam.webcamRef.current && !recordWebcam.webcamRef.current.contains(e.target)) {
      recordWebcam.close()
    }
  };

  useEffect(() => {
    if (recordWebcam.status === 'RECORDING' || recordWebcam.status === 'OPEN' || recordWebcam.status === 'INIT') {
      document.addEventListener('click', handleOutsideClick);
    }

    return () => {
      document.removeEventListener('click', handleOutsideClick);
    };
  }, [recordWebcam.status]);

  const handleRemoveListener = () => {
    document.removeEventListener('click', handleOutsideClick);
  }

  return (
    <>
      <div className="mainDiv ">
        <div className="d-flex justify-content-center">
        <div className="d-flex justify-content-center gap-3 align-items-center">
        {videoBlob && <Checkmark size='44px' /> }
         {FEATURE_FLAGS['browser-video']==true? <button
            data-bs-toggle="modal"
            data-bs-target="#videoRecord"
            type="button"
            className="btn-openClose"
            onClick={handleOpen}
          >
            Record Video
          </button>:null}
        </div>
        </div>
      
        {videoBlob && (
          <video  src={URL.createObjectURL(videoBlob)} height={300} width={500} controls/>
        )}
     
        <div
          className="modal fade"
          id="videoRecord"
          tabIndex="-1"    // changed here
          aria-labelledby="videoRecordLable"
          aria-hidden="true"
          ref={nodeRef}
        >
      </div>
    </>
  );
};


export const TemplateForm = function (props) {
  const [isLoading, setIsLoading] = useState(false);
  const [videoblob, setVideoBlob] = useState('hello');
  const [disabled, setDisabled] = useState(() => {
    if (props && props.disabled) {
      return props.disabled;
    }
    return false;
  });

  

  const onSave = async function (formValues) {
   // saving logic
  };

  const onSubmit = function (formValues) {
    let url_file=''
    let url_blob=''
    if (latest=="blob"){
      //const blob = new Blob([videoBlob], { type: 'application/octet-stream' });
      url_blob=URL.createObjectURL(videoblob)
      formValues['recorded-video']=url_blob
      // formValues['recorded-video']=url_blob;
    }
    else{
      //const file=new Blob([fileData],{ type: 'application/octet-stream' })
      url_file=URL.createObjectURL(fileData)
      formValues['recorded-video']=url_file
      // formValues['recorded-video']=url_file;
    }

    console.log(" Values ",formValues);
    setIsLoading(true);

    onSave(formValues);
  };

  
  const handleVideoUpdate = (blob) => {
    setVideoBlob(blob);
  };

  return (
    <div>
      <FormProvider {...methods}>
        <form onSubmit={methods.handleSubmit(onSubmit)}>
          <section className="app-card">
           
           
            <Webcam propBlob={videoBlob} onUpdate={handleVideoUpdate}/>
            {errors["base-video"] && (
              <p className="text-danger">{errors["base-video"].message}</p>
            )}
          </section>
             <p data-testid="rec-video-test">{videoblob}</p>
            {isLoading ? "Saving" : "Save Template"}
          </button>
        </form>
      </FormProvider>
    </div>
  );
};

My test Script

test("Video is recording successfully", async () => {
    const TestForm = () => {
    const methods = useForm();
    const mockElement: HTMLElement = document.createElement('div');
    global.URL.createObjectURL = jest.fn();
    mockElement.textContent = JSON.stringify({ host: 'localhost' }); // Provide the expected content
    mockElement.accessKey = '';
    mockElement.autocapitalize = '';
    mockElement.dir = '';
    const getBlob=jest.fn()
  
      // Spy on getElementById and return the mock element
    jest.spyOn(document, 'getElementById').mockReturnValue(mockElement);
    return (
          <FormProvider {...methods}>
              <TemplateForm/>
          </FormProvider>
    )
    
  };
  render(<TestForm/>)
  
  const recordBtn = screen.getByText('Record video')
  fireEvent.click(recordBtn)  // click to check popup  // Passed

  await waitFor(() => {}, { timeout: 5000 });

  const startBtn= screen.getByText("Start Recording")
  fireEvent.click(startBtn) // on popup click recording //Passed

  const stopRecord=document.getElementsByClassName('stop-btn-theme');
  const recElement = stopRecord[0];
  expect (recElement).toBeInTheDocument  // Passed

  fireEvent.click(recElement) // when stop button click
  const savebtn=screen.getByText('Save & Continue')  //its to save video recording //Passed

  await act(async () => {   // This test Failing
  // trigger the state update here
  const videoSave = screen.getByTestId('rec-video-test'); // it always give initial values Hello
  const updatedState = videoSave.textContent;
  expect(updatedState).toContain('blob:');
  
});

In my test script when i screen debug hello

it gives this which is initialized, it not giving the updated videoBlob value, the code was big so just pasted it here the important components, i just need some path how to solve this,any suggestions would help a lot.

0

There are 0 best solutions below