📦 Upgrade packages
This commit is contained in:
13
.github/workflows/auto-move-project-card.yml
vendored
13
.github/workflows/auto-move-project-card.yml
vendored
@ -1,13 +0,0 @@
|
|||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, edited, ready_for_review, synchronize]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
move-linked-issues:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Move linked issues to In Review column
|
|
||||||
steps:
|
|
||||||
- uses: rharter/github-move-linked-project-cards@v0.1.0
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
column: Reviewing 👀
|
|
41
.github/workflows/playwright.yml
vendored
41
.github/workflows/playwright.yml
vendored
@ -1,41 +0,0 @@
|
|||||||
name: Playwright Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
deployment_status:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test-builder:
|
|
||||||
timeout-minutes: 60
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.event.deployment_status.state == 'success' && contains(github.event.deployment_status.environment, 'builder')}}
|
|
||||||
steps:
|
|
||||||
- name: Log Info
|
|
||||||
run: echo ${{ github.event.deployment_status.target_url }} && echo ${{ github.event.deployment }} && echo ${{ github.event.deployment_status.environment }}
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: pnpm/action-setup@v2.2.2
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm i --frozen-lockfile
|
|
||||||
- name: Build dependencies
|
|
||||||
run: pnpm turbo run build --filter="builder^..."
|
|
||||||
- name: Install Playwright
|
|
||||||
run: npx playwright install --with-deps
|
|
||||||
- name: Run tests
|
|
||||||
working-directory: ./apps/builder
|
|
||||||
run: pnpm test
|
|
||||||
env:
|
|
||||||
PLAYWRIGHT_BUILDER_TEST_BASE_URL: ${{ github.event.deployment_status.target_url }}
|
|
||||||
DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
|
|
||||||
SMTP_HOST: smtp.ethereal.email
|
|
||||||
SMTP_PORT: 587
|
|
||||||
SMTP_SECURE: false
|
|
||||||
SMTP_USERNAME: tobin.tillman65@ethereal.email
|
|
||||||
SMTP_PASSWORD: Ty9BcwCBrK6w8AG2hx
|
|
||||||
STRIPE_TEST_PUBLIC_KEY: pk_test_fYwf5fyL66hiRIkFVcpStukl00K1MTPPmG
|
|
||||||
STRIPE_TEST_SECRET_KEY: ${{ secrets.STRIPE_TEST_SECRET_KEY }}
|
|
||||||
ENCRYPTION_SECRET: ${{ secrets.ENCRYPTION_SECRET }}
|
|
||||||
- name: Upload test results
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: playwright-results
|
|
||||||
path: ./apps/builder/playwright-report
|
|
56
.github/workflows/publish_docker_images.yml
vendored
56
.github/workflows/publish_docker_images.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
name: Publish Docker images
|
name: Build Docker images
|
||||||
|
|
||||||
on:
|
on:
|
||||||
create:
|
create:
|
||||||
@ -9,32 +9,34 @@ on:
|
|||||||
branches: [main]
|
branches: [main]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push_builder:
|
build:
|
||||||
name: Builder
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
app: ['builder', 'viewer']
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Extract Builder meta
|
- name: Extract existing image metadata
|
||||||
id: builder-meta
|
id: ${{ matrix.app }}-meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: baptistearno/typebot-builder
|
images: baptistearno/typebot-${{ matrix.app }}
|
||||||
tags: |
|
tags: |
|
||||||
type=ref,event=branch
|
type=ref,event=branch
|
||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
type=semver,pattern={{major}}
|
type=semver,pattern={{major}}
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
- if: github.event_name != 'pull_request'
|
||||||
if: github.event_name != 'pull_request'
|
name: Log in to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
- name: Build and push builder image
|
- name: Build image
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
@ -42,38 +44,4 @@ jobs:
|
|||||||
tags: ${{ steps.builder-meta.outputs.tags }}
|
tags: ${{ steps.builder-meta.outputs.tags }}
|
||||||
labels: ${{ steps.builder-meta.outputs.labels }}
|
labels: ${{ steps.builder-meta.outputs.labels }}
|
||||||
build-args: |
|
build-args: |
|
||||||
SCOPE=builder
|
SCOPE=${{ matrix.app }}
|
||||||
push_viewer:
|
|
||||||
name: Viewer
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out the repo
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Extract Viewer meta
|
|
||||||
id: viewer-meta
|
|
||||||
uses: docker/metadata-action@v4
|
|
||||||
with:
|
|
||||||
images: baptistearno/typebot-viewer
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=semver,pattern={{major}}
|
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Build and push viewer image
|
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.viewer-meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.viewer-meta.outputs.labels }}
|
|
||||||
build-args: |
|
|
||||||
SCOPE=viewer
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { NextChakraLink } from '../nextChakra/NextChakraLink'
|
|
||||||
import { Text } from '@chakra-ui/react'
|
import { Text } from '@chakra-ui/react'
|
||||||
|
import { TextLink } from 'components/shared/TextLink'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
type: 'register' | 'signin'
|
type: 'register' | 'signin'
|
||||||
@ -10,16 +10,11 @@ export const AuthSwitcher = ({ type }: Props) => (
|
|||||||
{type === 'signin' ? (
|
{type === 'signin' ? (
|
||||||
<Text>
|
<Text>
|
||||||
Don't have an account?{' '}
|
Don't have an account?{' '}
|
||||||
<NextChakraLink href="/register" color="blue.500" textDecor="underline">
|
<TextLink href="/register">Sign up for free</TextLink>
|
||||||
Sign up for free
|
|
||||||
</NextChakraLink>
|
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<Text>
|
<Text>
|
||||||
Already have an account?{' '}
|
Already have an account? <TextLink href="/signin">Sign in</TextLink>
|
||||||
<NextChakraLink href="/signin" color="blue.500" textDecor="underline">
|
|
||||||
Sign in
|
|
||||||
</NextChakraLink>
|
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -19,9 +19,9 @@ import {
|
|||||||
import { DividerWithText } from './DividerWithText'
|
import { DividerWithText } from './DividerWithText'
|
||||||
import { SocialLoginButtons } from './SocialLoginButtons'
|
import { SocialLoginButtons } from './SocialLoginButtons'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
|
||||||
import { BuiltInProviderType } from 'next-auth/providers'
|
import { BuiltInProviderType } from 'next-auth/providers'
|
||||||
import { useToast } from 'components/shared/hooks/useToast'
|
import { useToast } from 'components/shared/hooks/useToast'
|
||||||
|
import { TextLink } from 'components/shared/TextLink'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
defaultEmail?: string
|
defaultEmail?: string
|
||||||
@ -76,14 +76,12 @@ export const SignInForm = ({
|
|||||||
return (
|
return (
|
||||||
<Text>
|
<Text>
|
||||||
You need to{' '}
|
You need to{' '}
|
||||||
<NextChakraLink
|
<TextLink
|
||||||
href="https://docs.typebot.io/self-hosting/configuration"
|
href="https://docs.typebot.io/self-hosting/configuration"
|
||||||
isExternal
|
isExternal
|
||||||
color="blue.400"
|
|
||||||
textDecor="underline"
|
|
||||||
>
|
>
|
||||||
configure at least one auth provider
|
configure at least one auth provider
|
||||||
</NextChakraLink>{' '}
|
</TextLink>{' '}
|
||||||
(Email, Google, GitHub, Facebook or Azure AD).
|
(Email, Google, GitHub, Facebook or Azure AD).
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
|
@ -12,7 +12,6 @@ import {
|
|||||||
useDisclosure,
|
useDisclosure,
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
import { TypebotLogo } from 'assets/logos'
|
import { TypebotLogo } from 'assets/logos'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
|
||||||
import {
|
import {
|
||||||
ChevronLeftIcon,
|
ChevronLeftIcon,
|
||||||
HardDriveIcon,
|
HardDriveIcon,
|
||||||
@ -27,6 +26,7 @@ import { EmojiOrImageIcon } from 'components/shared/EmojiOrImageIcon'
|
|||||||
import { WorkspaceSettingsModal } from './WorkspaceSettingsModal'
|
import { WorkspaceSettingsModal } from './WorkspaceSettingsModal'
|
||||||
import { isNotDefined } from 'utils'
|
import { isNotDefined } from 'utils'
|
||||||
import { PlanTag } from 'components/shared/PlanTag'
|
import { PlanTag } from 'components/shared/PlanTag'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
export const DashboardHeader = () => {
|
export const DashboardHeader = () => {
|
||||||
const { user } = useUser()
|
const { user } = useUser()
|
||||||
@ -52,13 +52,9 @@ export const DashboardHeader = () => {
|
|||||||
maxW="1000px"
|
maxW="1000px"
|
||||||
flex="1"
|
flex="1"
|
||||||
>
|
>
|
||||||
<NextChakraLink
|
<Link href="/typebots" data-testid="typebot-logo">
|
||||||
className="w-24"
|
|
||||||
href="/typebots"
|
|
||||||
data-testid="typebot-logo"
|
|
||||||
>
|
|
||||||
<TypebotLogo w="30px" />
|
<TypebotLogo w="30px" />
|
||||||
</NextChakraLink>
|
</Link>
|
||||||
<HStack>
|
<HStack>
|
||||||
{user && workspace && (
|
{user && workspace && (
|
||||||
<WorkspaceSettingsModal
|
<WorkspaceSettingsModal
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Button } from '@chakra-ui/react'
|
import { Button } from '@chakra-ui/react'
|
||||||
import { ChevronLeftIcon } from 'assets/icons'
|
import { ChevronLeftIcon } from 'assets/icons'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
|
||||||
import { useTypebotDnd } from 'contexts/TypebotDndContext'
|
import { useTypebotDnd } from 'contexts/TypebotDndContext'
|
||||||
|
import Link from 'next/link'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
|
|
||||||
export const BackButton = ({ id }: { id: string | null }) => {
|
export const BackButton = ({ id }: { id: string | null }) => {
|
||||||
@ -17,7 +17,7 @@ export const BackButton = ({ id }: { id: string | null }) => {
|
|||||||
const handleMouseLeave = () => setMouseOverFolderId(undefined)
|
const handleMouseLeave = () => setMouseOverFolderId(undefined)
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
as={NextChakraLink}
|
as={Link}
|
||||||
href={id ? `/typebots/folders/${id}` : '/typebots'}
|
href={id ? `/typebots/folders/${id}` : '/typebots'}
|
||||||
leftIcon={<ChevronLeftIcon />}
|
leftIcon={<ChevronLeftIcon />}
|
||||||
variant={'outline'}
|
variant={'outline'}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { HStack, Stack, Text } from '@chakra-ui/react'
|
import { HStack, Stack, Text } from '@chakra-ui/react'
|
||||||
import { StripeClimateLogo } from 'assets/logos/StripeClimateLogo'
|
import { StripeClimateLogo } from 'assets/logos/StripeClimateLogo'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
|
||||||
import { ChangePlanForm } from 'components/shared/ChangePlanForm'
|
import { ChangePlanForm } from 'components/shared/ChangePlanForm'
|
||||||
|
import { TextLink } from 'components/shared/TextLink'
|
||||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||||
import { Plan } from 'db'
|
import { Plan } from 'db'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
@ -33,13 +33,9 @@ export const BillingContent = () => {
|
|||||||
<Text fontSize="xs" color="gray.500">
|
<Text fontSize="xs" color="gray.500">
|
||||||
Typebot is contributing 1% of your subscription to remove CO₂ from
|
Typebot is contributing 1% of your subscription to remove CO₂ from
|
||||||
the atmosphere.{' '}
|
the atmosphere.{' '}
|
||||||
<NextChakraLink
|
<TextLink href="https://climate.stripe.com/5VCRAq" isExternal>
|
||||||
href="https://climate.stripe.com/5VCRAq"
|
|
||||||
isExternal
|
|
||||||
textDecor="underline"
|
|
||||||
>
|
|
||||||
More info.
|
More info.
|
||||||
</NextChakraLink>
|
</TextLink>
|
||||||
</Text>
|
</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
{workspace.plan !== Plan.CUSTOM &&
|
{workspace.plan !== Plan.CUSTOM &&
|
||||||
|
@ -14,8 +14,8 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
import { DownloadIcon, FileIcon } from 'assets/icons'
|
import { DownloadIcon, FileIcon } from 'assets/icons'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
|
||||||
import { Workspace } from 'db'
|
import { Workspace } from 'db'
|
||||||
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useInvoicesQuery } from './queries/useInvoicesQuery'
|
import { useInvoicesQuery } from './queries/useInvoicesQuery'
|
||||||
|
|
||||||
@ -54,12 +54,12 @@ export const InvoicesList = ({ workspace }: Props) => {
|
|||||||
<Td>{getFormattedPrice(invoice.amount, invoice.currency)}</Td>
|
<Td>{getFormattedPrice(invoice.amount, invoice.currency)}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<IconButton
|
<IconButton
|
||||||
as={NextChakraLink}
|
as={Link}
|
||||||
size="xs"
|
size="xs"
|
||||||
icon={<DownloadIcon />}
|
icon={<DownloadIcon />}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
href={invoice.url}
|
href={invoice.url}
|
||||||
isExternal
|
target="_blank"
|
||||||
aria-label={'Download invoice'}
|
aria-label={'Download invoice'}
|
||||||
/>
|
/>
|
||||||
</Td>
|
</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
|
const resolvedRef: any = ref || defaultRef
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex justify="center" data-testid="checkbox">
|
<Flex justify="center" data-testid="checkbox" {...rest}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
ref={resolvedRef}
|
ref={resolvedRef}
|
||||||
{...rest}
|
|
||||||
isIndeterminate={indeterminate}
|
isIndeterminate={indeterminate}
|
||||||
isChecked={checked}
|
isChecked={checked}
|
||||||
/>
|
/>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Stack, HStack, Text } from '@chakra-ui/react'
|
import { Stack, HStack, Text } from '@chakra-ui/react'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
|
||||||
import { useUser } from 'contexts/UserContext'
|
import { useUser } from 'contexts/UserContext'
|
||||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||||
import { Plan } from 'db'
|
import { Plan } from 'db'
|
||||||
import { useToast } from '../hooks/useToast'
|
import { useToast } from '../hooks/useToast'
|
||||||
|
import { TextLink } from '../TextLink'
|
||||||
import { ProPlanContent } from './ProPlanContent'
|
import { ProPlanContent } from './ProPlanContent'
|
||||||
import { pay } from './queries/updatePlan'
|
import { pay } from './queries/updatePlan'
|
||||||
import { useCurrentSubscriptionInfo } from './queries/useCurrentSubscriptionInfo'
|
import { useCurrentSubscriptionInfo } from './queries/useCurrentSubscriptionInfo'
|
||||||
@ -89,13 +89,9 @@ export const ChangePlanForm = () => {
|
|||||||
</HStack>
|
</HStack>
|
||||||
<Text color="gray.500">
|
<Text color="gray.500">
|
||||||
Need custom limits? Specific features?{' '}
|
Need custom limits? Specific features?{' '}
|
||||||
<NextChakraLink
|
<TextLink href={'https://typebot.io/enterprise-lead-form'} isExternal>
|
||||||
href={'https://typebot.io/enterprise-lead-form'}
|
|
||||||
isExternal
|
|
||||||
textDecor="underline"
|
|
||||||
>
|
|
||||||
Let's chat!
|
Let's chat!
|
||||||
</NextChakraLink>
|
</TextLink>
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
|
@ -13,9 +13,9 @@ import {
|
|||||||
Flex,
|
Flex,
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
import { GoogleLogo } from 'assets/logos'
|
import { GoogleLogo } from 'assets/logos'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
|
||||||
import { Info } from 'components/shared/Info'
|
import { Info } from 'components/shared/Info'
|
||||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||||
|
import Link from 'next/link'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { getGoogleSheetsConsentScreenUrl } from 'services/integrations'
|
import { getGoogleSheetsConsentScreenUrl } from 'services/integrations'
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ export const GoogleSheetConnectModal = ({
|
|||||||
/>
|
/>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Button
|
<Button
|
||||||
as={NextChakraLink}
|
as={Link}
|
||||||
leftIcon={<GoogleLogo />}
|
leftIcon={<GoogleLogo />}
|
||||||
data-testid="google"
|
data-testid="google"
|
||||||
isLoading={['loading', 'authenticated'].includes(status)}
|
isLoading={['loading', 'authenticated'].includes(status)}
|
||||||
|
@ -18,12 +18,11 @@ import { CredentialsType, StripeCredentialsData } from 'models'
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||||
import { Input } from 'components/shared/Textbox'
|
import { Input } from 'components/shared/Textbox'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
|
||||||
import { MoreInfoTooltip } from 'components/shared/MoreInfoTooltip'
|
import { MoreInfoTooltip } from 'components/shared/MoreInfoTooltip'
|
||||||
import { ExternalLinkIcon } from 'assets/icons'
|
|
||||||
import { createCredentials } from 'services/credentials'
|
import { createCredentials } from 'services/credentials'
|
||||||
import { omit } from 'utils'
|
import { omit } from 'utils'
|
||||||
import { useToast } from 'components/shared/hooks/useToast'
|
import { useToast } from 'components/shared/hooks/useToast'
|
||||||
|
import { TextLink } from 'components/shared/TextLink'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
@ -157,13 +156,9 @@ export const StripeConfigModal = ({
|
|||||||
|
|
||||||
<Text>
|
<Text>
|
||||||
(You can find your keys{' '}
|
(You can find your keys{' '}
|
||||||
<NextChakraLink
|
<TextLink href="https://dashboard.stripe.com/apikeys" isExternal>
|
||||||
href="https://dashboard.stripe.com/apikeys"
|
here
|
||||||
isExternal
|
</TextLink>
|
||||||
textDecor="underline"
|
|
||||||
>
|
|
||||||
here <ExternalLinkIcon />
|
|
||||||
</NextChakraLink>
|
|
||||||
)
|
)
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { HStack, IconButton, Input } from '@chakra-ui/react'
|
import { HStack, IconButton, Input } from '@chakra-ui/react'
|
||||||
import { ExternalLinkIcon } from 'assets/icons'
|
import { ExternalLinkIcon } from 'assets/icons'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
|
||||||
import { useToast } from 'components/shared/hooks/useToast'
|
import { useToast } from 'components/shared/hooks/useToast'
|
||||||
import { SearchableDropdown } from 'components/shared/SearchableDropdown'
|
import { SearchableDropdown } from 'components/shared/SearchableDropdown'
|
||||||
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { useTypebots } from 'services/typebots'
|
import { useTypebots } from 'services/typebots'
|
||||||
@ -54,7 +54,7 @@ export const TypebotsDropdown = ({
|
|||||||
<IconButton
|
<IconButton
|
||||||
aria-label="Navigate to typebot"
|
aria-label="Navigate to typebot"
|
||||||
icon={<ExternalLinkIcon />}
|
icon={<ExternalLinkIcon />}
|
||||||
as={NextChakraLink}
|
as={Link}
|
||||||
href={`/typebots/${currentTypebot?.id}/edit?parentId=${query.typebotId}`}
|
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,
|
Text,
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
import { BuoyIcon, ChevronLeftIcon, RedoIcon, UndoIcon } from 'assets/icons'
|
import { BuoyIcon, ChevronLeftIcon, RedoIcon, UndoIcon } from 'assets/icons'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
|
||||||
import { RightPanel, useEditor } from 'contexts/EditorContext'
|
import { RightPanel, useEditor } from 'contexts/EditorContext'
|
||||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
@ -20,6 +19,7 @@ import { CollaborationMenuButton } from './CollaborationMenuButton'
|
|||||||
import { EditableTypebotName } from './EditableTypebotName'
|
import { EditableTypebotName } from './EditableTypebotName'
|
||||||
import { getBubbleActions } from 'typebot-js'
|
import { getBubbleActions } from 'typebot-js'
|
||||||
import { isCloudProdInstance } from 'services/utils'
|
import { isCloudProdInstance } from 'services/utils'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
export const headerHeight = 56
|
export const headerHeight = 56
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ export const TypebotHeader = () => {
|
|||||||
right={{ base: 280, xl: 0 }}
|
right={{ base: 280, xl: 0 }}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
as={NextChakraLink}
|
as={Link}
|
||||||
href={`/typebots/${typebot?.id}/edit`}
|
href={`/typebots/${typebot?.id}/edit`}
|
||||||
colorScheme={router.pathname.includes('/edit') ? 'blue' : 'gray'}
|
colorScheme={router.pathname.includes('/edit') ? 'blue' : 'gray'}
|
||||||
variant={router.pathname.includes('/edit') ? 'outline' : 'ghost'}
|
variant={router.pathname.includes('/edit') ? 'outline' : 'ghost'}
|
||||||
@ -80,7 +80,7 @@ export const TypebotHeader = () => {
|
|||||||
Flow
|
Flow
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
as={NextChakraLink}
|
as={Link}
|
||||||
href={`/typebots/${typebot?.id}/theme`}
|
href={`/typebots/${typebot?.id}/theme`}
|
||||||
colorScheme={router.pathname.endsWith('theme') ? 'blue' : 'gray'}
|
colorScheme={router.pathname.endsWith('theme') ? 'blue' : 'gray'}
|
||||||
variant={router.pathname.endsWith('theme') ? 'outline' : 'ghost'}
|
variant={router.pathname.endsWith('theme') ? 'outline' : 'ghost'}
|
||||||
@ -89,7 +89,7 @@ export const TypebotHeader = () => {
|
|||||||
Theme
|
Theme
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
as={NextChakraLink}
|
as={Link}
|
||||||
href={`/typebots/${typebot?.id}/settings`}
|
href={`/typebots/${typebot?.id}/settings`}
|
||||||
colorScheme={router.pathname.endsWith('settings') ? 'blue' : 'gray'}
|
colorScheme={router.pathname.endsWith('settings') ? 'blue' : 'gray'}
|
||||||
variant={router.pathname.endsWith('settings') ? 'outline' : 'ghost'}
|
variant={router.pathname.endsWith('settings') ? 'outline' : 'ghost'}
|
||||||
@ -98,7 +98,7 @@ export const TypebotHeader = () => {
|
|||||||
Settings
|
Settings
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
as={NextChakraLink}
|
as={Link}
|
||||||
href={`/typebots/${typebot?.id}/share`}
|
href={`/typebots/${typebot?.id}/share`}
|
||||||
colorScheme={router.pathname.endsWith('share') ? 'blue' : 'gray'}
|
colorScheme={router.pathname.endsWith('share') ? 'blue' : 'gray'}
|
||||||
variant={router.pathname.endsWith('share') ? 'outline' : 'ghost'}
|
variant={router.pathname.endsWith('share') ? 'outline' : 'ghost'}
|
||||||
@ -108,7 +108,7 @@ export const TypebotHeader = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
{typebot?.publishedTypebotId && (
|
{typebot?.publishedTypebotId && (
|
||||||
<Button
|
<Button
|
||||||
as={NextChakraLink}
|
as={Link}
|
||||||
href={`/typebots/${typebot?.id}/results`}
|
href={`/typebots/${typebot?.id}/results`}
|
||||||
colorScheme={router.pathname.includes('results') ? 'blue' : 'gray'}
|
colorScheme={router.pathname.includes('results') ? 'blue' : 'gray'}
|
||||||
variant={router.pathname.includes('results') ? 'outline' : 'ghost'}
|
variant={router.pathname.includes('results') ? 'outline' : 'ghost'}
|
||||||
@ -127,7 +127,7 @@ export const TypebotHeader = () => {
|
|||||||
>
|
>
|
||||||
<HStack alignItems="center" spacing={3}>
|
<HStack alignItems="center" spacing={3}>
|
||||||
<IconButton
|
<IconButton
|
||||||
as={NextChakraLink}
|
as={Link}
|
||||||
aria-label="Navigate back"
|
aria-label="Navigate back"
|
||||||
icon={<ChevronLeftIcon fontSize={25} />}
|
icon={<ChevronLeftIcon fontSize={25} />}
|
||||||
href={
|
href={
|
||||||
|
@ -8,59 +8,59 @@
|
|||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"test": "pnpm playwright test",
|
"test": "pnpm playwright test",
|
||||||
"test:open": "NO_RETRIES=1 pnpm playwright test --debug"
|
"test:report": "pnpm playwright show-report"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/css-reset": "2.0.8",
|
"@chakra-ui/css-reset": "2.0.8",
|
||||||
"@chakra-ui/react": "2.3.5",
|
"@chakra-ui/react": "2.3.6",
|
||||||
"@codemirror/lang-css": "6.0.0",
|
"@codemirror/lang-css": "6.0.1",
|
||||||
"@codemirror/lang-html": "6.1.2",
|
"@codemirror/lang-html": "6.1.3",
|
||||||
"@codemirror/lang-javascript": "6.1.0",
|
"@codemirror/lang-javascript": "6.1.1",
|
||||||
"@codemirror/lang-json": "6.0.0",
|
"@codemirror/lang-json": "6.0.1",
|
||||||
"@codemirror/lint": "6.0.0",
|
"@codemirror/lint": "6.0.0",
|
||||||
"@codemirror/state": "6.1.2",
|
"@codemirror/state": "6.1.2",
|
||||||
"@dnd-kit/core": "6.0.5",
|
"@dnd-kit/core": "6.0.5",
|
||||||
"@dnd-kit/sortable": "7.0.1",
|
"@dnd-kit/sortable": "7.0.1",
|
||||||
"@dnd-kit/utilities": "3.2.0",
|
"@dnd-kit/utilities": "3.2.0",
|
||||||
"@emotion/react": "11.10.4",
|
"@emotion/react": "11.10.5",
|
||||||
"@emotion/styled": "11.10.4",
|
"@emotion/styled": "11.10.5",
|
||||||
"@giphy/js-fetch-api": "4.4.0",
|
"@giphy/js-fetch-api": "4.4.0",
|
||||||
"@giphy/js-types": "4.2.1",
|
"@giphy/js-types": "4.2.1",
|
||||||
"@giphy/react-components": "6.2.0",
|
"@giphy/react-components": "6.2.0",
|
||||||
"@googleapis/drive": "3.0.1",
|
"@googleapis/drive": "4.0.0",
|
||||||
"@sentry/nextjs": "7.15.0",
|
"@sentry/nextjs": "7.17.3",
|
||||||
"@stripe/stripe-js": "1.41.0",
|
"@stripe/stripe-js": "1.42.1",
|
||||||
"@tanstack/react-table": "8.5.15",
|
"@tanstack/react-table": "8.5.22",
|
||||||
"@udecode/plate-basic-marks": "18.2.0",
|
"@udecode/plate-basic-marks": "18.7.0",
|
||||||
"@udecode/plate-common": "^7.0.2",
|
"@udecode/plate-common": "^7.0.2",
|
||||||
"@udecode/plate-core": "18.2.0",
|
"@udecode/plate-core": "18.7.0",
|
||||||
"@udecode/plate-link": "18.2.0",
|
"@udecode/plate-link": "18.7.0",
|
||||||
"@udecode/plate-serializer-html": "18.2.0",
|
"@udecode/plate-serializer-html": "18.7.0",
|
||||||
"@udecode/plate-ui-link": "18.2.0",
|
"@udecode/plate-ui-link": "18.7.0",
|
||||||
"@udecode/plate-ui-toolbar": "18.2.0",
|
"@udecode/plate-ui-toolbar": "18.7.0",
|
||||||
"aws-sdk": "2.1233.0",
|
"aws-sdk": "2.1245.0",
|
||||||
"bot-engine": "workspace:*",
|
"bot-engine": "workspace:*",
|
||||||
"browser-image-compression": "2.0.0",
|
"browser-image-compression": "2.0.0",
|
||||||
"canvas-confetti": "1.5.1",
|
"canvas-confetti": "1.6.0",
|
||||||
"codemirror": "6.0.1",
|
"codemirror": "6.0.1",
|
||||||
"cuid": "2.1.8",
|
"cuid": "2.1.8",
|
||||||
"deep-object-diff": "1.1.7",
|
"deep-object-diff": "1.1.7",
|
||||||
"dequal": "2.0.3",
|
"dequal": "2.0.3",
|
||||||
"emojilib": "3.0.7",
|
"emojilib": "3.0.7",
|
||||||
"focus-visible": "5.2.0",
|
"focus-visible": "5.2.0",
|
||||||
"framer-motion": "7.5.3",
|
"framer-motion": "7.6.4",
|
||||||
"google-auth-library": "8.5.2",
|
"google-auth-library": "8.6.0",
|
||||||
"google-spreadsheet": "3.3.0",
|
"google-spreadsheet": "3.3.0",
|
||||||
"got": "12.5.2",
|
"got": "12.5.2",
|
||||||
"htmlparser2": "8.0.1",
|
"htmlparser2": "8.0.1",
|
||||||
"immer": "9.0.15",
|
"immer": "9.0.16",
|
||||||
"js-video-url-parser": "0.5.1",
|
"js-video-url-parser": "0.5.1",
|
||||||
"jsonwebtoken": "8.5.1",
|
"jsonwebtoken": "8.5.1",
|
||||||
"kbar": "0.1.0-beta.36",
|
"kbar": "0.1.0-beta.36",
|
||||||
"micro": "9.4.1",
|
"micro": "9.4.1",
|
||||||
"micro-cors": "0.1.1",
|
"micro-cors": "0.1.1",
|
||||||
"minio": "7.0.32",
|
"minio": "7.0.32",
|
||||||
"next": "12.3.1",
|
"next": "13.0.1",
|
||||||
"next-auth": "4.12.3",
|
"next-auth": "4.12.3",
|
||||||
"nodemailer": "6.8.0",
|
"nodemailer": "6.8.0",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
@ -74,7 +74,7 @@
|
|||||||
"slate-history": "0.66.0",
|
"slate-history": "0.66.0",
|
||||||
"slate-hyperscript": "0.77.0",
|
"slate-hyperscript": "0.77.0",
|
||||||
"slate-react": "0.83.2",
|
"slate-react": "0.83.2",
|
||||||
"stripe": "10.14.0",
|
"stripe": "10.15.0",
|
||||||
"styled-components": "5.3.6",
|
"styled-components": "5.3.6",
|
||||||
"svg-round-corners": "0.4.1",
|
"svg-round-corners": "0.4.1",
|
||||||
"swr": "1.3.0",
|
"swr": "1.3.0",
|
||||||
@ -85,7 +85,7 @@
|
|||||||
"@faire/mjml-react": "2.1.4"
|
"@faire/mjml-react": "2.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.19.3",
|
"@babel/core": "7.19.6",
|
||||||
"@chakra-ui/styled-system": "2.3.4",
|
"@chakra-ui/styled-system": "2.3.4",
|
||||||
"@playwright/test": "1.27.1",
|
"@playwright/test": "1.27.1",
|
||||||
"@types/canvas-confetti": "1.4.3",
|
"@types/canvas-confetti": "1.4.3",
|
||||||
@ -93,24 +93,25 @@
|
|||||||
"@types/jsonwebtoken": "8.5.9",
|
"@types/jsonwebtoken": "8.5.9",
|
||||||
"@types/micro-cors": "0.1.2",
|
"@types/micro-cors": "0.1.2",
|
||||||
"@types/minio": "7.0.14",
|
"@types/minio": "7.0.14",
|
||||||
"@types/node": "18.11.0",
|
"@types/node": "18.11.9",
|
||||||
"@types/nodemailer": "6.4.6",
|
"@types/nodemailer": "6.4.6",
|
||||||
"@types/nprogress": "0.2.0",
|
"@types/nprogress": "0.2.0",
|
||||||
"@types/papaparse": "5.3.5",
|
"@types/papaparse": "5.3.5",
|
||||||
"@types/prettier": "2.7.1",
|
"@types/prettier": "2.7.1",
|
||||||
"@types/qs": "6.9.7",
|
"@types/qs": "6.9.7",
|
||||||
"@types/react": "18.0.21",
|
"@types/react": "18.0.24",
|
||||||
"@types/tinycolor2": "1.4.3",
|
"@types/tinycolor2": "1.4.3",
|
||||||
"@typescript-eslint/eslint-plugin": "5.40.0",
|
"@typescript-eslint/eslint-plugin": "5.42.0",
|
||||||
"@typescript-eslint/parser": "5.40.0",
|
"@typescript-eslint/parser": "5.42.0",
|
||||||
"db": "workspace:*",
|
"db": "workspace:*",
|
||||||
"dotenv": "16.0.3",
|
"dotenv": "16.0.3",
|
||||||
"eslint": "8.25.0",
|
"eslint": "8.26.0",
|
||||||
"eslint-config-next": "12.3.1",
|
"eslint-config-next": "13.0.1",
|
||||||
"eslint-plugin-react": "7.31.10",
|
"eslint-plugin-react": "7.31.10",
|
||||||
"models": "workspace:*",
|
"models": "workspace:*",
|
||||||
"next-transpile-modules": "9.1.0",
|
"next-transpile-modules": "10.0.0",
|
||||||
"typescript": "4.8.4",
|
"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 { TypebotHeader } from 'components/shared/TypebotHeader'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { HStack, Button, Tag, Flex, Text } from '@chakra-ui/react'
|
import { HStack, Button, Tag, Flex, Text } from '@chakra-ui/react'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
|
||||||
import { ResultsContent } from 'components/results/ResultsContent'
|
import { ResultsContent } from 'components/results/ResultsContent'
|
||||||
import { useTypebot } from 'contexts/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext'
|
||||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||||
@ -14,6 +13,7 @@ import { ResultsProvider } from 'contexts/ResultsProvider'
|
|||||||
import { UnlockPlanInfo } from 'components/shared/Info'
|
import { UnlockPlanInfo } from 'components/shared/Info'
|
||||||
import { getChatsLimit, getStorageLimit } from 'utils'
|
import { getChatsLimit, getStorageLimit } from 'utils'
|
||||||
import { useUsage } from 'components/dashboard/WorkspaceSettingsModal/BillingContent/UsageContent/useUsage'
|
import { useUsage } from 'components/dashboard/WorkspaceSettingsModal/BillingContent/UsageContent/useUsage'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
const ALERT_CHATS_PERCENT_THRESHOLD = 80
|
const ALERT_CHATS_PERCENT_THRESHOLD = 80
|
||||||
const ALERT_STORAGE_PERCENT_THRESHOLD = 80
|
const ALERT_STORAGE_PERCENT_THRESHOLD = 80
|
||||||
@ -130,7 +130,7 @@ const ResultsPage = () => {
|
|||||||
>
|
>
|
||||||
<HStack maxW="1600px" w="full" px="4">
|
<HStack maxW="1600px" w="full" px="4">
|
||||||
<Button
|
<Button
|
||||||
as={NextChakraLink}
|
as={Link}
|
||||||
colorScheme={!isAnalytics ? 'blue' : 'gray'}
|
colorScheme={!isAnalytics ? 'blue' : 'gray'}
|
||||||
variant={!isAnalytics ? 'outline' : 'ghost'}
|
variant={!isAnalytics ? 'outline' : 'ghost'}
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -144,7 +144,7 @@ const ResultsPage = () => {
|
|||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
as={NextChakraLink}
|
as={Link}
|
||||||
colorScheme={isAnalytics ? 'blue' : 'gray'}
|
colorScheme={isAnalytics ? 'blue' : 'gray'}
|
||||||
variant={isAnalytics ? 'outline' : 'ghost'}
|
variant={isAnalytics ? 'outline' : 'ghost'}
|
||||||
href={`/typebots/${typebot?.id}/results/analytics`}
|
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'
|
import path from 'path'
|
||||||
|
import { playwrightBaseConfig } from 'configs/playwright'
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
require('dotenv').config({
|
|
||||||
path: path.join(__dirname, 'playwright/.env'),
|
|
||||||
})
|
|
||||||
|
|
||||||
const config: PlaywrightTestConfig = {
|
const config: PlaywrightTestConfig = {
|
||||||
globalSetup: require.resolve(path.join(__dirname, 'playwright/global-setup')),
|
...playwrightBaseConfig,
|
||||||
testDir: path.join(__dirname, 'playwright/tests'),
|
testDir: path.join(__dirname, 'playwright/tests'),
|
||||||
retries: process.env.NO_RETRIES ? 0 : 2,
|
webServer: process.env.CI
|
||||||
workers: process.env.CI ? 1 : 3,
|
? {
|
||||||
reporter: 'html',
|
...(playwrightBaseConfig.webServer as { command: string }),
|
||||||
maxFailures: process.env.CI ? 10 : undefined,
|
port: 3000,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
use: {
|
use: {
|
||||||
actionTimeout: 0,
|
...playwrightBaseConfig.use,
|
||||||
baseURL: process.env.PLAYWRIGHT_BUILDER_TEST_BASE_URL,
|
baseURL: process.env.NEXTAUTH_URL,
|
||||||
trace: 'on-first-retry',
|
|
||||||
storageState: path.join(__dirname, 'playwright/firstUser.json'),
|
storageState: path.join(__dirname, 'playwright/firstUser.json'),
|
||||||
video: 'retain-on-failure',
|
|
||||||
locale: 'en-US',
|
|
||||||
},
|
},
|
||||||
outputDir: path.join(__dirname, 'playwright/test-results/'),
|
outputDir: path.join(__dirname, 'playwright/test-results/'),
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: 'Chrome',
|
|
||||||
use: {
|
|
||||||
...devices['Desktop Chrome'],
|
|
||||||
viewport: { width: 1400, height: 1000 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default config
|
export default config
|
||||||
|
@ -119,12 +119,12 @@
|
|||||||
"graphCoordinates": { "x": 639, "y": 142 }
|
"graphCoordinates": { "x": 639, "y": 142 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "q5dAhqSTCaNdiGSJm9B9Rw",
|
"id": "group1",
|
||||||
"blocks": [
|
"blocks": [
|
||||||
{
|
{
|
||||||
"id": "sgtE2Sy7cKykac9B223Kq9R",
|
"id": "sgtE2Sy7cKykac9B223Kq9R",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"groupId": "q5dAhqSTCaNdiGSJm9B9Rw",
|
"groupId": "group1",
|
||||||
"content": {
|
"content": {
|
||||||
"html": "<div>What's your name?</div>",
|
"html": "<div>What's your name?</div>",
|
||||||
"richText": [
|
"richText": [
|
||||||
@ -134,9 +134,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "sqEsMo747LTDnY9FjQcEwUv",
|
"id": "block1",
|
||||||
"type": "text input",
|
"type": "text input",
|
||||||
"groupId": "q5dAhqSTCaNdiGSJm9B9Rw",
|
"groupId": "group1",
|
||||||
"options": {
|
"options": {
|
||||||
"isLong": false,
|
"isLong": false,
|
||||||
"labels": {
|
"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 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',
|
apiVersion: '2022-08-01',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ test('should display user info properly', async ({ page }) => {
|
|||||||
await expect(page.locator('img >> nth=1')).toHaveAttribute(
|
await expect(page.locator('img >> nth=1')).toHaveAttribute(
|
||||||
'src',
|
'src',
|
||||||
new RegExp(
|
new RegExp(
|
||||||
`http://localhost:9000/typebot/public/users/${userId}/avatar`,
|
`${process.env.S3_ENDPOINT}/${process.env.S3_BUCKET}/public/users/${userId}/avatar`,
|
||||||
'gm'
|
'gm'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import test, { expect } from '@playwright/test'
|
import test, { expect } from '@playwright/test'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { importTypebotInDatabase } from 'utils/playwright/databaseActions'
|
import {
|
||||||
|
importTypebotInDatabase,
|
||||||
|
injectFakeResults,
|
||||||
|
} from 'utils/playwright/databaseActions'
|
||||||
import { starterWorkspaceId } from 'utils/playwright/databaseSetup'
|
import { starterWorkspaceId } from 'utils/playwright/databaseSetup'
|
||||||
|
|
||||||
test('analytics are not available for non-pro workspaces', async ({ page }) => {
|
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,
|
workspaceId: starterWorkspaceId,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
await injectFakeResults({ typebotId, count: 10 })
|
||||||
await page.goto(`/typebots/${typebotId}/results/analytics`)
|
await page.goto(`/typebots/${typebotId}/results/analytics`)
|
||||||
const firstDropoffBox = page.locator('text="%" >> nth=0')
|
const firstDropoffBox = page.locator('text="%" >> nth=0')
|
||||||
await firstDropoffBox.hover()
|
await firstDropoffBox.hover()
|
||||||
|
@ -227,7 +227,7 @@ test('should display invoices', async ({ page }) => {
|
|||||||
await page.click('text=Settings & Members')
|
await page.click('text=Settings & Members')
|
||||||
await page.click('text=Billing & Usage')
|
await page.click('text=Billing & Usage')
|
||||||
await expect(page.locator('text="Invoices"')).toBeVisible()
|
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()
|
await expect(page.locator('text="€39.00"')).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ test.describe.parallel('Image bubble block', () => {
|
|||||||
await expect(page.locator('img')).toHaveAttribute(
|
await expect(page.locator('img')).toHaveAttribute(
|
||||||
'src',
|
'src',
|
||||||
new RegExp(
|
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'
|
'gm'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -26,19 +26,19 @@ test.describe('Payment input block', () => {
|
|||||||
await page.fill('[placeholder="Typebot"]', 'My Stripe Account')
|
await page.fill('[placeholder="Typebot"]', 'My Stripe Account')
|
||||||
await page.fill(
|
await page.fill(
|
||||||
'[placeholder="sk_test_..."]',
|
'[placeholder="sk_test_..."]',
|
||||||
process.env.STRIPE_TEST_SECRET_KEY ?? ''
|
process.env.STRIPE_SECRET_KEY ?? ''
|
||||||
)
|
)
|
||||||
await page.fill(
|
await page.fill(
|
||||||
'[placeholder="sk_live_..."]',
|
'[placeholder="sk_live_..."]',
|
||||||
process.env.STRIPE_TEST_SECRET_KEY ?? ''
|
process.env.STRIPE_SECRET_KEY ?? ''
|
||||||
)
|
)
|
||||||
await page.fill(
|
await page.fill(
|
||||||
'[placeholder="pk_test_..."]',
|
'[placeholder="pk_test_..."]',
|
||||||
process.env.STRIPE_TEST_PUBLIC_KEY ?? ''
|
process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY ?? ''
|
||||||
)
|
)
|
||||||
await page.fill(
|
await page.fill(
|
||||||
'[placeholder="pk_live_..."]',
|
'[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 expect(page.locator('button >> text="Connect"')).toBeEnabled()
|
||||||
await page.click('button >> text="Connect"')
|
await page.click('button >> text="Connect"')
|
||||||
@ -66,6 +66,9 @@ test.describe('Payment input block', () => {
|
|||||||
await stripePaymentForm(page)
|
await stripePaymentForm(page)
|
||||||
.locator(`[placeholder="1234 1234 1234 1234"]`)
|
.locator(`[placeholder="1234 1234 1234 1234"]`)
|
||||||
.fill('4242424242424242')
|
.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 typebotViewer(page).locator(`text="Pay 30€"`).click()
|
||||||
await expect(typebotViewer(page).locator(`text="Success"`)).toBeVisible()
|
await expect(typebotViewer(page).locator(`text="Success"`)).toBeVisible()
|
||||||
})
|
})
|
||||||
|
@ -11,10 +11,9 @@ test.describe('Send email block', () => {
|
|||||||
if (
|
if (
|
||||||
!process.env.SMTP_USERNAME ||
|
!process.env.SMTP_USERNAME ||
|
||||||
!process.env.SMTP_PORT ||
|
!process.env.SMTP_PORT ||
|
||||||
!process.env.SMTP_SECURE ||
|
|
||||||
!process.env.SMTP_HOST ||
|
!process.env.SMTP_HOST ||
|
||||||
!process.env.SMTP_PASSWORD ||
|
!process.env.SMTP_PASSWORD ||
|
||||||
!process.env.SMTP_FROM
|
!process.env.NEXT_PUBLIC_SMTP_FROM
|
||||||
)
|
)
|
||||||
throw new Error('SMTP_ env vars are missing')
|
throw new Error('SMTP_ env vars are missing')
|
||||||
await importTypebotInDatabase(
|
await importTypebotInDatabase(
|
||||||
@ -29,7 +28,7 @@ test.describe('Send email block', () => {
|
|||||||
|
|
||||||
await page.goto(`/typebots/${typebotId}/edit`)
|
await page.goto(`/typebots/${typebotId}/edit`)
|
||||||
await page.click('text=Configure...')
|
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')
|
await page.click('text=Connect new')
|
||||||
const createButton = page.locator('button >> text=Create')
|
const createButton = page.locator('button >> text=Create')
|
||||||
await expect(createButton).toBeDisabled()
|
await expect(createButton).toBeDisabled()
|
||||||
@ -44,7 +43,6 @@ test.describe('Send email block', () => {
|
|||||||
process.env.SMTP_USERNAME
|
process.env.SMTP_USERNAME
|
||||||
)
|
)
|
||||||
await page.fill('[type="password"]', process.env.SMTP_PASSWORD)
|
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 page.fill('input[role="spinbutton"]', process.env.SMTP_PORT)
|
||||||
await expect(createButton).toBeEnabled()
|
await expect(createButton).toBeEnabled()
|
||||||
await createButton.click()
|
await createButton.click()
|
||||||
|
@ -24,7 +24,7 @@ test.describe('Webhook block', () => {
|
|||||||
await page.click('text=Configure...')
|
await page.click('text=Configure...')
|
||||||
await page.fill(
|
await page.fill(
|
||||||
'input[placeholder="Paste webhook URL..."]',
|
'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 page.click('text=Test the request')
|
||||||
await expect(page.locator('div[role="textbox"] >> nth=-1')).toContainText(
|
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.click('text=Configure...')
|
||||||
await page.fill(
|
await page.fill(
|
||||||
'input[placeholder="Paste webhook URL..."]',
|
'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=Advanced configuration')
|
||||||
await page.click('text=GET')
|
await page.click('text=GET')
|
||||||
|
@ -1,184 +1,174 @@
|
|||||||
import test, { expect, Page } from '@playwright/test'
|
import test, { expect, Page } from '@playwright/test'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
import { readFileSync } from 'fs'
|
import { readFileSync } from 'fs'
|
||||||
import { defaultTextInputOptions, InputBlockType } from 'models'
|
|
||||||
import { parse } from 'papaparse'
|
import { parse } from 'papaparse'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import {
|
import {
|
||||||
importTypebotInDatabase,
|
importTypebotInDatabase,
|
||||||
injectFakeResults,
|
injectFakeResults,
|
||||||
createTypebots,
|
|
||||||
} from 'utils/playwright/databaseActions'
|
} from 'utils/playwright/databaseActions'
|
||||||
import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
|
|
||||||
import { deleteButtonInConfirmDialog } from '../services/selectorUtils'
|
import { deleteButtonInConfirmDialog } from '../services/selectorUtils'
|
||||||
|
|
||||||
const typebotId = cuid()
|
const typebotId = cuid()
|
||||||
|
|
||||||
test('Submission table header should be parsed correctly', async ({ page }) => {
|
test.beforeEach(async () => {
|
||||||
const typebotId = cuid()
|
|
||||||
await importTypebotInDatabase(
|
await importTypebotInDatabase(
|
||||||
path.join(__dirname, '../fixtures/typebots/results/submissionHeader.json'),
|
path.join(__dirname, '../fixtures/typebots/results/submissionHeader.json'),
|
||||||
{
|
{
|
||||||
id: typebotId,
|
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 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 }) => {
|
test('Results', 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,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
await page.goto(`/typebots/${typebotId}/results`)
|
await page.goto(`/typebots/${typebotId}/results`)
|
||||||
|
|
||||||
// Resize
|
await test.step('Check header format', async () => {
|
||||||
expect((await page.locator('th >> nth=4').boundingBox())?.width).toBe(200)
|
await expect(page.locator('text=Submitted at')).toBeVisible()
|
||||||
await page.waitForTimeout(500)
|
await expect(page.locator('text=Welcome')).toBeVisible()
|
||||||
await page.dragAndDrop(
|
await expect(page.locator('text=Email')).toBeVisible()
|
||||||
'[data-testid="resize-handle"] >> nth=3',
|
await expect(page.locator('text=Name')).toBeVisible()
|
||||||
'[data-testid="resize-handle"] >> nth=3',
|
await expect(page.locator('text=Services')).toBeVisible()
|
||||||
{ targetPosition: { x: 150, y: 0 }, force: true }
|
await expect(page.locator('text=Additional information')).toBeVisible()
|
||||||
)
|
await expect(page.locator('text=utm_source')).toBeVisible()
|
||||||
await page.waitForTimeout(500)
|
await expect(page.locator('text=utm_userid')).toBeVisible()
|
||||||
expect((await page.locator('th >> nth=4').boundingBox())?.width).toBe(345)
|
})
|
||||||
|
|
||||||
// Hide
|
await test.step('Resize columns', async () => {
|
||||||
await expect(
|
expect((await page.locator('th >> nth=4').boundingBox())?.width).toBe(200)
|
||||||
page.locator('[data-testid="Submitted at header"]')
|
await page.waitForTimeout(500)
|
||||||
).toBeVisible()
|
await page.dragAndDrop(
|
||||||
await expect(page.locator('[data-testid="Email header"]')).toBeVisible()
|
'[data-testid="resize-handle"] >> nth=3',
|
||||||
await page.click('button >> text="Columns"')
|
'[data-testid="resize-handle"] >> nth=3',
|
||||||
await page.click('[aria-label="Hide column"] >> nth=0')
|
{ targetPosition: { x: 150, y: 0 }, force: true }
|
||||||
await page.click('[aria-label="Hide column"] >> nth=1')
|
)
|
||||||
await expect(page.locator('[data-testid="Submitted at header"]')).toBeHidden()
|
await page.waitForTimeout(500)
|
||||||
await expect(page.locator('[data-testid="Email header"]')).toBeHidden()
|
expect((await page.locator('th >> nth=4').boundingBox())?.width).toBe(345)
|
||||||
|
})
|
||||||
|
|
||||||
// Reorder
|
await test.step('Hide columns', async () => {
|
||||||
await expect(page.locator('th >> nth=1')).toHaveText('Welcome')
|
await expect(
|
||||||
await expect(page.locator('th >> nth=2')).toHaveText('Name')
|
page.locator('[data-testid="Submitted at header"]')
|
||||||
await page.dragAndDrop(
|
).toBeVisible()
|
||||||
'[aria-label="Drag"] >> nth=0',
|
await expect(page.locator('[data-testid="Email header"]')).toBeVisible()
|
||||||
'[aria-label="Drag"] >> nth=0',
|
await page.click('button >> text="Columns"')
|
||||||
{ targetPosition: { x: 0, y: 80 }, force: true }
|
await page.click('[aria-label="Hide column"] >> nth=0')
|
||||||
)
|
await page.click('[aria-label="Hide column"] >> nth=1')
|
||||||
await expect(page.locator('th >> nth=1')).toHaveText('Name')
|
await expect(
|
||||||
await expect(page.locator('th >> nth=2')).toHaveText('Welcome')
|
page.locator('[data-testid="Submitted at header"]')
|
||||||
|
).toBeHidden()
|
||||||
|
await expect(page.locator('[data-testid="Email header"]')).toBeHidden()
|
||||||
|
})
|
||||||
|
|
||||||
// Preferences should be persisted
|
await test.step('Reorder columns', async () => {
|
||||||
const saveAndReload = async (page: Page) => {
|
await expect(page.locator('th >> nth=1')).toHaveText('Welcome')
|
||||||
await page.click('text="Theme"')
|
await expect(page.locator('th >> nth=2')).toHaveText('Name')
|
||||||
await page.waitForTimeout(2000)
|
await page.dragAndDrop(
|
||||||
await page.goto(`/typebots/${typebotId}/results`)
|
'[aria-label="Drag"] >> nth=0',
|
||||||
}
|
'[aria-label="Drag"] >> nth=0',
|
||||||
await saveAndReload(page)
|
{ targetPosition: { x: 0, y: 80 }, force: true }
|
||||||
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('th >> nth=1')).toHaveText('Name')
|
||||||
await expect(page.locator('[data-testid="Email header"]')).toBeHidden()
|
await expect(page.locator('th >> nth=2')).toHaveText('Welcome')
|
||||||
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[]) => {
|
const validateExportSelection = (data: unknown[]) => {
|
||||||
expect(data).toHaveLength(3)
|
expect(data).toHaveLength(3)
|
||||||
expect((data[1] as unknown[])[1]).toBe('content199')
|
expect((data[1] as unknown[])[3]).toBe('content199')
|
||||||
expect((data[2] as unknown[])[1]).toBe('content198')
|
expect((data[2] as unknown[])[3]).toBe('content198')
|
||||||
}
|
}
|
||||||
|
|
||||||
const validateExportAll = (data: unknown[]) => {
|
const validateExportAll = (data: unknown[]) => {
|
||||||
expect(data).toHaveLength(201)
|
expect(data).toHaveLength(201)
|
||||||
expect((data[1] as unknown[])[1]).toBe('content199')
|
expect((data[1] as unknown[])[3]).toBe('content199')
|
||||||
expect((data[200] as unknown[])[1]).toBe('content0')
|
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 { freeWorkspaceId } from 'utils/playwright/databaseSetup'
|
||||||
import { typebotViewer } from 'utils/playwright/testHelpers'
|
import { typebotViewer } from 'utils/playwright/testHelpers'
|
||||||
|
|
||||||
|
const typebotId = cuid()
|
||||||
|
|
||||||
test.describe.parallel('Settings page', () => {
|
test.describe.parallel('Settings page', () => {
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
await importTypebotInDatabase(
|
||||||
|
path.join(__dirname, '../fixtures/typebots/settings.json'),
|
||||||
|
{
|
||||||
|
id: typebotId,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
test.describe('General', () => {
|
test.describe('General', () => {
|
||||||
test('should reflect change in real-time', async ({ page }) => {
|
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 page.goto(`/typebots/${typebotId}/settings`)
|
||||||
await expect(
|
await expect(
|
||||||
typebotViewer(page).locator('a:has-text("Made with Typebot")')
|
typebotViewer(page).locator('a:has-text("Made with Typebot")')
|
||||||
@ -134,7 +136,6 @@ test.describe.parallel('Settings page', () => {
|
|||||||
await expect(
|
await expect(
|
||||||
typebotViewer(page).locator('text="What\'s your name?"')
|
typebotViewer(page).locator('text="What\'s your name?"')
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
await page.click('button:has-text("General")')
|
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('[data-testid="starter-lock-tag"]')
|
page.locator('[data-testid="starter-lock-tag"]')
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
|
@ -91,7 +91,7 @@ test('can manage members', async ({ page }) => {
|
|||||||
await page.goto('/typebots')
|
await page.goto('/typebots')
|
||||||
await page.click('text=Settings & Members')
|
await page.click('text=Settings & Members')
|
||||||
await page.click('text="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 expect(page.locator('button >> text="Invite"')).toBeEnabled()
|
||||||
await page.fill(
|
await page.fill(
|
||||||
'input[placeholder="colleague@company.com"]',
|
'input[placeholder="colleague@company.com"]',
|
||||||
|
@ -15,14 +15,14 @@
|
|||||||
"update-search": "docker run -it --rm --env-file=.env -e \"CONFIG=$(cat docsearch-scrapper-config.json | jq -r tostring)\" algolia/docsearch-scraper"
|
"update-search": "docker run -it --rm --env-file=.env -e \"CONFIG=$(cat docsearch-scrapper-config.json | jq -r tostring)\" algolia/docsearch-scraper"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "2.1.0",
|
"@docusaurus/core": "2.2.0",
|
||||||
"@docusaurus/preset-classic": "2.1.0",
|
"@docusaurus/preset-classic": "2.2.0",
|
||||||
"@docusaurus/theme-search-algolia": "2.1.0",
|
"@docusaurus/theme-search-algolia": "2.2.0",
|
||||||
"@docusaurus/theme-common": "2.1.0",
|
"@docusaurus/theme-common": "2.2.0",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"@mdx-js/react": "1.6.22",
|
"@mdx-js/react": "1.6.22",
|
||||||
"@svgr/webpack": "6.5.0",
|
"@svgr/webpack": "6.5.1",
|
||||||
"clsx": "1.2.1",
|
"clsx": "1.2.1",
|
||||||
"file-loader": "6.2.0",
|
"file-loader": "6.2.0",
|
||||||
"prism-react-renderer": "1.3.5",
|
"prism-react-renderer": "1.3.5",
|
||||||
|
@ -36,8 +36,8 @@ export const Testimonial = (props: TestimonialProps) => {
|
|||||||
src={image}
|
src={image}
|
||||||
alt={name}
|
alt={name}
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
width="80px"
|
width={80}
|
||||||
height="80px"
|
height={80}
|
||||||
className="rounded-full"
|
className="rounded-full"
|
||||||
/>
|
/>
|
||||||
<Box>
|
<Box>
|
||||||
|
@ -9,35 +9,35 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/icon": "3.0.11",
|
"@chakra-ui/icon": "3.0.11",
|
||||||
"@chakra-ui/react": "2.3.5",
|
"@chakra-ui/react": "2.3.6",
|
||||||
"@emotion/react": "11.10.4",
|
"@emotion/react": "11.10.5",
|
||||||
"@emotion/styled": "11.10.4",
|
"@emotion/styled": "11.10.5",
|
||||||
"aos": "2.3.4",
|
"aos": "2.3.4",
|
||||||
"bot-engine": "workspace:*",
|
"bot-engine": "workspace:*",
|
||||||
"focus-visible": "5.2.0",
|
"focus-visible": "5.2.0",
|
||||||
"framer-motion": "7.5.3",
|
"framer-motion": "7.6.4",
|
||||||
"models": "workspace:*",
|
"models": "workspace:*",
|
||||||
"next": "12.3.1",
|
"next": "13.0.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"utils": "workspace:*",
|
"utils": "workspace:*",
|
||||||
"db": "workspace:*"
|
"db": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.19.3",
|
"@babel/core": "7.19.6",
|
||||||
"@chakra-ui/styled-system": "2.3.4",
|
"@chakra-ui/styled-system": "2.3.4",
|
||||||
"@next/bundle-analyzer": "12.3.1",
|
"@next/bundle-analyzer": "13.0.1",
|
||||||
"@types/aos": "3.0.4",
|
"@types/aos": "3.0.4",
|
||||||
"@types/node": "18.11.0",
|
"@types/node": "18.11.9",
|
||||||
"@types/react": "18.0.21",
|
"@types/react": "18.0.24",
|
||||||
"@typescript-eslint/eslint-plugin": "5.40.0",
|
"@typescript-eslint/eslint-plugin": "5.42.0",
|
||||||
"@typescript-eslint/parser": "5.40.0",
|
"@typescript-eslint/parser": "5.42.0",
|
||||||
"autoprefixer": "10.4.12",
|
"autoprefixer": "10.4.13",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint": "8.25.0",
|
"eslint": "8.26.0",
|
||||||
"eslint-config-next": "12.3.1",
|
"eslint-config-next": "13.0.1",
|
||||||
"eslint-plugin-react": "7.31.10",
|
"eslint-plugin-react": "7.31.10",
|
||||||
"next-transpile-modules": "9.1.0",
|
"next-transpile-modules": "10.0.0",
|
||||||
"postcss": "8.4.18",
|
"postcss": "8.4.18",
|
||||||
"prettier": "2.7.1",
|
"prettier": "2.7.1",
|
||||||
"typescript": "4.8.4"
|
"typescript": "4.8.4"
|
||||||
|
@ -5,27 +5,27 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "ENVSH_ENV=.env.local bash ../../env.sh next dev -p 3001",
|
"dev": "ENVSH_ENV=.env.local bash ../../env.sh next dev -p 3001",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start -p 3001",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"test": "pnpm playwright test",
|
"test": "pnpm playwright test",
|
||||||
"test:open": "PWDEBUG=1 pnpm playwright test"
|
"test:report": "pnpm playwright show-report"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/nextjs": "7.15.0",
|
"@sentry/nextjs": "7.17.3",
|
||||||
"aws-sdk": "2.1233.0",
|
"aws-sdk": "2.1245.0",
|
||||||
"bot-engine": "*",
|
"bot-engine": "*",
|
||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
"cuid": "2.1.8",
|
"cuid": "2.1.8",
|
||||||
"db": "workspace:*",
|
"db": "workspace:*",
|
||||||
"google-spreadsheet": "3.3.0",
|
"google-spreadsheet": "3.3.0",
|
||||||
"got": "12.5.2",
|
"got": "12.5.2",
|
||||||
"next": "12.3.1",
|
"next": "13.0.1",
|
||||||
"nodemailer": "6.8.0",
|
"nodemailer": "6.8.0",
|
||||||
"qs": "6.11.0",
|
"qs": "6.11.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"sanitize-html": "2.7.2",
|
"sanitize-html": "2.7.3",
|
||||||
"stripe": "10.14.0"
|
"stripe": "10.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-env": "7.19.4",
|
"@babel/preset-env": "7.19.4",
|
||||||
@ -33,26 +33,27 @@
|
|||||||
"@playwright/test": "1.27.1",
|
"@playwright/test": "1.27.1",
|
||||||
"@types/cors": "2.8.12",
|
"@types/cors": "2.8.12",
|
||||||
"@types/google-spreadsheet": "3.3.0",
|
"@types/google-spreadsheet": "3.3.0",
|
||||||
"@types/node": "18.11.0",
|
"@types/node": "18.11.9",
|
||||||
"@types/nodemailer": "6.4.6",
|
"@types/nodemailer": "6.4.6",
|
||||||
"@types/papaparse": "5.3.5",
|
"@types/papaparse": "5.3.5",
|
||||||
"@types/qs": "6.9.7",
|
"@types/qs": "6.9.7",
|
||||||
"@types/react": "18.0.21",
|
"@types/react": "18.0.24",
|
||||||
"@types/sanitize-html": "2.6.2",
|
"@types/sanitize-html": "2.6.2",
|
||||||
"@typescript-eslint/eslint-plugin": "5.40.0",
|
"@typescript-eslint/eslint-plugin": "5.42.0",
|
||||||
"@typescript-eslint/parser": "5.40.0",
|
"@typescript-eslint/parser": "5.42.0",
|
||||||
"dotenv": "16.0.3",
|
"dotenv": "16.0.3",
|
||||||
"emails": "workspace:*",
|
"emails": "workspace:*",
|
||||||
"eslint": "8.25.0",
|
"eslint": "8.26.0",
|
||||||
"eslint-config-next": "12.3.1",
|
"eslint-config-next": "13.0.1",
|
||||||
"eslint-plugin-react": "7.31.10",
|
"eslint-plugin-react": "7.31.10",
|
||||||
"eslint-plugin-react-hooks": "4.6.0",
|
"eslint-plugin-react-hooks": "4.6.0",
|
||||||
"google-auth-library": "8.5.2",
|
"google-auth-library": "8.6.0",
|
||||||
"models": "workspace:*",
|
"models": "workspace:*",
|
||||||
"next-transpile-modules": "9.1.0",
|
"next-transpile-modules": "10.0.0",
|
||||||
"node-fetch": "^3.2.10",
|
"node-fetch": "^3.2.10",
|
||||||
"papaparse": "5.3.2",
|
"papaparse": "5.3.2",
|
||||||
"typescript": "4.8.4",
|
"typescript": "4.8.4",
|
||||||
"utils": "workspace:*"
|
"utils": "workspace:*",
|
||||||
|
"configs": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,21 @@
|
|||||||
import { devices, PlaywrightTestConfig } from '@playwright/test'
|
import { PlaywrightTestConfig } from '@playwright/test'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { playwrightBaseConfig } from 'configs/playwright'
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
require('dotenv').config({
|
|
||||||
path: path.join(__dirname, 'playwright/.env'),
|
|
||||||
})
|
|
||||||
|
|
||||||
const config: PlaywrightTestConfig = {
|
const config: PlaywrightTestConfig = {
|
||||||
globalSetup: require.resolve(path.join(__dirname, 'playwright/global-setup')),
|
...playwrightBaseConfig,
|
||||||
testDir: path.join(__dirname, 'playwright/tests'),
|
testDir: path.join(__dirname, 'playwright/tests'),
|
||||||
timeout: 10 * 2000,
|
webServer: process.env.CI
|
||||||
expect: {
|
? {
|
||||||
timeout: 5000,
|
...(playwrightBaseConfig.webServer as { command: string }),
|
||||||
},
|
port: 3001,
|
||||||
retries: process.env.NO_RETRIES ? 0 : 1,
|
}
|
||||||
workers: process.env.CI ? 1 : 3,
|
: undefined,
|
||||||
reporter: 'html',
|
|
||||||
maxFailures: process.env.CI ? 10 : undefined,
|
|
||||||
use: {
|
use: {
|
||||||
actionTimeout: 0,
|
...playwrightBaseConfig.use,
|
||||||
baseURL: process.env.VIEWER_URL,
|
baseURL: process.env.NEXT_PUBLIC_VIEWER_URL,
|
||||||
trace: 'on-first-retry',
|
|
||||||
video: 'retain-on-failure',
|
|
||||||
locale: 'en-US',
|
|
||||||
},
|
},
|
||||||
outputDir: path.join(__dirname, 'playwright/test-results/'),
|
outputDir: path.join(__dirname, 'playwright/test-results/'),
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: 'Chrome',
|
|
||||||
use: {
|
|
||||||
...devices['Desktop Chrome'],
|
|
||||||
viewport: { width: 1400, height: 1000 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default config
|
export default config
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
VIEWER_URL=http://localhost:3001
|
|
||||||
BUILDER_URL=http://localhost:3000
|
|
||||||
DATABASE_URL=postgresql://postgres:typebot@localhost:5432/typebot
|
|
||||||
ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6 #256-bits secret (can be generated here: https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx)
|
|
@ -1,7 +1,7 @@
|
|||||||
import { CredentialsType, SmtpCredentialsData } from 'models'
|
import { CredentialsType, SmtpCredentialsData } from 'models'
|
||||||
import { PrismaClient } from 'db'
|
import { PrismaClient } from 'db'
|
||||||
import { encrypt } from 'utils/api'
|
import { encrypt } from 'utils/api'
|
||||||
import { freeWorkspaceId } from 'utils/playwright/databaseSetup'
|
import { proWorkspaceId } from 'utils/playwright/databaseSetup'
|
||||||
|
|
||||||
const prisma = new PrismaClient()
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ export const createSmtpCredentials = (
|
|||||||
iv,
|
iv,
|
||||||
name: smtpData.from.email as string,
|
name: smtpData.from.email as string,
|
||||||
type: CredentialsType.SMTP,
|
type: CredentialsType.SMTP,
|
||||||
workspaceId: freeWorkspaceId,
|
workspaceId: proWorkspaceId,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ test('should work as expected', async ({ page, browser }) => {
|
|||||||
await expect(
|
await expect(
|
||||||
typebotViewer(page).locator(`text="3 files uploaded"`)
|
typebotViewer(page).locator(`text="3 files uploaded"`)
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
await page.goto(`${process.env.BUILDER_URL}/typebots/${typebotId}/results`)
|
await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`)
|
||||||
await expect(page.locator('text="api.json"')).toHaveAttribute(
|
await expect(page.locator('text="api.json"')).toHaveAttribute(
|
||||||
'href',
|
'href',
|
||||||
/.+\/api\.json/
|
/.+\/api\.json/
|
||||||
@ -52,7 +52,7 @@ test('should work as expected', async ({ page, browser }) => {
|
|||||||
const file = readFileSync(downloadPath as string).toString()
|
const file = readFileSync(downloadPath as string).toString()
|
||||||
const { data } = parse(file)
|
const { data } = parse(file)
|
||||||
expect(data).toHaveLength(2)
|
expect(data).toHaveLength(2)
|
||||||
expect((data[1] as unknown[])[1]).toContain('http://localhost:9000')
|
expect((data[1] as unknown[])[1]).toContain(process.env.S3_ENDPOINT)
|
||||||
|
|
||||||
const urls = (
|
const urls = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
@ -72,17 +72,7 @@ test('should work as expected', async ({ page, browser }) => {
|
|||||||
await page.locator('button >> text="Delete" >> nth=1').click()
|
await page.locator('button >> text="Delete" >> nth=1').click()
|
||||||
await expect(page.locator('text="api.json"')).toBeHidden()
|
await expect(page.locator('text="api.json"')).toBeHidden()
|
||||||
await page2.goto(urls[0])
|
await page2.goto(urls[0])
|
||||||
await expect(
|
await expect(page2.locator('pre')).toBeHidden()
|
||||||
page2.locator('span:has-text("The specified key does not exist.")')
|
|
||||||
).toBeVisible()
|
|
||||||
await page2.goto(urls[1])
|
|
||||||
await expect(
|
|
||||||
page2.locator('span:has-text("The specified key does not exist.")')
|
|
||||||
).toBeVisible()
|
|
||||||
await page2.goto(urls[2])
|
|
||||||
await expect(
|
|
||||||
page2.locator('span:has-text("The specified key does not exist.")')
|
|
||||||
).toBeVisible()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: uncomment on 1st of November
|
// TODO: uncomment on 1st of November
|
||||||
@ -124,7 +114,7 @@ test('should work as expected', async ({ page, browser }) => {
|
|||||||
// await page.evaluate(() =>
|
// await page.evaluate(() =>
|
||||||
// window.localStorage.setItem('workspaceId', 'starterWorkspace')
|
// window.localStorage.setItem('workspaceId', 'starterWorkspace')
|
||||||
// )
|
// )
|
||||||
// await page.goto(`${process.env.BUILDER_URL}/typebots/${typebotId}/results`)
|
// await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`)
|
||||||
// await expect(page.locator('text="150%"')).toBeVisible()
|
// await expect(page.locator('text="150%"')).toBeVisible()
|
||||||
// await expect(page.locator('text="api.json"')).toBeHidden()
|
// await expect(page.locator('text="api.json"')).toBeHidden()
|
||||||
// })
|
// })
|
||||||
|
@ -16,7 +16,7 @@ test('should work as expected', async ({ page }) => {
|
|||||||
await typebotViewer(page).locator('input').fill('26')
|
await typebotViewer(page).locator('input').fill('26')
|
||||||
await typebotViewer(page).locator('input').press('Enter')
|
await typebotViewer(page).locator('input').press('Enter')
|
||||||
await typebotViewer(page).locator('button >> text=Yes').click()
|
await typebotViewer(page).locator('button >> text=Yes').click()
|
||||||
await page.goto(`${process.env.BUILDER_URL}/typebots/${typebotId}/results`)
|
await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`)
|
||||||
await expect(page.locator('text="Baptiste"')).toBeVisible()
|
await expect(page.locator('text="Baptiste"')).toBeVisible()
|
||||||
await expect(page.locator('text="26"')).toBeVisible()
|
await expect(page.locator('text="26"')).toBeVisible()
|
||||||
await expect(page.locator('text="Yes"')).toBeVisible()
|
await expect(page.locator('text="Yes"')).toBeVisible()
|
||||||
|
@ -17,10 +17,17 @@ const mockSmtpCredentials: SmtpCredentialsData = {
|
|||||||
password: 'yXZChpPy25Qa5yBbeH',
|
password: 'yXZChpPy25Qa5yBbeH',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
try {
|
||||||
|
const credentialsId = 'send-email-credentials'
|
||||||
|
await createSmtpCredentials(credentialsId, mockSmtpCredentials)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
test('should send an email', async ({ page }) => {
|
test('should send an email', async ({ page }) => {
|
||||||
const typebotId = cuid()
|
const typebotId = cuid()
|
||||||
const credentialsId = 'send-email-credentials'
|
|
||||||
await createSmtpCredentials(credentialsId, mockSmtpCredentials)
|
|
||||||
await importTypebotInDatabase(
|
await importTypebotInDatabase(
|
||||||
path.join(__dirname, '../fixtures/typebots/sendEmail.json'),
|
path.join(__dirname, '../fixtures/typebots/sendEmail.json'),
|
||||||
{ id: typebotId, publicId: `${typebotId}-public` }
|
{ id: typebotId, publicId: `${typebotId}-public` }
|
||||||
@ -41,7 +48,7 @@ test('should send an email', async ({ page }) => {
|
|||||||
await expect(
|
await expect(
|
||||||
page.locator('text="<baptiste.arnaud95@gmail.com>" >> nth=0')
|
page.locator('text="<baptiste.arnaud95@gmail.com>" >> nth=0')
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
await page.goto(`${process.env.BUILDER_URL}/typebots/${typebotId}/results`)
|
await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`)
|
||||||
await page.click('text="See logs"')
|
await page.click('text="See logs"')
|
||||||
await expect(page.locator('text="Email successfully sent"')).toBeVisible()
|
await expect(page.locator('text="Email successfully sent"')).toBeVisible()
|
||||||
})
|
})
|
||||||
|
@ -3,18 +3,25 @@ import path from 'path'
|
|||||||
import { importTypebotInDatabase } from 'utils/playwright/databaseActions'
|
import { importTypebotInDatabase } from 'utils/playwright/databaseActions'
|
||||||
import { typebotViewer } from 'utils/playwright/testHelpers'
|
import { typebotViewer } from 'utils/playwright/testHelpers'
|
||||||
|
|
||||||
test('should work as expected', async ({ page }) => {
|
const typebotId = 'cl0ibhi7s0018n21aarlmg0cm'
|
||||||
const typebotId = 'cl0ibhi7s0018n21aarlmg0cm'
|
const linkedTypebotId = 'cl0ibhv8d0130n21aw8doxhj5'
|
||||||
const linkedTypebotId = 'cl0ibhv8d0130n21aw8doxhj5'
|
|
||||||
await importTypebotInDatabase(
|
|
||||||
path.join(__dirname, '../fixtures/typebots/linkTypebots/1.json'),
|
|
||||||
{ id: typebotId, publicId: `${typebotId}-public` }
|
|
||||||
)
|
|
||||||
await importTypebotInDatabase(
|
|
||||||
path.join(__dirname, '../fixtures/typebots/linkTypebots/2.json'),
|
|
||||||
{ id: linkedTypebotId, publicId: `${linkedTypebotId}-public` }
|
|
||||||
)
|
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
try {
|
||||||
|
await importTypebotInDatabase(
|
||||||
|
path.join(__dirname, '../fixtures/typebots/linkTypebots/1.json'),
|
||||||
|
{ id: typebotId, publicId: `${typebotId}-public` }
|
||||||
|
)
|
||||||
|
await importTypebotInDatabase(
|
||||||
|
path.join(__dirname, '../fixtures/typebots/linkTypebots/2.json'),
|
||||||
|
{ id: linkedTypebotId, publicId: `${linkedTypebotId}-public` }
|
||||||
|
)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should work as expected', async ({ page }) => {
|
||||||
await page.goto(`/${typebotId}-public`)
|
await page.goto(`/${typebotId}-public`)
|
||||||
await typebotViewer(page).locator('input').fill('Hello there!')
|
await typebotViewer(page).locator('input').fill('Hello there!')
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -26,6 +33,6 @@ test('should work as expected', async ({ page }) => {
|
|||||||
),
|
),
|
||||||
typebotViewer(page).locator('input').press('Enter'),
|
typebotViewer(page).locator('input').press('Enter'),
|
||||||
])
|
])
|
||||||
await page.goto(`${process.env.BUILDER_URL}/typebots/${typebotId}/results`)
|
await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`)
|
||||||
await expect(page.locator('text=Hello there!')).toBeVisible()
|
await expect(page.locator('text=Hello there!')).toBeVisible()
|
||||||
})
|
})
|
||||||
|
@ -4,12 +4,15 @@ import path from 'path'
|
|||||||
import { HttpMethod } from 'models'
|
import { HttpMethod } from 'models'
|
||||||
import {
|
import {
|
||||||
createWebhook,
|
createWebhook,
|
||||||
|
deleteTypebots,
|
||||||
|
deleteWebhooks,
|
||||||
importTypebotInDatabase,
|
importTypebotInDatabase,
|
||||||
} from 'utils/playwright/databaseActions'
|
} from 'utils/playwright/databaseActions'
|
||||||
import { typebotViewer } from 'utils/playwright/testHelpers'
|
import { typebotViewer } from 'utils/playwright/testHelpers'
|
||||||
|
|
||||||
test('should execute webhooks properly', async ({ page }) => {
|
const typebotId = cuid()
|
||||||
const typebotId = cuid()
|
|
||||||
|
test.beforeEach(async () => {
|
||||||
await importTypebotInDatabase(
|
await importTypebotInDatabase(
|
||||||
path.join(__dirname, '../fixtures/typebots/webhook.json'),
|
path.join(__dirname, '../fixtures/typebots/webhook.json'),
|
||||||
{ id: typebotId, publicId: `${typebotId}-public` }
|
{ id: typebotId, publicId: `${typebotId}-public` }
|
||||||
@ -38,7 +41,18 @@ test('should execute webhooks properly', async ({ page }) => {
|
|||||||
method: HttpMethod.POST,
|
method: HttpMethod.POST,
|
||||||
body: `{{Full body}}`,
|
body: `{{Full body}}`,
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test.afterEach(async () => {
|
||||||
|
await deleteTypebots([typebotId])
|
||||||
|
await deleteWebhooks([
|
||||||
|
'failing-webhook',
|
||||||
|
'partial-body-webhook',
|
||||||
|
'full-body-webhook',
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should execute webhooks properly', async ({ page }) => {
|
||||||
await page.goto(`/${typebotId}-public`)
|
await page.goto(`/${typebotId}-public`)
|
||||||
await typebotViewer(page).locator('text=Send failing webhook').click()
|
await typebotViewer(page).locator('text=Send failing webhook').click()
|
||||||
await typebotViewer(page)
|
await typebotViewer(page)
|
||||||
|
15
package.json
15
package.json
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "2.8.2",
|
"version": "2.8.3",
|
||||||
"name": "typebot-os",
|
"name": "typebot-os",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
@ -12,23 +12,24 @@
|
|||||||
"docker:nuke": "docker compose -f docker-compose.dev.yml down --volumes --remove-orphans",
|
"docker:nuke": "docker compose -f docker-compose.dev.yml down --volumes --remove-orphans",
|
||||||
"dev": "pnpm docker:up && NEXT_PUBLIC_E2E_TEST=false turbo run dev --filter=builder... --filter=viewer... --parallel --no-cache",
|
"dev": "pnpm docker:up && NEXT_PUBLIC_E2E_TEST=false turbo run dev --filter=builder... --filter=viewer... --parallel --no-cache",
|
||||||
"dev:mocking": "pnpm docker:up && NEXT_PUBLIC_E2E_TEST=true turbo run dev --filter=builder... --filter=viewer... --parallel --no-cache",
|
"dev:mocking": "pnpm docker:up && NEXT_PUBLIC_E2E_TEST=true turbo run dev --filter=builder... --filter=viewer... --parallel --no-cache",
|
||||||
"build": "pnpm docker:up && turbo run build",
|
"build": "pnpm docker:up && TURBO_CI_VENDOR_ENV_KEY=NEXT_PUBLIC_ turbo run build",
|
||||||
"build:builder": "turbo run build --filter=builder... && ENVSH_ENV=./apps/builder/.env.docker ENVSH_OUTPUT=./apps/builder/public/__env.js bash env.sh",
|
"build:builder": "TURBO_CI_VENDOR_ENV_KEY=NEXT_PUBLIC_ turbo run build --filter=builder... && ENVSH_ENV=./apps/builder/.env.docker ENVSH_OUTPUT=./apps/builder/public/__env.js bash env.sh",
|
||||||
"build:viewer": "turbo run build --filter=viewer... && ENVSH_ENV=./apps/viewer/.env.docker ENVSH_OUTPUT=./apps/viewer/public/__env.js bash env.sh",
|
"build:viewer": "TURBO_CI_VENDOR_ENV_KEY=NEXT_PUBLIC_ turbo run build --filter=viewer... && ENVSH_ENV=./apps/viewer/.env.docker ENVSH_OUTPUT=./apps/viewer/public/__env.js bash env.sh",
|
||||||
"build:landing-page": "turbo run build --filter=landing-page... && ENVSH_ENV=./apps/landing-page/.env.docker ENVSH_OUTPUT=./apps/landing-page/public/__env.js bash env.sh",
|
"build:landing-page": "turbo run build --filter=landing-page... && ENVSH_ENV=./apps/landing-page/.env.docker ENVSH_OUTPUT=./apps/landing-page/public/__env.js bash env.sh",
|
||||||
"test:builder": "cd apps/builder && pnpm test",
|
"test:builder": "cd apps/builder && pnpm test",
|
||||||
"lint": "turbo run lint",
|
"test:viewer": "cd apps/viewer && pnpm test",
|
||||||
"db:migrate": "cd packages/db && pnpm migration:deploy",
|
"db:migrate": "cd packages/db && pnpm migration:deploy",
|
||||||
|
"build:ci": "turbo run build --filter=builder... --filter=viewer... && ENVSH_ENV=./apps/builder/.env.docker ENVSH_OUTPUT=./apps/builder/public/__env.js bash env.sh && ENVSH_ENV=./apps/viewer/.env.docker ENVSH_OUTPUT=./apps/viewer/public/__env.js bash env.sh",
|
||||||
"generate-change-log": "pnpx gitmoji-changelog"
|
"generate-change-log": "pnpx gitmoji-changelog"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cz-emoji": "1.3.2-canary.2",
|
"cz-emoji": "1.3.2-canary.2",
|
||||||
"turbo": "1.5.6"
|
"turbo": "1.6.3"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"commitizen": {
|
"commitizen": {
|
||||||
"path": "node_modules/cz-emoji"
|
"path": "node_modules/cz-emoji"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@7.12.2"
|
"packageManager": "pnpm@7.14.0"
|
||||||
}
|
}
|
||||||
|
@ -11,30 +11,30 @@
|
|||||||
"lint": "eslint --fix -c ./.eslintrc.js \"./src/**/*.ts*\""
|
"lint": "eslint --fix -c ./.eslintrc.js \"./src/**/*.ts*\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@stripe/react-stripe-js": "1.13.0",
|
"@stripe/react-stripe-js": "1.14.1",
|
||||||
"@stripe/stripe-js": "1.41.0",
|
"@stripe/stripe-js": "1.42.1",
|
||||||
"prop-types": "15.8.1",
|
"prop-types": "15.8.1",
|
||||||
"qs": "6.11.0",
|
"qs": "6.11.0",
|
||||||
"react-frame-component": "5.2.3",
|
"react-frame-component": "5.2.3",
|
||||||
"react-phone-number-input": "3.2.11",
|
"react-phone-number-input": "3.2.12",
|
||||||
"react-scroll": "1.8.7",
|
"react-scroll": "1.8.8",
|
||||||
"react-transition-group": "4.4.5",
|
"react-transition-group": "4.4.5",
|
||||||
"resize-observer": "1.0.4"
|
"resize-observer": "1.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "18.11.0",
|
"@types/node": "18.11.9",
|
||||||
"@types/qs": "6.9.7",
|
"@types/qs": "6.9.7",
|
||||||
"@types/react": "18.0.21",
|
"@types/react": "18.0.24",
|
||||||
"@types/react-phone-number-input": "3.0.14",
|
"@types/react-phone-number-input": "3.0.14",
|
||||||
"@types/react-scroll": "1.8.4",
|
"@types/react-scroll": "1.8.4",
|
||||||
"@types/react-transition-group": "4.4.5",
|
"@types/react-transition-group": "4.4.5",
|
||||||
"@typescript-eslint/eslint-plugin": "5.40.0",
|
"@typescript-eslint/eslint-plugin": "5.42.0",
|
||||||
"@typescript-eslint/parser": "5.40.0",
|
"@typescript-eslint/parser": "5.42.0",
|
||||||
"autoprefixer": "10.4.12",
|
"autoprefixer": "10.4.13",
|
||||||
"baptistearno-tsup": "^0.1.0",
|
"baptistearno-tsup": "^0.1.0",
|
||||||
"db": "workspace:*",
|
"db": "workspace:*",
|
||||||
"eslint": "8.25.0",
|
"eslint": "8.26.0",
|
||||||
"eslint-config-next": "12.3.1",
|
"eslint-config-next": "13.0.1",
|
||||||
"eslint-config-prettier": "8.5.0",
|
"eslint-config-prettier": "8.5.0",
|
||||||
"eslint-plugin-prettier": "4.2.1",
|
"eslint-plugin-prettier": "4.2.1",
|
||||||
"eslint-plugin-react": "7.31.10",
|
"eslint-plugin-react": "7.31.10",
|
||||||
@ -43,7 +43,7 @@
|
|||||||
"prettier": "2.7.1",
|
"prettier": "2.7.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"tailwindcss": "3.1.8",
|
"tailwindcss": "3.2.1",
|
||||||
"typescript": "4.8.4",
|
"typescript": "4.8.4",
|
||||||
"utils": "workspace:*"
|
"utils": "workspace:*"
|
||||||
},
|
},
|
||||||
|
@ -35,7 +35,7 @@ export const InputChatBlock = ({
|
|||||||
) => void
|
) => void
|
||||||
onSkip: () => void
|
onSkip: () => void
|
||||||
}) => {
|
}) => {
|
||||||
const { typebot } = useTypebot()
|
const { typebot, isLoading } = useTypebot()
|
||||||
const { addAnswer } = useAnswers()
|
const { addAnswer } = useAnswers()
|
||||||
const [answer, setAnswer] = useState<string>()
|
const [answer, setAnswer] = useState<string>()
|
||||||
const [isEditting, setIsEditting] = useState(false)
|
const [isEditting, setIsEditting] = useState(false)
|
||||||
@ -66,6 +66,8 @@ export const InputChatBlock = ({
|
|||||||
setIsEditting(true)
|
setIsEditting(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isLoading) return null
|
||||||
|
|
||||||
if (answer) {
|
if (answer) {
|
||||||
const avatarUrl = typebot.theme.chat.guestAvatar?.url
|
const avatarUrl = typebot.theme.chat.guestAvatar?.url
|
||||||
return (
|
return (
|
||||||
|
14
packages/configs/package.json
Normal file
14
packages/configs/package.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "configs",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "./index.ts",
|
||||||
|
"types": "./index.ts",
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"private": true,
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "1.27.1",
|
||||||
|
"@types/node": "18.11.9",
|
||||||
|
"dotenv": "16.0.3",
|
||||||
|
"utils": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
47
packages/configs/playwright/baseConfig.ts
Normal file
47
packages/configs/playwright/baseConfig.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { PlaywrightTestConfig } from '@playwright/test'
|
||||||
|
import path from 'path'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
const builderLocalEnvPath = path.join(
|
||||||
|
__dirname,
|
||||||
|
'../../../apps/builder/.env.local'
|
||||||
|
)
|
||||||
|
const localViewerEnvPath = path.join(
|
||||||
|
__dirname,
|
||||||
|
'../../../apps/viewer/.env.local'
|
||||||
|
)
|
||||||
|
if (fs.existsSync(builderLocalEnvPath))
|
||||||
|
require('dotenv').config({
|
||||||
|
path: builderLocalEnvPath,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (fs.existsSync(localViewerEnvPath))
|
||||||
|
require('dotenv').config({
|
||||||
|
path: localViewerEnvPath,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const playwrightBaseConfig: PlaywrightTestConfig = {
|
||||||
|
globalSetup: require.resolve(path.join(__dirname, 'globalSetup')),
|
||||||
|
timeout: process.env.CI ? 50 * 1000 : 20 * 1000,
|
||||||
|
expect: {
|
||||||
|
timeout: process.env.CI ? 10 * 1000 : 5 * 1000,
|
||||||
|
},
|
||||||
|
retries: process.env.NO_RETRIES ? 0 : 1,
|
||||||
|
workers: process.env.CI ? 2 : 3,
|
||||||
|
reporter: [[process.env.CI ? 'github' : 'list'], ['html']],
|
||||||
|
maxFailures: process.env.CI ? 10 : undefined,
|
||||||
|
webServer: process.env.CI
|
||||||
|
? {
|
||||||
|
command: 'pnpm run start',
|
||||||
|
timeout: 60_000,
|
||||||
|
reuseExistingServer: true,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
use: {
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
video: 'retain-on-failure',
|
||||||
|
locale: 'en-US',
|
||||||
|
browserName: 'chromium',
|
||||||
|
viewport: { width: 1400, height: 1000 },
|
||||||
|
},
|
||||||
|
}
|
1
packages/configs/playwright/index.ts
Normal file
1
packages/configs/playwright/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { playwrightBaseConfig } from './baseConfig'
|
6
packages/configs/tsconfig.json
Normal file
6
packages/configs/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
"main": "./index.ts",
|
"main": "./index.ts",
|
||||||
"types": "./index.ts",
|
"types": "./index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "dotenv -e ../../apps/builder/.env.local prisma db push && pnpm generate:schema && pnpm start:sutdio ",
|
"dev": "dotenv -e ../../apps/builder/.env.local prisma db push && pnpm generate:schema",
|
||||||
"build": "pnpm generate:schema",
|
"build": "pnpm generate:schema",
|
||||||
"start:sutdio": "dotenv -e ../../apps/builder/.env.local -v BROWSER=none prisma studio",
|
"start:sutdio": "dotenv -e ../../apps/builder/.env.local -v BROWSER=none prisma studio",
|
||||||
"generate:schema": "dotenv -e ../../apps/builder/.env.local prisma generate",
|
"generate:schema": "dotenv -e ../../apps/builder/.env.local prisma generate",
|
||||||
@ -16,10 +16,10 @@
|
|||||||
"migration:deploy": "prisma migrate deploy"
|
"migration:deploy": "prisma migrate deploy"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "4.4.0"
|
"@prisma/client": "4.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prisma": "4.4.0",
|
"prisma": "4.5.0",
|
||||||
"typescript": "4.8.4",
|
"typescript": "4.8.4",
|
||||||
"dotenv-cli": "6.0.0"
|
"dotenv-cli": "6.0.0"
|
||||||
}
|
}
|
||||||
|
@ -14,15 +14,15 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@faire/mjml-react": "2.1.4",
|
"@faire/mjml-react": "2.1.4",
|
||||||
"@types/node": "18.11.0",
|
"@types/node": "18.11.9",
|
||||||
"@types/nodemailer": "6.4.6",
|
"@types/nodemailer": "6.4.6",
|
||||||
"@types/react": "18.0.21",
|
"@types/react": "18.0.24",
|
||||||
"concurrently": "7.4.0",
|
"concurrently": "7.5.0",
|
||||||
"http-server": "14.1.1",
|
"http-server": "14.1.1",
|
||||||
"nodemailer": "6.8.0",
|
"nodemailer": "6.8.0",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"serve": "14.0.1",
|
"serve": "14.0.1",
|
||||||
"tsx": "3.10.1",
|
"tsx": "3.11.0",
|
||||||
"utils": "workspace:*"
|
"utils": "workspace:*"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "4.8.4",
|
"typescript": "4.8.4",
|
||||||
"next": "12.3.1",
|
"next": "13.0.1",
|
||||||
"db": "workspace:*"
|
"db": "workspace:*"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
@ -5,17 +5,19 @@
|
|||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start:local": "ts-node index.ts",
|
"start:local": "tsx index.ts",
|
||||||
"start:staging": "NODE_ENV=staging ts-node index.ts",
|
"start:staging": "NODE_ENV=staging tsx index.ts",
|
||||||
"start:prod": "NODE_ENV=production ts-node index.ts"
|
"start:prod": "NODE_ENV=production tsx index.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "18.11.0",
|
"@types/node": "18.11.9",
|
||||||
|
"axios": "^1.1.3",
|
||||||
"db": "workspace:*",
|
"db": "workspace:*",
|
||||||
|
"emails": "workspace:*",
|
||||||
|
"got": "12.5.2",
|
||||||
"models": "workspace:*",
|
"models": "workspace:*",
|
||||||
"ts-node": "10.9.1",
|
"tsx": "3.11.0",
|
||||||
"typescript": "4.8.4",
|
"typescript": "4.8.4",
|
||||||
"utils": "workspace:*",
|
"utils": "workspace:*"
|
||||||
"emails": "workspace:*"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,16 +11,16 @@
|
|||||||
"test": "pnpm jest"
|
"test": "pnpm jest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "29.1.2",
|
"@types/jest": "29.2.1",
|
||||||
"@typescript-eslint/eslint-plugin": "5.40.0",
|
"@typescript-eslint/eslint-plugin": "5.42.0",
|
||||||
"@typescript-eslint/parser": "5.40.0",
|
"@typescript-eslint/parser": "5.42.0",
|
||||||
"baptistearno-tsup": "^0.1.0",
|
"baptistearno-tsup": "^0.1.0",
|
||||||
"eslint": "8.25.0",
|
"eslint": "8.26.0",
|
||||||
"eslint-plugin-functional": "4.4.1",
|
"eslint-plugin-functional": "4.4.1",
|
||||||
"eslint-plugin-jest": "27.1.2",
|
"eslint-plugin-jest": "27.1.3",
|
||||||
"eslint-plugin-prettier": "4.2.1",
|
"eslint-plugin-prettier": "4.2.1",
|
||||||
"jest": "29.2.0",
|
"jest": "29.2.2",
|
||||||
"jest-environment-jsdom": "29.2.0",
|
"jest-environment-jsdom": "29.2.2",
|
||||||
"prettier": "2.7.1",
|
"prettier": "2.7.1",
|
||||||
"ts-jest": "29.0.3",
|
"ts-jest": "29.0.3",
|
||||||
"typescript": "4.8.4"
|
"typescript": "4.8.4"
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "1.27.1",
|
"@playwright/test": "1.27.1",
|
||||||
"@types/nodemailer": "6.4.6",
|
"@types/nodemailer": "6.4.6",
|
||||||
"aws-sdk": "2.1233.0",
|
"aws-sdk": "2.1245.0",
|
||||||
"cuid": "2.1.8",
|
"cuid": "2.1.8",
|
||||||
"db": "workspace:*",
|
"db": "workspace:*",
|
||||||
"models": "workspace:*",
|
"models": "workspace:*",
|
||||||
"next": "12.3.1",
|
"next": "13.0.1",
|
||||||
"nodemailer": "6.8.0",
|
"nodemailer": "6.8.0",
|
||||||
"typescript": "4.8.4"
|
"typescript": "4.8.4"
|
||||||
},
|
},
|
||||||
|
@ -62,7 +62,7 @@ const createAnswers = ({
|
|||||||
resultId: `${resultIdPrefix}-result${idx}`,
|
resultId: `${resultIdPrefix}-result${idx}`,
|
||||||
content: `content${idx}`,
|
content: `content${idx}`,
|
||||||
blockId: 'block1',
|
blockId: 'block1',
|
||||||
groupId: 'block1',
|
groupId: 'group1',
|
||||||
storageUsed: fakeStorage ? Math.round(fakeStorage / count) : null,
|
storageUsed: fakeStorage ? Math.round(fakeStorage / count) : null,
|
||||||
})),
|
})),
|
||||||
],
|
],
|
||||||
@ -95,6 +95,24 @@ export const deleteWorkspaces = async (workspaceIds: string[]) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const deleteTypebots = async (typebotIds: string[]) => {
|
||||||
|
await prisma.typebot.deleteMany({
|
||||||
|
where: { id: { in: typebotIds } },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteCredentials = async (credentialIds: string[]) => {
|
||||||
|
await prisma.credentials.deleteMany({
|
||||||
|
where: { id: { in: credentialIds } },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteWebhooks = async (webhookIds: string[]) => {
|
||||||
|
await prisma.webhook.deleteMany({
|
||||||
|
where: { id: { in: webhookIds } },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const createWorkspaces = async (workspaces: Partial<Workspace>[]) => {
|
export const createWorkspaces = async (workspaces: Partial<Workspace>[]) => {
|
||||||
const workspaceIds = workspaces.map((workspace) => workspace.id ?? cuid())
|
const workspaceIds = workspaces.map((workspace) => workspace.id ?? cuid())
|
||||||
await prisma.workspace.createMany({
|
await prisma.workspace.createMany({
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"author": "baptisteArno",
|
"author": "baptisteArno",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@prettier/plugin-php": "0.19.1",
|
"@prettier/plugin-php": "0.19.2",
|
||||||
"prettier": "2.7.1"
|
"prettier": "2.7.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
4496
pnpm-lock.yaml
generated
4496
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,12 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo "VERCEL_GIT_COMMIT_REF: $VERCEL_GIT_COMMIT_REF"
|
|
||||||
|
|
||||||
if [[ "$VERCEL_GIT_COMMIT_REF" == "dev" || "$VERCEL_GIT_COMMIT_REF" == "main" ]] ; then
|
|
||||||
echo "✅ - Build can proceed"
|
|
||||||
exit 1;
|
|
||||||
|
|
||||||
else
|
|
||||||
echo "🛑 - Build cancelled"
|
|
||||||
exit 0;
|
|
||||||
fi
|
|
10
turbo.json
10
turbo.json
@ -9,15 +9,9 @@
|
|||||||
"db#build": {
|
"db#build": {
|
||||||
"cache": false
|
"cache": false
|
||||||
},
|
},
|
||||||
"test": {
|
|
||||||
"dependsOn": [],
|
|
||||||
"outputs": []
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"outputs": []
|
|
||||||
},
|
|
||||||
"dev": {
|
"dev": {
|
||||||
"dependsOn": ["^dev"]
|
"dependsOn": ["^dev"],
|
||||||
|
"cache": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user