2
0

perf(e2e): ️ Migrate to Playwright

This commit is contained in:
Baptiste Arnaud
2022-01-28 09:42:31 +01:00
parent c5aaa323d1
commit 73f277fce7
145 changed files with 3104 additions and 2346 deletions

View File

@ -1,7 +1,7 @@
import { Flex } from '@chakra-ui/react'
import React from 'react'
import Graph from './graph/Graph'
import { DndContext } from 'contexts/DndContext'
import { StepDndContext } from 'contexts/StepDndContext'
import { StepTypesList } from './StepTypesList'
import { PreviewDrawer } from './preview/PreviewDrawer'
import { RightPanel, useEditor } from 'contexts/EditorContext'
@ -12,14 +12,14 @@ export const Board = () => {
const { rightPanel } = useEditor()
return (
<Flex flex="1" pos="relative" bgColor="gray.50" h="full">
<DndContext>
<StepDndContext>
<StepTypesList />
<GraphProvider>
<Graph flex="1" />
<BoardMenuButton pos="absolute" right="40px" top="20px" />
{rightPanel === RightPanel.PREVIEW && <PreviewDrawer />}
</GraphProvider>
</DndContext>
</StepDndContext>
</Flex>
)
}

View File

@ -1,6 +1,6 @@
import { Flex, HStack, StackProps, Text } from '@chakra-ui/react'
import { StepType, DraggableStepType } from 'models'
import { useDnd } from 'contexts/DndContext'
import { useStepDnd } from 'contexts/StepDndContext'
import React, { useEffect, useState } from 'react'
import { StepIcon } from './StepIcon'
import { StepTypeLabel } from './StepTypeLabel'
@ -12,7 +12,7 @@ export const StepCard = ({
type: DraggableStepType
onMouseDown: (e: React.MouseEvent, type: DraggableStepType) => void
}) => {
const { draggedStepType } = useDnd()
const { draggedStepType } = useStepDnd()
const [isMouseDown, setIsMouseDown] = useState(false)
useEffect(() => {

View File

@ -13,12 +13,12 @@ import {
IntegrationStepType,
LogicStepType,
} from 'models'
import { useDnd } from 'contexts/DndContext'
import { useStepDnd } from 'contexts/StepDndContext'
import React, { useState } from 'react'
import { StepCard, StepCardOverlay } from './StepCard'
export const StepTypesList = () => {
const { setDraggedStepType, draggedStepType } = useDnd()
const { setDraggedStepType, draggedStepType } = useStepDnd()
const [position, setPosition] = useState({
x: 0,
y: 0,

View File

@ -8,7 +8,7 @@ import {
import React, { useEffect, useMemo, useState } from 'react'
import { Block } from 'models'
import { useGraph } from 'contexts/GraphContext'
import { useDnd } from 'contexts/DndContext'
import { useStepDnd } from 'contexts/StepDndContext'
import { StepsList } from './StepsList'
import { filterTable, isDefined } from 'utils'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
@ -22,8 +22,8 @@ type Props = {
export const BlockNode = ({ block }: Props) => {
const { connectingIds, setConnectingIds, previewingEdgeId } = useGraph()
const { typebot, updateBlock } = useTypebot()
const { setMouseOverBlockId } = useDnd()
const { draggedStep, draggedStepType } = useDnd()
const { setMouseOverBlockId } = useStepDnd()
const { draggedStep, draggedStepType } = useStepDnd()
const [isMouseDown, setIsMouseDown] = useState(false)
const [isConnecting, setIsConnecting] = useState(false)
const isPreviewing = useMemo(() => {

View File

@ -1,5 +1,5 @@
import { Flex, Portal, Stack, Text, useEventListener } from '@chakra-ui/react'
import { useDnd } from 'contexts/DndContext'
import { useStepDnd } from 'contexts/StepDndContext'
import { Coordinates } from 'contexts/GraphContext'
import { useTypebot } from 'contexts/TypebotContext'
import { ChoiceInputStep, ChoiceItem } from 'models'
@ -19,7 +19,7 @@ export const ChoiceItemsList = ({ step }: ChoiceItemsListProps) => {
mouseOverBlockId,
setDraggedChoiceItem,
setMouseOverBlockId,
} = useDnd()
} = useStepDnd()
const showSortPlaceholders = useMemo(
() => mouseOverBlockId === step.blockId && draggedChoiceItem,
[draggedChoiceItem, mouseOverBlockId, step.blockId]

View File

@ -28,7 +28,6 @@ export const VideoUploadContent = ({ content, onSubmit }: Props) => {
placeholder="Paste the video link..."
initialValue={content?.url ?? ''}
onChange={handleUrlChange}
delay={100}
/>
<Text fontSize="sm" color="gray.400" textAlign="center">
Works with Youtube, Vimeo and others

View File

@ -31,13 +31,12 @@ export const ChoiceInputSettingsBody = ({
/>
{options?.isMultipleChoice && (
<Stack>
<FormLabel mb="0" htmlFor="send">
<FormLabel mb="0" htmlFor="button">
Button label:
</FormLabel>
<DebouncedInput
id="send"
id="button"
initialValue={options?.buttonLabel ?? 'Send'}
delay={100}
onChange={handleButtonLabelChange}
/>
</Stack>

View File

@ -40,7 +40,6 @@ export const ComparisonItem = ({
/>
{item.comparisonOperator !== ComparisonOperators.IS_SET && (
<InputWithVariableButton
delay={100}
initialValue={item.value ?? ''}
onChange={handleChangeValue}
placeholder="Type a value..."

View File

@ -21,6 +21,7 @@ export const ConditionSettingsBody = ({
return (
<TableList<Comparison>
initialItems={options.comparisons}
onItemsChange={handleComparisonsChange}
Item={ComparisonItem}
ComponentBetweenItems={() => (

View File

@ -49,7 +49,6 @@ export const DateInputSettingsBody = ({
<DebouncedInput
id="from"
initialValue={options.labels.from}
delay={100}
onChange={handleFromChange}
/>
</Stack>
@ -62,7 +61,6 @@ export const DateInputSettingsBody = ({
<DebouncedInput
id="to"
initialValue={options.labels.to}
delay={100}
onChange={handleToChange}
/>
</Stack>
@ -74,7 +72,6 @@ export const DateInputSettingsBody = ({
<DebouncedInput
id="button"
initialValue={options.labels.button}
delay={100}
onChange={handleButtonLabelChange}
/>
</Stack>

View File

@ -29,7 +29,6 @@ export const EmailInputSettingsBody = ({
<DebouncedInput
id="placeholder"
initialValue={options.labels.placeholder}
delay={100}
onChange={handlePlaceholderChange}
/>
</Stack>
@ -40,7 +39,6 @@ export const EmailInputSettingsBody = ({
<DebouncedInput
id="button"
initialValue={options.labels.button}
delay={100}
onChange={handleButtonLabelChange}
/>
</Stack>

View File

@ -51,7 +51,6 @@ export const GoogleAnalyticsSettings = ({
id="tracking-id"
initialValue={options?.trackingId ?? ''}
placeholder="G-123456..."
delay={100}
onChange={handleTrackingIdChange}
/>
</Stack>
@ -63,7 +62,6 @@ export const GoogleAnalyticsSettings = ({
id="category"
initialValue={options?.category ?? ''}
placeholder="Example: Typebot"
delay={100}
onChange={handleCategoryChange}
/>
</Stack>
@ -75,7 +73,6 @@ export const GoogleAnalyticsSettings = ({
id="action"
initialValue={options?.action ?? ''}
placeholder="Example: Submit email"
delay={100}
onChange={handleActionChange}
/>
</Stack>
@ -98,7 +95,6 @@ export const GoogleAnalyticsSettings = ({
id="label"
initialValue={options?.label ?? ''}
placeholder="Example: Campaign Z"
delay={100}
onChange={handleLabelChange}
/>
</Stack>

View File

@ -149,7 +149,7 @@ const ActionOptions = ({
case GoogleSheetsAction.INSERT_ROW:
return (
<TableList<Cell>
initialItems={options.cellsToInsert}
initialItems={options.cellsToInsert ?? { byId: {}, allIds: [] }}
onItemsChange={handleInsertColumnsChange}
Item={UpdatingCellItem}
addLabel="Add a value"
@ -167,7 +167,7 @@ const ActionOptions = ({
/>
<Text>Cells to update</Text>
<TableList<Cell>
initialItems={options.cellsToUpsert}
initialItems={options.cellsToUpsert ?? { byId: {}, allIds: [] }}
onItemsChange={handleUpsertColumnsChange}
Item={UpdatingCellItem}
addLabel="Add a value"
@ -186,7 +186,7 @@ const ActionOptions = ({
/>
<Text>Cells to extract</Text>
<TableList<ExtractingCell>
initialItems={options.cellsToExtract}
initialItems={options.cellsToExtract ?? { byId: {}, allIds: [] }}
onItemsChange={handleExtractingCellsChange}
Item={ExtractingCellItem}
addLabel="Add a value"

View File

@ -30,8 +30,8 @@ export const SheetsDropdown = ({
selectedItem={currentSheet?.name}
items={(sheets ?? []).map((s) => s.name)}
onValueChange={handleSpreadsheetSelect}
placeholder={isLoading ? 'Loading...' : 'Select the sheet'}
isDisabled={isLoading}
placeholder={'Select the sheet'}
isLoading={isLoading}
/>
)
}

View File

@ -28,8 +28,8 @@ export const SpreadsheetsDropdown = ({
selectedItem={currentSpreadsheet?.name}
items={(spreadsheets ?? []).map((s) => s.name)}
onValueChange={handleSpreadsheetSelect}
placeholder={isLoading ? 'Loading...' : 'Search for spreadsheet'}
isDisabled={isLoading}
placeholder={'Search for spreadsheet'}
isLoading={isLoading}
/>
)
}

View File

@ -37,7 +37,6 @@ export const NumberInputSettingsBody = ({
<DebouncedInput
id="placeholder"
initialValue={options.labels.placeholder}
delay={100}
onChange={handlePlaceholderChange}
/>
</Stack>
@ -48,7 +47,6 @@ export const NumberInputSettingsBody = ({
<DebouncedInput
id="button"
initialValue={options?.labels?.button ?? 'Send'}
delay={100}
onChange={handleButtonLabelChange}
/>
</Stack>

View File

@ -29,7 +29,6 @@ export const PhoneNumberSettingsBody = ({
<DebouncedInput
id="placeholder"
initialValue={options.labels.placeholder}
delay={100}
onChange={handlePlaceholderChange}
/>
</Stack>
@ -40,7 +39,6 @@ export const PhoneNumberSettingsBody = ({
<DebouncedInput
id="button"
initialValue={options.labels.button}
delay={100}
onChange={handleButtonLabelChange}
/>
</Stack>

View File

@ -1,6 +1,7 @@
import { FormLabel, Stack } from '@chakra-ui/react'
import { DebouncedInput } from 'components/shared/DebouncedInput'
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
import { InputWithVariableButton } from 'components/shared/TextboxWithVariableButton'
import { RedirectOptions } from 'models'
import React from 'react'
@ -21,11 +22,10 @@ export const RedirectSettings = ({ options, onOptionsChange }: Props) => {
<FormLabel mb="0" htmlFor="tracking-id">
Url:
</FormLabel>
<DebouncedInput
<InputWithVariableButton
id="tracking-id"
initialValue={options.url ?? ''}
placeholder="Type a URL..."
delay={100}
onChange={handleUrlChange}
/>
</Stack>

View File

@ -37,7 +37,6 @@ export const SetVariableSettingsBody = ({
<DebouncedTextarea
id="expression"
initialValue={options.expressionToEvaluate ?? ''}
delay={100}
onChange={handleExpressionChange}
/>
</Stack>

View File

@ -38,7 +38,6 @@ export const TextInputSettingsBody = ({
<DebouncedInput
id="placeholder"
initialValue={options.labels.placeholder}
delay={100}
onChange={handlePlaceholderChange}
/>
</Stack>
@ -49,7 +48,6 @@ export const TextInputSettingsBody = ({
<DebouncedInput
id="button"
initialValue={options.labels.button}
delay={100}
onChange={handleButtonLabelChange}
/>
</Stack>

View File

@ -29,7 +29,6 @@ export const UrlInputSettingsBody = ({
<DebouncedInput
id="placeholder"
initialValue={options.labels.placeholder}
delay={100}
onChange={handlePlaceholderChange}
/>
</Stack>
@ -40,7 +39,6 @@ export const UrlInputSettingsBody = ({
<DebouncedInput
id="button"
initialValue={options.labels.button}
delay={100}
onChange={handleButtonLabelChange}
/>
</Stack>

View File

@ -29,7 +29,6 @@ export const VariableForTestInputs = ({
<FormLabel htmlFor={'value' + id}>Test value:</FormLabel>
<DebouncedInput
id={'value' + id}
delay={100}
initialValue={item.value ?? ''}
onChange={handleValueChange}
/>

View File

@ -130,7 +130,7 @@ export const WebhookSettings = ({
</AccordionButton>
<AccordionPanel pb={4} as={Stack} spacing="6">
<TableList<KeyValue>
initialItems={webhook?.queryParams}
initialItems={webhook?.queryParams ?? { byId: {}, allIds: [] }}
onItemsChange={handleQueryParamsChange}
Item={QueryParamsInputs}
addLabel="Add a param"
@ -144,7 +144,7 @@ export const WebhookSettings = ({
</AccordionButton>
<AccordionPanel pb={4} as={Stack} spacing="6">
<TableList<KeyValue>
initialItems={webhook?.headers}
initialItems={webhook?.headers ?? { byId: {}, allIds: [] }}
onItemsChange={handleHeadersChange}
Item={HeadersInputs}
addLabel="Add a value"
@ -171,7 +171,9 @@ export const WebhookSettings = ({
</AccordionButton>
<AccordionPanel pb={4} as={Stack} spacing="6">
<TableList<VariableForTest>
initialItems={options?.variablesForTest}
initialItems={
options?.variablesForTest ?? { byId: {}, allIds: [] }
}
onItemsChange={handleVariablesChange}
Item={VariableForTestInputs}
addLabel="Add an entry"
@ -194,7 +196,9 @@ export const WebhookSettings = ({
</AccordionButton>
<AccordionPanel pb={4} as={Stack} spacing="6">
<TableList<ResponseVariableMapping>
initialItems={options?.responseVariableMapping}
initialItems={
options?.responseVariableMapping ?? { byId: {}, allIds: [] }
}
onItemsChange={handleResponseMappingChange}
Item={ResponseMappingInputs}
/>

View File

@ -8,10 +8,9 @@ import {
} from '@chakra-ui/react'
import React, { useEffect, useState } from 'react'
import { BubbleStep, DraggableStep, Step, TextBubbleStep } from 'models'
import { useGraph } from 'contexts/GraphContext'
import { Coordinates, useGraph } from 'contexts/GraphContext'
import { StepIcon } from 'components/board/StepTypesList/StepIcon'
import { isBubbleStep, isTextBubbleStep } from 'utils'
import { Coordinates } from '@dnd-kit/core/dist/types'
import { TextEditor } from './TextEditor/TextEditor'
import { StepNodeContent } from './StepNodeContent/StepNodeContent'
import { useTypebot } from 'contexts/TypebotContext'
@ -43,7 +42,8 @@ export const StepNode = ({
) => void
}) => {
const { query } = useRouter()
const { setConnectingIds, connectingIds } = useGraph()
const { setConnectingIds, connectingIds, openedStepId, setOpenedStepId } =
useGraph()
const { moveStep } = useTypebot()
const [isConnecting, setIsConnecting] = useState(false)
const [mouseDownEvent, setMouseDownEvent] =
@ -57,6 +57,11 @@ export const StepNode = ({
onClose: onModalClose,
} = useDisclosure()
useEffect(() => {
if (query.stepId?.toString() === step.id) setOpenedStepId(step.id)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [query])
useEffect(() => {
setIsConnecting(
connectingIds?.target?.blockId === step.blockId &&
@ -126,6 +131,16 @@ export const StepNode = ({
setIsEditing(false)
}
const handleClick = (e: React.MouseEvent) => {
e.stopPropagation()
setOpenedStepId(step.id)
}
const handleExpandClick = () => {
setOpenedStepId(undefined)
onModalOpen()
}
return isEditing && isTextBubbleStep(step) ? (
<TextEditor
stepId={step.id}
@ -137,11 +152,7 @@ export const StepNode = ({
renderMenu={() => <StepNodeContextMenu stepId={step.id} />}
>
{(ref, isOpened) => (
<Popover
placement="left"
isLazy
defaultIsOpen={query.stepId?.toString() === step.id}
>
<Popover placement="left" isLazy isOpen={openedStepId === step.id}>
<PopoverTrigger>
<Flex
pos="relative"
@ -151,6 +162,7 @@ export const StepNode = ({
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onMouseUp={handleMouseUp}
onClick={handleClick}
data-testid={`step-${step.id}`}
w="full"
>
@ -166,7 +178,11 @@ export const StepNode = ({
align="flex-start"
w="full"
>
<StepIcon type={step.type} mt="1" />
<StepIcon
type={step.type}
mt="1"
data-testid={`${step.id}-icon`}
/>
<StepNodeContent step={step} />
<TargetEndpoint
pos="absolute"
@ -189,7 +205,10 @@ export const StepNode = ({
</Flex>
</PopoverTrigger>
{hasSettingsPopover(step) && (
<SettingsPopoverContent step={step} onExpandClick={onModalOpen} />
<SettingsPopoverContent
step={step}
onExpandClick={handleExpandClick}
/>
)}
{hasContentPopover(step) && <ContentPopover step={step} />}
<SettingsModal isOpen={isModalOpen} onClose={onModalClose}>

View File

@ -7,26 +7,31 @@ export const ConditionNodeContent = ({ step }: { step: ConditionStep }) => {
const { typebot } = useTypebot()
return (
<Flex>
<Stack color={'gray.500'}>
{step.options?.comparisons.allIds.map((comparisonId, idx) => {
const comparison = step.options?.comparisons.byId[comparisonId]
const variable = typebot?.variables.byId[comparison?.variableId ?? '']
return (
<HStack key={comparisonId} spacing={1}>
{idx > 0 && <Text>{step.options?.logicalOperator ?? ''}</Text>}
{variable?.name && (
<Tag bgColor="orange.400">{variable.name}</Tag>
)}
{comparison.comparisonOperator && (
<Text>{comparison?.comparisonOperator}</Text>
)}
{comparison?.value && (
<Tag bgColor={'green.400'}>{comparison.value}</Tag>
)}
</HStack>
)
})}
</Stack>
{step.options?.comparisons.allIds.length === 0 ? (
<Text color={'gray.500'}>Configure...</Text>
) : (
<Stack>
{step.options?.comparisons.allIds.map((comparisonId, idx) => {
const comparison = step.options?.comparisons.byId[comparisonId]
const variable =
typebot?.variables.byId[comparison?.variableId ?? '']
return (
<HStack key={comparisonId} spacing={1}>
{idx > 0 && <Text>{step.options?.logicalOperator ?? ''}</Text>}
{variable?.name && (
<Tag bgColor="orange.400">{variable.name}</Tag>
)}
{comparison.comparisonOperator && (
<Text>{comparison?.comparisonOperator}</Text>
)}
{comparison?.value && (
<Tag bgColor={'green.400'}>{comparison.value}</Tag>
)}
</HStack>
)
})}
</Stack>
)}
<SourceEndpoint
source={{
blockId: step.blockId,

View File

@ -80,11 +80,7 @@ export const StepNodeContent = ({ step }: Props) => {
)
}
case InputStepType.DATE: {
return (
<Text color={'gray.500'}>
{step.options?.labels?.from ?? 'Pick a date...'}
</Text>
)
return <Text color={'gray.500'}>Pick a date...</Text>
}
case InputStepType.PHONE: {
return (

View File

@ -1,6 +1,6 @@
import { useEventListener, Stack, Flex, Portal } from '@chakra-ui/react'
import { DraggableStep, Step, Table } from 'models'
import { useDnd } from 'contexts/DndContext'
import { useStepDnd } from 'contexts/StepDndContext'
import { Coordinates } from 'contexts/GraphContext'
import { useMemo, useState } from 'react'
import { StepNode, StepNodeOverlay } from './StepNode'
@ -20,7 +20,7 @@ export const StepsList = ({
mouseOverBlockId,
setDraggedStepType,
setMouseOverBlockId,
} = useDnd()
} = useStepDnd()
const { createStep } = useTypebot()
const [expandedPlaceholderIndex, setExpandedPlaceholderIndex] = useState<
number | undefined

View File

@ -1,8 +1,8 @@
import { Flex, FlexProps, useEventListener } from '@chakra-ui/react'
import React, { useRef, useMemo } from 'react'
import React, { useRef, useMemo, useEffect } from 'react'
import { blockWidth, useGraph } from 'contexts/GraphContext'
import { BlockNode } from './BlockNode/BlockNode'
import { useDnd } from 'contexts/DndContext'
import { useStepDnd } from 'contexts/StepDndContext'
import { Edges } from './Edges'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import { headerHeight } from 'components/shared/TypebotHeader/TypebotHeader'
@ -10,16 +10,23 @@ import { DraggableStepType } from 'models'
const Graph = ({ ...props }: FlexProps) => {
const { draggedStepType, setDraggedStepType, draggedStep, setDraggedStep } =
useDnd()
useStepDnd()
const graphContainerRef = useRef<HTMLDivElement | null>(null)
const editorContainerRef = useRef<HTMLDivElement | null>(null)
const { createBlock, typebot } = useTypebot()
const { graphPosition, setGraphPosition } = useGraph()
const { graphPosition, setGraphPosition, setOpenedStepId } = useGraph()
const transform = useMemo(
() =>
`translate(${graphPosition.x}px, ${graphPosition.y}px) scale(${graphPosition.scale})`,
[graphPosition]
)
useEffect(() => {
editorContainerRef.current = document.getElementById(
'editor-container'
) as HTMLDivElement
}, [])
const handleMouseWheel = (e: WheelEvent) => {
e.preventDefault()
const isPinchingTrackpad = e.ctrlKey
@ -57,6 +64,9 @@ const Graph = ({ ...props }: FlexProps) => {
}
useEventListener('mousedown', handleMouseDown, undefined, { capture: true })
const handleClick = () => setOpenedStepId(undefined)
useEventListener('click', handleClick, editorContainerRef.current)
if (!typebot) return <></>
return (
<Flex ref={graphContainerRef} {...props}>