📦 Upgrade packages
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import { NextChakraLink } from '../nextChakra/NextChakraLink'
|
||||
import { Text } from '@chakra-ui/react'
|
||||
import { TextLink } from 'components/shared/TextLink'
|
||||
|
||||
type Props = {
|
||||
type: 'register' | 'signin'
|
||||
@ -10,16 +10,11 @@ export const AuthSwitcher = ({ type }: Props) => (
|
||||
{type === 'signin' ? (
|
||||
<Text>
|
||||
Don't have an account?{' '}
|
||||
<NextChakraLink href="/register" color="blue.500" textDecor="underline">
|
||||
Sign up for free
|
||||
</NextChakraLink>
|
||||
<TextLink href="/register">Sign up for free</TextLink>
|
||||
</Text>
|
||||
) : (
|
||||
<Text>
|
||||
Already have an account?{' '}
|
||||
<NextChakraLink href="/signin" color="blue.500" textDecor="underline">
|
||||
Sign in
|
||||
</NextChakraLink>
|
||||
Already have an account? <TextLink href="/signin">Sign in</TextLink>
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
|
@ -19,9 +19,9 @@ import {
|
||||
import { DividerWithText } from './DividerWithText'
|
||||
import { SocialLoginButtons } from './SocialLoginButtons'
|
||||
import { useRouter } from 'next/router'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import { BuiltInProviderType } from 'next-auth/providers'
|
||||
import { useToast } from 'components/shared/hooks/useToast'
|
||||
import { TextLink } from 'components/shared/TextLink'
|
||||
|
||||
type Props = {
|
||||
defaultEmail?: string
|
||||
@ -76,14 +76,12 @@ export const SignInForm = ({
|
||||
return (
|
||||
<Text>
|
||||
You need to{' '}
|
||||
<NextChakraLink
|
||||
<TextLink
|
||||
href="https://docs.typebot.io/self-hosting/configuration"
|
||||
isExternal
|
||||
color="blue.400"
|
||||
textDecor="underline"
|
||||
>
|
||||
configure at least one auth provider
|
||||
</NextChakraLink>{' '}
|
||||
</TextLink>{' '}
|
||||
(Email, Google, GitHub, Facebook or Azure AD).
|
||||
</Text>
|
||||
)
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react'
|
||||
import { TypebotLogo } from 'assets/logos'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import {
|
||||
ChevronLeftIcon,
|
||||
HardDriveIcon,
|
||||
@ -27,6 +26,7 @@ import { EmojiOrImageIcon } from 'components/shared/EmojiOrImageIcon'
|
||||
import { WorkspaceSettingsModal } from './WorkspaceSettingsModal'
|
||||
import { isNotDefined } from 'utils'
|
||||
import { PlanTag } from 'components/shared/PlanTag'
|
||||
import Link from 'next/link'
|
||||
|
||||
export const DashboardHeader = () => {
|
||||
const { user } = useUser()
|
||||
@ -52,13 +52,9 @@ export const DashboardHeader = () => {
|
||||
maxW="1000px"
|
||||
flex="1"
|
||||
>
|
||||
<NextChakraLink
|
||||
className="w-24"
|
||||
href="/typebots"
|
||||
data-testid="typebot-logo"
|
||||
>
|
||||
<Link href="/typebots" data-testid="typebot-logo">
|
||||
<TypebotLogo w="30px" />
|
||||
</NextChakraLink>
|
||||
</Link>
|
||||
<HStack>
|
||||
{user && workspace && (
|
||||
<WorkspaceSettingsModal
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Button } from '@chakra-ui/react'
|
||||
import { ChevronLeftIcon } from 'assets/icons'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import { useTypebotDnd } from 'contexts/TypebotDndContext'
|
||||
import Link from 'next/link'
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
export const BackButton = ({ id }: { id: string | null }) => {
|
||||
@ -17,7 +17,7 @@ export const BackButton = ({ id }: { id: string | null }) => {
|
||||
const handleMouseLeave = () => setMouseOverFolderId(undefined)
|
||||
return (
|
||||
<Button
|
||||
as={NextChakraLink}
|
||||
as={Link}
|
||||
href={id ? `/typebots/folders/${id}` : '/typebots'}
|
||||
leftIcon={<ChevronLeftIcon />}
|
||||
variant={'outline'}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { HStack, Stack, Text } from '@chakra-ui/react'
|
||||
import { StripeClimateLogo } from 'assets/logos/StripeClimateLogo'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import { ChangePlanForm } from 'components/shared/ChangePlanForm'
|
||||
import { TextLink } from 'components/shared/TextLink'
|
||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||
import { Plan } from 'db'
|
||||
import React from 'react'
|
||||
@ -33,13 +33,9 @@ export const BillingContent = () => {
|
||||
<Text fontSize="xs" color="gray.500">
|
||||
Typebot is contributing 1% of your subscription to remove CO₂ from
|
||||
the atmosphere.{' '}
|
||||
<NextChakraLink
|
||||
href="https://climate.stripe.com/5VCRAq"
|
||||
isExternal
|
||||
textDecor="underline"
|
||||
>
|
||||
<TextLink href="https://climate.stripe.com/5VCRAq" isExternal>
|
||||
More info.
|
||||
</NextChakraLink>
|
||||
</TextLink>
|
||||
</Text>
|
||||
</HStack>
|
||||
{workspace.plan !== Plan.CUSTOM &&
|
||||
|
@ -14,8 +14,8 @@ import {
|
||||
Text,
|
||||
} from '@chakra-ui/react'
|
||||
import { DownloadIcon, FileIcon } from 'assets/icons'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import { Workspace } from 'db'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import { useInvoicesQuery } from './queries/useInvoicesQuery'
|
||||
|
||||
@ -54,12 +54,12 @@ export const InvoicesList = ({ workspace }: Props) => {
|
||||
<Td>{getFormattedPrice(invoice.amount, invoice.currency)}</Td>
|
||||
<Td>
|
||||
<IconButton
|
||||
as={NextChakraLink}
|
||||
as={Link}
|
||||
size="xs"
|
||||
icon={<DownloadIcon />}
|
||||
variant="outline"
|
||||
href={invoice.url}
|
||||
isExternal
|
||||
target="_blank"
|
||||
aria-label={'Download invoice'}
|
||||
/>
|
||||
</Td>
|
||||
|
@ -1,47 +0,0 @@
|
||||
import { PropsWithChildren } from 'react'
|
||||
import NextLink from 'next/link'
|
||||
import { LinkProps as NextLinkProps } from 'next/dist/client/link'
|
||||
import {
|
||||
Link as ChakraLink,
|
||||
LinkProps as ChakraLinkProps,
|
||||
} from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
|
||||
export type NextChakraLinkProps = PropsWithChildren<
|
||||
NextLinkProps & Omit<ChakraLinkProps, 'href'>
|
||||
>
|
||||
|
||||
export const NextChakraLink = React.forwardRef<
|
||||
HTMLAnchorElement,
|
||||
NextChakraLinkProps
|
||||
>(
|
||||
(
|
||||
{
|
||||
href,
|
||||
replace,
|
||||
scroll,
|
||||
shallow,
|
||||
prefetch,
|
||||
children,
|
||||
locale,
|
||||
...chakraProps
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
return (
|
||||
<NextLink
|
||||
passHref={true}
|
||||
href={href}
|
||||
replace={replace}
|
||||
scroll={scroll}
|
||||
shallow={shallow}
|
||||
prefetch={prefetch}
|
||||
locale={locale}
|
||||
>
|
||||
<ChakraLink ref={ref} {...chakraProps}>
|
||||
{children}
|
||||
</ChakraLink>
|
||||
</NextLink>
|
||||
)
|
||||
}
|
||||
)
|
@ -248,10 +248,9 @@ const IndeterminateCheckbox = React.forwardRef(
|
||||
const resolvedRef: any = ref || defaultRef
|
||||
|
||||
return (
|
||||
<Flex justify="center" data-testid="checkbox">
|
||||
<Flex justify="center" data-testid="checkbox" {...rest}>
|
||||
<Checkbox
|
||||
ref={resolvedRef}
|
||||
{...rest}
|
||||
isIndeterminate={indeterminate}
|
||||
isChecked={checked}
|
||||
/>
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Stack, HStack, Text } from '@chakra-ui/react'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import { useUser } from 'contexts/UserContext'
|
||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||
import { Plan } from 'db'
|
||||
import { useToast } from '../hooks/useToast'
|
||||
import { TextLink } from '../TextLink'
|
||||
import { ProPlanContent } from './ProPlanContent'
|
||||
import { pay } from './queries/updatePlan'
|
||||
import { useCurrentSubscriptionInfo } from './queries/useCurrentSubscriptionInfo'
|
||||
@ -89,13 +89,9 @@ export const ChangePlanForm = () => {
|
||||
</HStack>
|
||||
<Text color="gray.500">
|
||||
Need custom limits? Specific features?{' '}
|
||||
<NextChakraLink
|
||||
href={'https://typebot.io/enterprise-lead-form'}
|
||||
isExternal
|
||||
textDecor="underline"
|
||||
>
|
||||
<TextLink href={'https://typebot.io/enterprise-lead-form'} isExternal>
|
||||
Let's chat!
|
||||
</NextChakraLink>
|
||||
</TextLink>
|
||||
</Text>
|
||||
</Stack>
|
||||
)
|
||||
|
@ -13,9 +13,9 @@ import {
|
||||
Flex,
|
||||
} from '@chakra-ui/react'
|
||||
import { GoogleLogo } from 'assets/logos'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import { Info } from 'components/shared/Info'
|
||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import { getGoogleSheetsConsentScreenUrl } from 'services/integrations'
|
||||
|
||||
@ -53,7 +53,7 @@ export const GoogleSheetConnectModal = ({
|
||||
/>
|
||||
<Flex>
|
||||
<Button
|
||||
as={NextChakraLink}
|
||||
as={Link}
|
||||
leftIcon={<GoogleLogo />}
|
||||
data-testid="google"
|
||||
isLoading={['loading', 'authenticated'].includes(status)}
|
||||
|
@ -18,12 +18,11 @@ import { CredentialsType, StripeCredentialsData } from 'models'
|
||||
import React, { useState } from 'react'
|
||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||
import { Input } from 'components/shared/Textbox'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import { MoreInfoTooltip } from 'components/shared/MoreInfoTooltip'
|
||||
import { ExternalLinkIcon } from 'assets/icons'
|
||||
import { createCredentials } from 'services/credentials'
|
||||
import { omit } from 'utils'
|
||||
import { useToast } from 'components/shared/hooks/useToast'
|
||||
import { TextLink } from 'components/shared/TextLink'
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean
|
||||
@ -157,13 +156,9 @@ export const StripeConfigModal = ({
|
||||
|
||||
<Text>
|
||||
(You can find your keys{' '}
|
||||
<NextChakraLink
|
||||
href="https://dashboard.stripe.com/apikeys"
|
||||
isExternal
|
||||
textDecor="underline"
|
||||
>
|
||||
here <ExternalLinkIcon />
|
||||
</NextChakraLink>
|
||||
<TextLink href="https://dashboard.stripe.com/apikeys" isExternal>
|
||||
here
|
||||
</TextLink>
|
||||
)
|
||||
</Text>
|
||||
</Stack>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { HStack, IconButton, Input } from '@chakra-ui/react'
|
||||
import { ExternalLinkIcon } from 'assets/icons'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import { useToast } from 'components/shared/hooks/useToast'
|
||||
import { SearchableDropdown } from 'components/shared/SearchableDropdown'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useMemo } from 'react'
|
||||
import { useTypebots } from 'services/typebots'
|
||||
@ -54,7 +54,7 @@ export const TypebotsDropdown = ({
|
||||
<IconButton
|
||||
aria-label="Navigate to typebot"
|
||||
icon={<ExternalLinkIcon />}
|
||||
as={NextChakraLink}
|
||||
as={Link}
|
||||
href={`/typebots/${currentTypebot?.id}/edit?parentId=${query.typebotId}`}
|
||||
/>
|
||||
)}
|
||||
|
37
apps/builder/components/shared/TextLink.tsx
Normal file
37
apps/builder/components/shared/TextLink.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import Link, { LinkProps } from 'next/link'
|
||||
import React from 'react'
|
||||
import { chakra, HStack, TextProps } from '@chakra-ui/react'
|
||||
import { ExternalLinkIcon } from 'assets/icons'
|
||||
|
||||
type TextLinkProps = LinkProps & TextProps & { isExternal?: boolean }
|
||||
|
||||
export const TextLink = ({
|
||||
children,
|
||||
href,
|
||||
shallow,
|
||||
replace,
|
||||
scroll,
|
||||
prefetch,
|
||||
isExternal,
|
||||
...textProps
|
||||
}: TextLinkProps) => (
|
||||
<Link
|
||||
href={href}
|
||||
shallow={shallow}
|
||||
replace={replace}
|
||||
scroll={scroll}
|
||||
prefetch={prefetch}
|
||||
target={isExternal ? '_blank' : undefined}
|
||||
>
|
||||
<chakra.span textDecor="underline" display="inline-block" {...textProps}>
|
||||
{isExternal ? (
|
||||
<HStack spacing={1}>
|
||||
<chakra.span>{children}</chakra.span>
|
||||
<ExternalLinkIcon />
|
||||
</HStack>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</chakra.span>
|
||||
</Link>
|
||||
)
|
@ -8,7 +8,6 @@ import {
|
||||
Text,
|
||||
} from '@chakra-ui/react'
|
||||
import { BuoyIcon, ChevronLeftIcon, RedoIcon, UndoIcon } from 'assets/icons'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import { RightPanel, useEditor } from 'contexts/EditorContext'
|
||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||
import { useRouter } from 'next/router'
|
||||
@ -20,6 +19,7 @@ import { CollaborationMenuButton } from './CollaborationMenuButton'
|
||||
import { EditableTypebotName } from './EditableTypebotName'
|
||||
import { getBubbleActions } from 'typebot-js'
|
||||
import { isCloudProdInstance } from 'services/utils'
|
||||
import Link from 'next/link'
|
||||
|
||||
export const headerHeight = 56
|
||||
|
||||
@ -71,7 +71,7 @@ export const TypebotHeader = () => {
|
||||
right={{ base: 280, xl: 0 }}
|
||||
>
|
||||
<Button
|
||||
as={NextChakraLink}
|
||||
as={Link}
|
||||
href={`/typebots/${typebot?.id}/edit`}
|
||||
colorScheme={router.pathname.includes('/edit') ? 'blue' : 'gray'}
|
||||
variant={router.pathname.includes('/edit') ? 'outline' : 'ghost'}
|
||||
@ -80,7 +80,7 @@ export const TypebotHeader = () => {
|
||||
Flow
|
||||
</Button>
|
||||
<Button
|
||||
as={NextChakraLink}
|
||||
as={Link}
|
||||
href={`/typebots/${typebot?.id}/theme`}
|
||||
colorScheme={router.pathname.endsWith('theme') ? 'blue' : 'gray'}
|
||||
variant={router.pathname.endsWith('theme') ? 'outline' : 'ghost'}
|
||||
@ -89,7 +89,7 @@ export const TypebotHeader = () => {
|
||||
Theme
|
||||
</Button>
|
||||
<Button
|
||||
as={NextChakraLink}
|
||||
as={Link}
|
||||
href={`/typebots/${typebot?.id}/settings`}
|
||||
colorScheme={router.pathname.endsWith('settings') ? 'blue' : 'gray'}
|
||||
variant={router.pathname.endsWith('settings') ? 'outline' : 'ghost'}
|
||||
@ -98,7 +98,7 @@ export const TypebotHeader = () => {
|
||||
Settings
|
||||
</Button>
|
||||
<Button
|
||||
as={NextChakraLink}
|
||||
as={Link}
|
||||
href={`/typebots/${typebot?.id}/share`}
|
||||
colorScheme={router.pathname.endsWith('share') ? 'blue' : 'gray'}
|
||||
variant={router.pathname.endsWith('share') ? 'outline' : 'ghost'}
|
||||
@ -108,7 +108,7 @@ export const TypebotHeader = () => {
|
||||
</Button>
|
||||
{typebot?.publishedTypebotId && (
|
||||
<Button
|
||||
as={NextChakraLink}
|
||||
as={Link}
|
||||
href={`/typebots/${typebot?.id}/results`}
|
||||
colorScheme={router.pathname.includes('results') ? 'blue' : 'gray'}
|
||||
variant={router.pathname.includes('results') ? 'outline' : 'ghost'}
|
||||
@ -127,7 +127,7 @@ export const TypebotHeader = () => {
|
||||
>
|
||||
<HStack alignItems="center" spacing={3}>
|
||||
<IconButton
|
||||
as={NextChakraLink}
|
||||
as={Link}
|
||||
aria-label="Navigate back"
|
||||
icon={<ChevronLeftIcon fontSize={25} />}
|
||||
href={
|
||||
|
@ -8,59 +8,59 @@
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"test": "pnpm playwright test",
|
||||
"test:open": "NO_RETRIES=1 pnpm playwright test --debug"
|
||||
"test:report": "pnpm playwright show-report"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/css-reset": "2.0.8",
|
||||
"@chakra-ui/react": "2.3.5",
|
||||
"@codemirror/lang-css": "6.0.0",
|
||||
"@codemirror/lang-html": "6.1.2",
|
||||
"@codemirror/lang-javascript": "6.1.0",
|
||||
"@codemirror/lang-json": "6.0.0",
|
||||
"@chakra-ui/react": "2.3.6",
|
||||
"@codemirror/lang-css": "6.0.1",
|
||||
"@codemirror/lang-html": "6.1.3",
|
||||
"@codemirror/lang-javascript": "6.1.1",
|
||||
"@codemirror/lang-json": "6.0.1",
|
||||
"@codemirror/lint": "6.0.0",
|
||||
"@codemirror/state": "6.1.2",
|
||||
"@dnd-kit/core": "6.0.5",
|
||||
"@dnd-kit/sortable": "7.0.1",
|
||||
"@dnd-kit/utilities": "3.2.0",
|
||||
"@emotion/react": "11.10.4",
|
||||
"@emotion/styled": "11.10.4",
|
||||
"@emotion/react": "11.10.5",
|
||||
"@emotion/styled": "11.10.5",
|
||||
"@giphy/js-fetch-api": "4.4.0",
|
||||
"@giphy/js-types": "4.2.1",
|
||||
"@giphy/react-components": "6.2.0",
|
||||
"@googleapis/drive": "3.0.1",
|
||||
"@sentry/nextjs": "7.15.0",
|
||||
"@stripe/stripe-js": "1.41.0",
|
||||
"@tanstack/react-table": "8.5.15",
|
||||
"@udecode/plate-basic-marks": "18.2.0",
|
||||
"@googleapis/drive": "4.0.0",
|
||||
"@sentry/nextjs": "7.17.3",
|
||||
"@stripe/stripe-js": "1.42.1",
|
||||
"@tanstack/react-table": "8.5.22",
|
||||
"@udecode/plate-basic-marks": "18.7.0",
|
||||
"@udecode/plate-common": "^7.0.2",
|
||||
"@udecode/plate-core": "18.2.0",
|
||||
"@udecode/plate-link": "18.2.0",
|
||||
"@udecode/plate-serializer-html": "18.2.0",
|
||||
"@udecode/plate-ui-link": "18.2.0",
|
||||
"@udecode/plate-ui-toolbar": "18.2.0",
|
||||
"aws-sdk": "2.1233.0",
|
||||
"@udecode/plate-core": "18.7.0",
|
||||
"@udecode/plate-link": "18.7.0",
|
||||
"@udecode/plate-serializer-html": "18.7.0",
|
||||
"@udecode/plate-ui-link": "18.7.0",
|
||||
"@udecode/plate-ui-toolbar": "18.7.0",
|
||||
"aws-sdk": "2.1245.0",
|
||||
"bot-engine": "workspace:*",
|
||||
"browser-image-compression": "2.0.0",
|
||||
"canvas-confetti": "1.5.1",
|
||||
"canvas-confetti": "1.6.0",
|
||||
"codemirror": "6.0.1",
|
||||
"cuid": "2.1.8",
|
||||
"deep-object-diff": "1.1.7",
|
||||
"dequal": "2.0.3",
|
||||
"emojilib": "3.0.7",
|
||||
"focus-visible": "5.2.0",
|
||||
"framer-motion": "7.5.3",
|
||||
"google-auth-library": "8.5.2",
|
||||
"framer-motion": "7.6.4",
|
||||
"google-auth-library": "8.6.0",
|
||||
"google-spreadsheet": "3.3.0",
|
||||
"got": "12.5.2",
|
||||
"htmlparser2": "8.0.1",
|
||||
"immer": "9.0.15",
|
||||
"immer": "9.0.16",
|
||||
"js-video-url-parser": "0.5.1",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"kbar": "0.1.0-beta.36",
|
||||
"micro": "9.4.1",
|
||||
"micro-cors": "0.1.1",
|
||||
"minio": "7.0.32",
|
||||
"next": "12.3.1",
|
||||
"next": "13.0.1",
|
||||
"next-auth": "4.12.3",
|
||||
"nodemailer": "6.8.0",
|
||||
"nprogress": "0.2.0",
|
||||
@ -74,7 +74,7 @@
|
||||
"slate-history": "0.66.0",
|
||||
"slate-hyperscript": "0.77.0",
|
||||
"slate-react": "0.83.2",
|
||||
"stripe": "10.14.0",
|
||||
"stripe": "10.15.0",
|
||||
"styled-components": "5.3.6",
|
||||
"svg-round-corners": "0.4.1",
|
||||
"swr": "1.3.0",
|
||||
@ -85,7 +85,7 @@
|
||||
"@faire/mjml-react": "2.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.19.3",
|
||||
"@babel/core": "7.19.6",
|
||||
"@chakra-ui/styled-system": "2.3.4",
|
||||
"@playwright/test": "1.27.1",
|
||||
"@types/canvas-confetti": "1.4.3",
|
||||
@ -93,24 +93,25 @@
|
||||
"@types/jsonwebtoken": "8.5.9",
|
||||
"@types/micro-cors": "0.1.2",
|
||||
"@types/minio": "7.0.14",
|
||||
"@types/node": "18.11.0",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/nodemailer": "6.4.6",
|
||||
"@types/nprogress": "0.2.0",
|
||||
"@types/papaparse": "5.3.5",
|
||||
"@types/prettier": "2.7.1",
|
||||
"@types/qs": "6.9.7",
|
||||
"@types/react": "18.0.21",
|
||||
"@types/react": "18.0.24",
|
||||
"@types/tinycolor2": "1.4.3",
|
||||
"@typescript-eslint/eslint-plugin": "5.40.0",
|
||||
"@typescript-eslint/parser": "5.40.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.42.0",
|
||||
"@typescript-eslint/parser": "5.42.0",
|
||||
"db": "workspace:*",
|
||||
"dotenv": "16.0.3",
|
||||
"eslint": "8.25.0",
|
||||
"eslint-config-next": "12.3.1",
|
||||
"eslint": "8.26.0",
|
||||
"eslint-config-next": "13.0.1",
|
||||
"eslint-plugin-react": "7.31.10",
|
||||
"models": "workspace:*",
|
||||
"next-transpile-modules": "9.1.0",
|
||||
"next-transpile-modules": "10.0.0",
|
||||
"typescript": "4.8.4",
|
||||
"utils": "workspace:*"
|
||||
"utils": "workspace:*",
|
||||
"configs": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import { Seo } from 'components/Seo'
|
||||
import { TypebotHeader } from 'components/shared/TypebotHeader'
|
||||
import React, { useMemo } from 'react'
|
||||
import { HStack, Button, Tag, Flex, Text } from '@chakra-ui/react'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import { ResultsContent } from 'components/results/ResultsContent'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||
@ -14,6 +13,7 @@ import { ResultsProvider } from 'contexts/ResultsProvider'
|
||||
import { UnlockPlanInfo } from 'components/shared/Info'
|
||||
import { getChatsLimit, getStorageLimit } from 'utils'
|
||||
import { useUsage } from 'components/dashboard/WorkspaceSettingsModal/BillingContent/UsageContent/useUsage'
|
||||
import Link from 'next/link'
|
||||
|
||||
const ALERT_CHATS_PERCENT_THRESHOLD = 80
|
||||
const ALERT_STORAGE_PERCENT_THRESHOLD = 80
|
||||
@ -130,7 +130,7 @@ const ResultsPage = () => {
|
||||
>
|
||||
<HStack maxW="1600px" w="full" px="4">
|
||||
<Button
|
||||
as={NextChakraLink}
|
||||
as={Link}
|
||||
colorScheme={!isAnalytics ? 'blue' : 'gray'}
|
||||
variant={!isAnalytics ? 'outline' : 'ghost'}
|
||||
size="sm"
|
||||
@ -144,7 +144,7 @@ const ResultsPage = () => {
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
as={NextChakraLink}
|
||||
as={Link}
|
||||
colorScheme={isAnalytics ? 'blue' : 'gray'}
|
||||
variant={isAnalytics ? 'outline' : 'ghost'}
|
||||
href={`/typebots/${typebot?.id}/results/analytics`}
|
||||
|
@ -1,35 +1,22 @@
|
||||
import { devices, PlaywrightTestConfig } from '@playwright/test'
|
||||
import { PlaywrightTestConfig } from '@playwright/test'
|
||||
import path from 'path'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require('dotenv').config({
|
||||
path: path.join(__dirname, 'playwright/.env'),
|
||||
})
|
||||
import { playwrightBaseConfig } from 'configs/playwright'
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
globalSetup: require.resolve(path.join(__dirname, 'playwright/global-setup')),
|
||||
...playwrightBaseConfig,
|
||||
testDir: path.join(__dirname, 'playwright/tests'),
|
||||
retries: process.env.NO_RETRIES ? 0 : 2,
|
||||
workers: process.env.CI ? 1 : 3,
|
||||
reporter: 'html',
|
||||
maxFailures: process.env.CI ? 10 : undefined,
|
||||
webServer: process.env.CI
|
||||
? {
|
||||
...(playwrightBaseConfig.webServer as { command: string }),
|
||||
port: 3000,
|
||||
}
|
||||
: undefined,
|
||||
use: {
|
||||
actionTimeout: 0,
|
||||
baseURL: process.env.PLAYWRIGHT_BUILDER_TEST_BASE_URL,
|
||||
trace: 'on-first-retry',
|
||||
...playwrightBaseConfig.use,
|
||||
baseURL: process.env.NEXTAUTH_URL,
|
||||
storageState: path.join(__dirname, 'playwright/firstUser.json'),
|
||||
video: 'retain-on-failure',
|
||||
locale: 'en-US',
|
||||
},
|
||||
outputDir: path.join(__dirname, 'playwright/test-results/'),
|
||||
projects: [
|
||||
{
|
||||
name: 'Chrome',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
viewport: { width: 1400, height: 1000 },
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default config
|
||||
|
@ -119,12 +119,12 @@
|
||||
"graphCoordinates": { "x": 639, "y": 142 }
|
||||
},
|
||||
{
|
||||
"id": "q5dAhqSTCaNdiGSJm9B9Rw",
|
||||
"id": "group1",
|
||||
"blocks": [
|
||||
{
|
||||
"id": "sgtE2Sy7cKykac9B223Kq9R",
|
||||
"type": "text",
|
||||
"groupId": "q5dAhqSTCaNdiGSJm9B9Rw",
|
||||
"groupId": "group1",
|
||||
"content": {
|
||||
"html": "<div>What's your name?</div>",
|
||||
"richText": [
|
||||
@ -134,9 +134,9 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "sqEsMo747LTDnY9FjQcEwUv",
|
||||
"id": "block1",
|
||||
"type": "text input",
|
||||
"groupId": "q5dAhqSTCaNdiGSJm9B9Rw",
|
||||
"groupId": "group1",
|
||||
"options": {
|
||||
"isLong": false,
|
||||
"labels": {
|
||||
|
@ -1,14 +0,0 @@
|
||||
import { FullConfig } from '@playwright/test'
|
||||
import { setupDatabase, teardownDatabase } from 'utils/playwright/databaseSetup'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require('dotenv').config({ path: '.env' })
|
||||
|
||||
async function globalSetup(config: FullConfig) {
|
||||
const { baseURL } = config.projects[0].use
|
||||
if (!baseURL) throw new Error('baseURL is missing')
|
||||
await teardownDatabase()
|
||||
await setupDatabase()
|
||||
}
|
||||
|
||||
export default globalSetup
|
@ -10,7 +10,7 @@ import { proWorkspaceId } from 'utils/playwright/databaseSetup'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
const stripe = new Stripe(process.env.STRIPE_TEST_SECRET_KEY ?? '', {
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY ?? '', {
|
||||
apiVersion: '2022-08-01',
|
||||
})
|
||||
|
||||
|
@ -21,7 +21,7 @@ test('should display user info properly', async ({ page }) => {
|
||||
await expect(page.locator('img >> nth=1')).toHaveAttribute(
|
||||
'src',
|
||||
new RegExp(
|
||||
`http://localhost:9000/typebot/public/users/${userId}/avatar`,
|
||||
`${process.env.S3_ENDPOINT}/${process.env.S3_BUCKET}/public/users/${userId}/avatar`,
|
||||
'gm'
|
||||
)
|
||||
)
|
||||
|
@ -1,7 +1,10 @@
|
||||
import test, { expect } from '@playwright/test'
|
||||
import cuid from 'cuid'
|
||||
import path from 'path'
|
||||
import { importTypebotInDatabase } from 'utils/playwright/databaseActions'
|
||||
import {
|
||||
importTypebotInDatabase,
|
||||
injectFakeResults,
|
||||
} from 'utils/playwright/databaseActions'
|
||||
import { starterWorkspaceId } from 'utils/playwright/databaseSetup'
|
||||
|
||||
test('analytics are not available for non-pro workspaces', async ({ page }) => {
|
||||
@ -13,6 +16,7 @@ test('analytics are not available for non-pro workspaces', async ({ page }) => {
|
||||
workspaceId: starterWorkspaceId,
|
||||
}
|
||||
)
|
||||
await injectFakeResults({ typebotId, count: 10 })
|
||||
await page.goto(`/typebots/${typebotId}/results/analytics`)
|
||||
const firstDropoffBox = page.locator('text="%" >> nth=0')
|
||||
await firstDropoffBox.hover()
|
||||
|
@ -227,7 +227,7 @@ test('should display invoices', async ({ page }) => {
|
||||
await page.click('text=Settings & Members')
|
||||
await page.click('text=Billing & Usage')
|
||||
await expect(page.locator('text="Invoices"')).toBeVisible()
|
||||
await expect(page.locator('tr')).toHaveCount(2)
|
||||
await expect(page.locator('tr')).toHaveCount(3)
|
||||
await expect(page.locator('text="€39.00"')).toBeVisible()
|
||||
})
|
||||
|
||||
|
@ -33,7 +33,7 @@ test.describe.parallel('Image bubble block', () => {
|
||||
await expect(page.locator('img')).toHaveAttribute(
|
||||
'src',
|
||||
new RegExp(
|
||||
`http://localhost:9000/typebot/public/typebots/${typebotId}/avatar.jpg`,
|
||||
`${process.env.S3_ENDPOINT}/${process.env.S3_BUCKET}/public/typebots/${typebotId}/avatar.jpg`,
|
||||
'gm'
|
||||
)
|
||||
)
|
||||
|
@ -26,19 +26,19 @@ test.describe('Payment input block', () => {
|
||||
await page.fill('[placeholder="Typebot"]', 'My Stripe Account')
|
||||
await page.fill(
|
||||
'[placeholder="sk_test_..."]',
|
||||
process.env.STRIPE_TEST_SECRET_KEY ?? ''
|
||||
process.env.STRIPE_SECRET_KEY ?? ''
|
||||
)
|
||||
await page.fill(
|
||||
'[placeholder="sk_live_..."]',
|
||||
process.env.STRIPE_TEST_SECRET_KEY ?? ''
|
||||
process.env.STRIPE_SECRET_KEY ?? ''
|
||||
)
|
||||
await page.fill(
|
||||
'[placeholder="pk_test_..."]',
|
||||
process.env.STRIPE_TEST_PUBLIC_KEY ?? ''
|
||||
process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY ?? ''
|
||||
)
|
||||
await page.fill(
|
||||
'[placeholder="pk_live_..."]',
|
||||
process.env.STRIPE_TEST_PUBLIC_KEY ?? ''
|
||||
process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY ?? ''
|
||||
)
|
||||
await expect(page.locator('button >> text="Connect"')).toBeEnabled()
|
||||
await page.click('button >> text="Connect"')
|
||||
@ -66,6 +66,9 @@ test.describe('Payment input block', () => {
|
||||
await stripePaymentForm(page)
|
||||
.locator(`[placeholder="1234 1234 1234 1234"]`)
|
||||
.fill('4242424242424242')
|
||||
const zipInput = stripePaymentForm(page).getByPlaceholder('90210')
|
||||
const isZipInputVisible = await zipInput.isVisible()
|
||||
if (isZipInputVisible) await zipInput.fill('12345')
|
||||
await typebotViewer(page).locator(`text="Pay 30€"`).click()
|
||||
await expect(typebotViewer(page).locator(`text="Success"`)).toBeVisible()
|
||||
})
|
||||
|
@ -11,10 +11,9 @@ test.describe('Send email block', () => {
|
||||
if (
|
||||
!process.env.SMTP_USERNAME ||
|
||||
!process.env.SMTP_PORT ||
|
||||
!process.env.SMTP_SECURE ||
|
||||
!process.env.SMTP_HOST ||
|
||||
!process.env.SMTP_PASSWORD ||
|
||||
!process.env.SMTP_FROM
|
||||
!process.env.NEXT_PUBLIC_SMTP_FROM
|
||||
)
|
||||
throw new Error('SMTP_ env vars are missing')
|
||||
await importTypebotInDatabase(
|
||||
@ -29,7 +28,7 @@ test.describe('Send email block', () => {
|
||||
|
||||
await page.goto(`/typebots/${typebotId}/edit`)
|
||||
await page.click('text=Configure...')
|
||||
await page.click(`text=${process.env.SMTP_FROM}`)
|
||||
await page.click(`text=notifications@typebot.io`)
|
||||
await page.click('text=Connect new')
|
||||
const createButton = page.locator('button >> text=Create')
|
||||
await expect(createButton).toBeDisabled()
|
||||
@ -44,7 +43,6 @@ test.describe('Send email block', () => {
|
||||
process.env.SMTP_USERNAME
|
||||
)
|
||||
await page.fill('[type="password"]', process.env.SMTP_PASSWORD)
|
||||
if (process.env.SMTP_SECURE === 'true') await page.click('text=Use TLS?')
|
||||
await page.fill('input[role="spinbutton"]', process.env.SMTP_PORT)
|
||||
await expect(createButton).toBeEnabled()
|
||||
await createButton.click()
|
||||
|
@ -24,7 +24,7 @@ test.describe('Webhook block', () => {
|
||||
await page.click('text=Configure...')
|
||||
await page.fill(
|
||||
'input[placeholder="Paste webhook URL..."]',
|
||||
`${process.env.PLAYWRIGHT_BUILDER_TEST_BASE_URL}/api/mock/webhook-easy-config`
|
||||
`${process.env.NEXTAUTH_URL}/api/mock/webhook-easy-config`
|
||||
)
|
||||
await page.click('text=Test the request')
|
||||
await expect(page.locator('div[role="textbox"] >> nth=-1')).toContainText(
|
||||
@ -46,7 +46,7 @@ test.describe('Webhook block', () => {
|
||||
await page.click('text=Configure...')
|
||||
await page.fill(
|
||||
'input[placeholder="Paste webhook URL..."]',
|
||||
`${process.env.PLAYWRIGHT_BUILDER_TEST_BASE_URL}/api/mock/webhook`
|
||||
`${process.env.NEXTAUTH_URL}/api/mock/webhook`
|
||||
)
|
||||
await page.click('text=Advanced configuration')
|
||||
await page.click('text=GET')
|
||||
|
@ -1,184 +1,174 @@
|
||||
import test, { expect, Page } from '@playwright/test'
|
||||
import cuid from 'cuid'
|
||||
import { readFileSync } from 'fs'
|
||||
import { defaultTextInputOptions, InputBlockType } from 'models'
|
||||
import { parse } from 'papaparse'
|
||||
import path from 'path'
|
||||
import {
|
||||
importTypebotInDatabase,
|
||||
injectFakeResults,
|
||||
createTypebots,
|
||||
} from 'utils/playwright/databaseActions'
|
||||
import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
|
||||
import { deleteButtonInConfirmDialog } from '../services/selectorUtils'
|
||||
|
||||
const typebotId = cuid()
|
||||
|
||||
test('Submission table header should be parsed correctly', async ({ page }) => {
|
||||
const typebotId = cuid()
|
||||
test.beforeEach(async () => {
|
||||
await importTypebotInDatabase(
|
||||
path.join(__dirname, '../fixtures/typebots/results/submissionHeader.json'),
|
||||
{
|
||||
id: typebotId,
|
||||
}
|
||||
)
|
||||
await page.goto(`/typebots/${typebotId}/results`)
|
||||
await expect(page.locator('text=Submitted at')).toBeVisible()
|
||||
await expect(page.locator('text=Welcome')).toBeVisible()
|
||||
await expect(page.locator('text=Email')).toBeVisible()
|
||||
await expect(page.locator('text=Name')).toBeVisible()
|
||||
await expect(page.locator('text=Services')).toBeVisible()
|
||||
await expect(page.locator('text=Additional information')).toBeVisible()
|
||||
await expect(page.locator('text=utm_source')).toBeVisible()
|
||||
await expect(page.locator('text=utm_userid')).toBeVisible()
|
||||
})
|
||||
|
||||
test('results should be deletable', async ({ page }) => {
|
||||
await createTypebots([
|
||||
{
|
||||
id: typebotId,
|
||||
...parseDefaultGroupWithBlock({
|
||||
type: InputBlockType.TEXT,
|
||||
options: defaultTextInputOptions,
|
||||
}),
|
||||
},
|
||||
])
|
||||
await injectFakeResults({ typebotId, count: 200, isChronological: true })
|
||||
await page.goto(`/typebots/${typebotId}/results`)
|
||||
await expect(page.locator('text=content199')).toBeVisible()
|
||||
await page.click('[data-testid="checkbox"] >> nth=1')
|
||||
await page.click('[data-testid="checkbox"] >> nth=2')
|
||||
await page.click('text="Delete"')
|
||||
await deleteButtonInConfirmDialog(page).click()
|
||||
await expect(page.locator('text=content199')).toBeHidden()
|
||||
await expect(page.locator('text=content198')).toBeHidden()
|
||||
await page.waitForTimeout(1000)
|
||||
await page.click('[data-testid="checkbox"] >> nth=0')
|
||||
await page.click('text="Delete"')
|
||||
await deleteButtonInConfirmDialog(page).click()
|
||||
await page.waitForTimeout(1000)
|
||||
expect(await page.locator('tr').count()).toBe(1)
|
||||
await expect(page.locator('text="Delete"')).toBeHidden()
|
||||
})
|
||||
|
||||
test('submissions table should have infinite scroll', async ({ page }) => {
|
||||
const scrollToBottom = () =>
|
||||
page.evaluate(() => {
|
||||
const tableWrapper = document.querySelector('.table-wrapper')
|
||||
if (!tableWrapper) return
|
||||
tableWrapper.scrollTo(0, tableWrapper.scrollHeight)
|
||||
})
|
||||
|
||||
await injectFakeResults({ typebotId, count: 200, isChronological: true })
|
||||
await page.goto(`/typebots/${typebotId}/results`)
|
||||
await expect(page.locator('text=content199')).toBeVisible()
|
||||
|
||||
await expect(page.locator('text=content149')).toBeHidden()
|
||||
await scrollToBottom()
|
||||
await expect(page.locator('text=content149')).toBeVisible()
|
||||
|
||||
await expect(page.locator('text=content99')).toBeHidden()
|
||||
await scrollToBottom()
|
||||
await expect(page.locator('text=content99')).toBeVisible()
|
||||
|
||||
await expect(page.locator('text=content49')).toBeHidden()
|
||||
await scrollToBottom()
|
||||
await expect(page.locator('text=content49')).toBeVisible()
|
||||
await expect(page.locator('text=content0')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should correctly export selection in CSV', async ({ page }) => {
|
||||
await page.goto(`/typebots/${typebotId}/results`)
|
||||
await expect(page.locator('text=content199')).toBeVisible()
|
||||
await page.click('[data-testid="checkbox"] >> nth=1')
|
||||
await page.click('[data-testid="checkbox"] >> nth=2')
|
||||
const [download] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.locator('text="Export"').click(),
|
||||
])
|
||||
const path = await download.path()
|
||||
expect(path).toBeDefined()
|
||||
const file = readFileSync(path as string).toString()
|
||||
const { data } = parse(file)
|
||||
validateExportSelection(data)
|
||||
|
||||
await page.click('[data-testid="checkbox"] >> nth=0')
|
||||
const [downloadAll] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.locator('text="Export"').click(),
|
||||
])
|
||||
const pathAll = await downloadAll.path()
|
||||
expect(pathAll).toBeDefined()
|
||||
const fileAll = readFileSync(pathAll as string).toString()
|
||||
const { data: dataAll } = parse(fileAll)
|
||||
validateExportAll(dataAll)
|
||||
})
|
||||
|
||||
test('Can resize, hide and reorder columns', async ({ page }) => {
|
||||
const typebotId = cuid()
|
||||
await importTypebotInDatabase(
|
||||
path.join(__dirname, '../fixtures/typebots/results/submissionHeader.json'),
|
||||
{
|
||||
id: typebotId,
|
||||
}
|
||||
)
|
||||
test('Results', async ({ page }) => {
|
||||
await page.goto(`/typebots/${typebotId}/results`)
|
||||
|
||||
// Resize
|
||||
expect((await page.locator('th >> nth=4').boundingBox())?.width).toBe(200)
|
||||
await page.waitForTimeout(500)
|
||||
await page.dragAndDrop(
|
||||
'[data-testid="resize-handle"] >> nth=3',
|
||||
'[data-testid="resize-handle"] >> nth=3',
|
||||
{ targetPosition: { x: 150, y: 0 }, force: true }
|
||||
)
|
||||
await page.waitForTimeout(500)
|
||||
expect((await page.locator('th >> nth=4').boundingBox())?.width).toBe(345)
|
||||
await test.step('Check header format', async () => {
|
||||
await expect(page.locator('text=Submitted at')).toBeVisible()
|
||||
await expect(page.locator('text=Welcome')).toBeVisible()
|
||||
await expect(page.locator('text=Email')).toBeVisible()
|
||||
await expect(page.locator('text=Name')).toBeVisible()
|
||||
await expect(page.locator('text=Services')).toBeVisible()
|
||||
await expect(page.locator('text=Additional information')).toBeVisible()
|
||||
await expect(page.locator('text=utm_source')).toBeVisible()
|
||||
await expect(page.locator('text=utm_userid')).toBeVisible()
|
||||
})
|
||||
|
||||
// Hide
|
||||
await expect(
|
||||
page.locator('[data-testid="Submitted at header"]')
|
||||
).toBeVisible()
|
||||
await expect(page.locator('[data-testid="Email header"]')).toBeVisible()
|
||||
await page.click('button >> text="Columns"')
|
||||
await page.click('[aria-label="Hide column"] >> nth=0')
|
||||
await page.click('[aria-label="Hide column"] >> nth=1')
|
||||
await expect(page.locator('[data-testid="Submitted at header"]')).toBeHidden()
|
||||
await expect(page.locator('[data-testid="Email header"]')).toBeHidden()
|
||||
await test.step('Resize columns', async () => {
|
||||
expect((await page.locator('th >> nth=4').boundingBox())?.width).toBe(200)
|
||||
await page.waitForTimeout(500)
|
||||
await page.dragAndDrop(
|
||||
'[data-testid="resize-handle"] >> nth=3',
|
||||
'[data-testid="resize-handle"] >> nth=3',
|
||||
{ targetPosition: { x: 150, y: 0 }, force: true }
|
||||
)
|
||||
await page.waitForTimeout(500)
|
||||
expect((await page.locator('th >> nth=4').boundingBox())?.width).toBe(345)
|
||||
})
|
||||
|
||||
// Reorder
|
||||
await expect(page.locator('th >> nth=1')).toHaveText('Welcome')
|
||||
await expect(page.locator('th >> nth=2')).toHaveText('Name')
|
||||
await page.dragAndDrop(
|
||||
'[aria-label="Drag"] >> nth=0',
|
||||
'[aria-label="Drag"] >> nth=0',
|
||||
{ targetPosition: { x: 0, y: 80 }, force: true }
|
||||
)
|
||||
await expect(page.locator('th >> nth=1')).toHaveText('Name')
|
||||
await expect(page.locator('th >> nth=2')).toHaveText('Welcome')
|
||||
await test.step('Hide columns', async () => {
|
||||
await expect(
|
||||
page.locator('[data-testid="Submitted at header"]')
|
||||
).toBeVisible()
|
||||
await expect(page.locator('[data-testid="Email header"]')).toBeVisible()
|
||||
await page.click('button >> text="Columns"')
|
||||
await page.click('[aria-label="Hide column"] >> nth=0')
|
||||
await page.click('[aria-label="Hide column"] >> nth=1')
|
||||
await expect(
|
||||
page.locator('[data-testid="Submitted at header"]')
|
||||
).toBeHidden()
|
||||
await expect(page.locator('[data-testid="Email header"]')).toBeHidden()
|
||||
})
|
||||
|
||||
// Preferences should be persisted
|
||||
const saveAndReload = async (page: Page) => {
|
||||
await page.click('text="Theme"')
|
||||
await page.waitForTimeout(2000)
|
||||
await page.goto(`/typebots/${typebotId}/results`)
|
||||
}
|
||||
await saveAndReload(page)
|
||||
expect((await page.locator('th >> nth=1').boundingBox())?.width).toBe(345)
|
||||
await expect(page.locator('[data-testid="Submitted at header"]')).toBeHidden()
|
||||
await expect(page.locator('[data-testid="Email header"]')).toBeHidden()
|
||||
await expect(page.locator('th >> nth=1')).toHaveText('Name')
|
||||
await expect(page.locator('th >> nth=2')).toHaveText('Welcome')
|
||||
await test.step('Reorder columns', async () => {
|
||||
await expect(page.locator('th >> nth=1')).toHaveText('Welcome')
|
||||
await expect(page.locator('th >> nth=2')).toHaveText('Name')
|
||||
await page.dragAndDrop(
|
||||
'[aria-label="Drag"] >> nth=0',
|
||||
'[aria-label="Drag"] >> nth=0',
|
||||
{ targetPosition: { x: 0, y: 80 }, force: true }
|
||||
)
|
||||
await expect(page.locator('th >> nth=1')).toHaveText('Name')
|
||||
await expect(page.locator('th >> nth=2')).toHaveText('Welcome')
|
||||
})
|
||||
|
||||
await test.step('Preferences should be persisted', async () => {
|
||||
await saveAndReload(page)
|
||||
expect((await page.locator('th >> nth=1').boundingBox())?.width).toBe(345)
|
||||
await expect(
|
||||
page.locator('[data-testid="Submitted at header"]')
|
||||
).toBeHidden()
|
||||
await expect(page.locator('[data-testid="Email header"]')).toBeHidden()
|
||||
await expect(page.locator('th >> nth=1')).toHaveText('Name')
|
||||
await expect(page.locator('th >> nth=2')).toHaveText('Welcome')
|
||||
})
|
||||
|
||||
await test.step('Infinite scroll', async () => {
|
||||
await expect(page.locator('text=content199')).toBeVisible()
|
||||
|
||||
await expect(page.locator('text=content149')).toBeHidden()
|
||||
await scrollToBottom(page)
|
||||
await expect(page.locator('text=content149')).toBeVisible()
|
||||
|
||||
await expect(page.locator('text=content99')).toBeHidden()
|
||||
await scrollToBottom(page)
|
||||
await expect(page.locator('text=content99')).toBeVisible()
|
||||
|
||||
await expect(page.locator('text=content49')).toBeHidden()
|
||||
await scrollToBottom(page)
|
||||
await expect(page.locator('text=content49')).toBeVisible()
|
||||
await expect(page.locator('text=content0')).toBeVisible()
|
||||
})
|
||||
|
||||
await test.step('Export', async () => {
|
||||
// For some reason, we need to double click on checkboxes to check them
|
||||
await getNthCheckbox(page, 1).dblclick()
|
||||
await getNthCheckbox(page, 2).dblclick()
|
||||
const [download] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.getByRole('button', { name: 'Export 2' }).click(),
|
||||
])
|
||||
const path = await download.path()
|
||||
expect(path).toBeDefined()
|
||||
const file = readFileSync(path as string).toString()
|
||||
const { data } = parse(file)
|
||||
validateExportSelection(data)
|
||||
|
||||
await getNthCheckbox(page, 0).click()
|
||||
const [downloadAll] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.getByRole('button', { name: 'Export 200' }).click(),
|
||||
])
|
||||
const pathAll = await downloadAll.path()
|
||||
expect(pathAll).toBeDefined()
|
||||
const fileAll = readFileSync(pathAll as string).toString()
|
||||
const { data: dataAll } = parse(fileAll)
|
||||
validateExportAll(dataAll)
|
||||
await getNthCheckbox(page, 0).click()
|
||||
})
|
||||
|
||||
await test.step('Delete', async () => {
|
||||
await getNthCheckbox(page, 1).click()
|
||||
await getNthCheckbox(page, 2).click()
|
||||
await page.click('text="Delete"')
|
||||
await deleteButtonInConfirmDialog(page).click()
|
||||
await expect(page.locator('text=content199')).toBeHidden()
|
||||
await expect(page.locator('text=content198')).toBeHidden()
|
||||
await page.waitForTimeout(1000)
|
||||
await page.click('[data-testid="checkbox"] >> nth=0')
|
||||
await page.click('text="Delete"')
|
||||
await deleteButtonInConfirmDialog(page).click()
|
||||
await page.waitForTimeout(1000)
|
||||
expect(await page.locator('tr').count()).toBe(1)
|
||||
await expect(page.locator('text="Delete"')).toBeHidden()
|
||||
})
|
||||
})
|
||||
|
||||
const validateExportSelection = (data: unknown[]) => {
|
||||
expect(data).toHaveLength(3)
|
||||
expect((data[1] as unknown[])[1]).toBe('content199')
|
||||
expect((data[2] as unknown[])[1]).toBe('content198')
|
||||
expect((data[1] as unknown[])[3]).toBe('content199')
|
||||
expect((data[2] as unknown[])[3]).toBe('content198')
|
||||
}
|
||||
|
||||
const validateExportAll = (data: unknown[]) => {
|
||||
expect(data).toHaveLength(201)
|
||||
expect((data[1] as unknown[])[1]).toBe('content199')
|
||||
expect((data[200] as unknown[])[1]).toBe('content0')
|
||||
expect((data[1] as unknown[])[3]).toBe('content199')
|
||||
expect((data[200] as unknown[])[3]).toBe('content0')
|
||||
}
|
||||
|
||||
const scrollToBottom = (page: Page) =>
|
||||
page.evaluate(() => {
|
||||
const tableWrapper = document.querySelector('.table-wrapper')
|
||||
if (!tableWrapper) return
|
||||
tableWrapper.scrollTo(0, tableWrapper.scrollHeight)
|
||||
})
|
||||
|
||||
const saveAndReload = async (page: Page) => {
|
||||
await page.click('text="Theme"')
|
||||
await page.waitForTimeout(2000)
|
||||
await page.goto(`/typebots/${typebotId}/results`)
|
||||
}
|
||||
|
||||
const getNthCheckbox = (page: Page, n: number) =>
|
||||
page.getByTestId('checkbox').nth(n)
|
||||
|
@ -6,17 +6,19 @@ import { importTypebotInDatabase } from 'utils/playwright/databaseActions'
|
||||
import { freeWorkspaceId } from 'utils/playwright/databaseSetup'
|
||||
import { typebotViewer } from 'utils/playwright/testHelpers'
|
||||
|
||||
const typebotId = cuid()
|
||||
|
||||
test.describe.parallel('Settings page', () => {
|
||||
test.beforeAll(async () => {
|
||||
await importTypebotInDatabase(
|
||||
path.join(__dirname, '../fixtures/typebots/settings.json'),
|
||||
{
|
||||
id: typebotId,
|
||||
}
|
||||
)
|
||||
})
|
||||
test.describe('General', () => {
|
||||
test('should reflect change in real-time', async ({ page }) => {
|
||||
const typebotId = cuid()
|
||||
await importTypebotInDatabase(
|
||||
path.join(__dirname, '../fixtures/typebots/settings.json'),
|
||||
{
|
||||
id: typebotId,
|
||||
}
|
||||
)
|
||||
|
||||
await page.goto(`/typebots/${typebotId}/settings`)
|
||||
await expect(
|
||||
typebotViewer(page).locator('a:has-text("Made with Typebot")')
|
||||
@ -134,7 +136,6 @@ test.describe.parallel('Settings page', () => {
|
||||
await expect(
|
||||
typebotViewer(page).locator('text="What\'s your name?"')
|
||||
).toBeVisible()
|
||||
await page.click('button:has-text("General")')
|
||||
await expect(
|
||||
page.locator('[data-testid="starter-lock-tag"]')
|
||||
).toBeVisible()
|
||||
|
@ -91,7 +91,7 @@ test('can manage members', async ({ page }) => {
|
||||
await page.goto('/typebots')
|
||||
await page.click('text=Settings & Members')
|
||||
await page.click('text="Members"')
|
||||
await expect(page.locator('text="user@email.com"')).toBeVisible()
|
||||
await expect(page.locator('text="user@email.com"').nth(1)).toBeVisible()
|
||||
await expect(page.locator('button >> text="Invite"')).toBeEnabled()
|
||||
await page.fill(
|
||||
'input[placeholder="colleague@company.com"]',
|
||||
|
Reference in New Issue
Block a user