Skip to content
Snippets Groups Projects
use-run-tests-button.ts 5.68 KiB
import React from 'react'
import { useDropzone } from 'react-dropzone'
import { useQueryClient } from 'react-query'
import {
  FileWithName,
  Formalization,
  Implementation,
  Pattern,
} from 'src/models'
import {
  MAX_FILE_SIZE,
  SIZE_TOO_LARGE,
} from 'src/pages/patterns/add-pattern/use-add-formalizations'
import { GetFormalizationsQuery } from 'src/pages/patterns/query-builder/get-formalizations-query'
import { RunTestsStep1Mutation } from '../query-builder/run-tests-step-1-mutation'
import { RunTestsStep2Mutation } from '../query-builder/run-tests-step-2-mutation'
import { useToast } from '@chakra-ui/react'
import { navigate, routes } from '@redwoodjs/router'

export function useRunTestsButton({
  patternId,
  implementationId,
  onClose,
}: {
  patternId: Pattern['id']
  implementationId: Implementation['id']
  onClose: () => void
}) {
  const queryClient = useQueryClient()
  const toast = useToast()

  const [name, setName] = React.useState('')

  React.useEffect(() => {
    queryClient.invalidateQueries(GetFormalizationsQuery.key)
  }, [])

  // Formalization
  const [formalizationId, setFormalizationId] =
    React.useState<Formalization['id']>('')

  const formalizationsQuery = GetFormalizationsQuery.useQuery(
    {
      patternId: patternId,
    },
    {
      onSettled: (data) => {
        setFormalizationId(data[0]?.id ?? '')
      },
    }
  )

  // Strategy
  const [strategy, setStrategy] = React.useState<FileWithName>()
  const onDropStrategy = React.useCallback((acceptedFiles: File[]) => {
    if (acceptedFiles.length > 1) {
      throw Error('Expected 1 strategy file')
    }
    const res = acceptedFiles.map(
      (f) =>
        ({
          file: f,
          name: f.name.split('.')[0] ?? f.name,
        } as FileWithName)
    )
    setStrategy(res[0])

    return [...res]
  }, [])
  const strategyDropzoneState = useDropzone({
    onDrop: onDropStrategy,
    noDrag: true,
    validator: (file) => {
      if (file.size > MAX_FILE_SIZE) {
        return {
          code: SIZE_TOO_LARGE,
          message: `Maximo ${MAX_FILE_SIZE} MB.`,
        }
      }
      return null
    },
  })

  const onRemoveStrategy = () => setStrategy(null)

  // Test cases instantiation
  const [testCasesInstantiation, setTestCasesInstantiation] =
    React.useState<FileWithName>()
  const onDropTestCasesInstantiation = React.useCallback(
    (acceptedFiles: File[]) => {
      if (acceptedFiles.length > 1) {
        throw Error('Expected 1 test file')
      }
      const res = acceptedFiles.map(
        (f) =>
          ({
            file: f,
            name: f.name.split('.')[0] ?? f.name,
          } as FileWithName)
      )
      setTestCasesInstantiation(res[0])

      return [...res]
    },
    []
  )

  const testCasesInstantiationDropzoneState = useDropzone({
    onDrop: onDropTestCasesInstantiation,
    noDrag: true,
    validator: (file) => {
      if (file.size > MAX_FILE_SIZE) {
        return {
          code: SIZE_TOO_LARGE,
          message: `Maximo ${MAX_FILE_SIZE} MB.`,
        }
      }
      return null
    },
  })

  const onRemoveTestCasesInstantiation = () => setTestCasesInstantiation(null)

  // Wrapper
  const [wrapper, setWrapper] = React.useState<FileWithName>()
  const onDropWrapper = React.useCallback((acceptedFiles: File[]) => {
    if (acceptedFiles.length > 1) {
      throw Error('Expected 1 test file')
    }
    const res = acceptedFiles.map(
      (f) =>
        ({
          file: f,
          name: f.name.split('.')[0] ?? f.name,
        } as FileWithName)
    )
    setWrapper(res[0])

    return [...res]
  }, [])

  const wrapperDropzoneState = useDropzone({
    onDrop: onDropWrapper,
    noDrag: true,
    validator: (file) => {
      if (file.size > MAX_FILE_SIZE) {
        return {
          code: SIZE_TOO_LARGE,
          message: `Maximo ${MAX_FILE_SIZE} MB.`,
        }
      }
      return null
    },
  })

  const onRemoveWrapper = () => setWrapper(null)

  // Utils

  const isValid = React.useMemo(() => {
    return (
      Boolean(formalizationId) &&
      Boolean(strategy) &&
      Boolean(testCasesInstantiation) &&
      Boolean(name)
    )
  }, [formalizationId, strategy, testCasesInstantiation, name])

  const runTestsMutation = RunTestsStep1Mutation.useMutation()

  type Step = 'Generating' | 'Testing' | 'Loading'
  const [step, setStep] = React.useState<Step>('Generating')
  const [genericJUnitPath, setGenericJUnitPath] = React.useState<string>(null)
  const handleOnRunTestsStep1 = async () => {
    if (!isValid) return

    setStep('Loading')

    const response = await runTestsMutation.mutateAsync({
      id: formalizationId,
      strategyFile: strategy.file,
      testCasesInstantiationFile: testCasesInstantiation.file,
    })

    setGenericJUnitPath(response.path)
    setStep('Testing')
  }

  const step2Mutation = RunTestsStep2Mutation.useMutation({
    onSuccess: (report) => {
      navigate(routes.report({ id: report.id }))
    },
  })
  const handleOnRunTestsStep2 = async () => {
    setStep('Loading')

    await step2Mutation.mutateAsync({
      id: formalizationId,
      implementationId,
      wrapperFile: wrapper.file,
      name,
    })

    onClose()

    toast({
      status: 'success',
      position: 'top',
      title: `Se han corrido los tests satisfactoriamente.`,
    })
  }

  return {
    name,
    setName,
    formalizations: formalizationsQuery.data ?? [],
    formalizationId,
    setFormalizationId,
    isValid,
    strategyDropzoneState,
    strategy,
    onRemoveStrategy,
    testCasesInstantiationDropzoneState,
    testCasesInstantiation,
    onRemoveTestCasesInstantiation,
    handleOnRunTestsStep1,
    handleOnRunTestsStep2,
    wrapper,
    onRemoveWrapper,
    wrapperDropzoneState,
    genericJUnitPath,
    step,
  }
}