diff --git a/apps/builder/assets/icons.tsx b/apps/builder/assets/icons.tsx
index 98f2137cf92..da586c66315 100644
--- a/apps/builder/assets/icons.tsx
+++ b/apps/builder/assets/icons.tsx
@@ -489,3 +489,10 @@ export const AlertIcon = (props: IconProps) => (
)
+
+export const CloudOffIcon = (props: IconProps) => (
+
+
+
+
+)
diff --git a/apps/builder/components/shared/buttons/PublishButton.tsx b/apps/builder/components/shared/buttons/PublishButton.tsx
index 25da59a8497..e82951b424c 100644
--- a/apps/builder/components/shared/buttons/PublishButton.tsx
+++ b/apps/builder/components/shared/buttons/PublishButton.tsx
@@ -12,7 +12,12 @@ import {
useDisclosure,
ButtonProps,
} from '@chakra-ui/react'
-import { ChevronLeftIcon } from 'assets/icons'
+import {
+ ChevronLeftIcon,
+ CloudOffIcon,
+ LockedIcon,
+ UnlockedIcon,
+} from 'assets/icons'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import { useWorkspace } from 'contexts/WorkspaceContext'
import { InputBlockType } from 'models'
@@ -34,6 +39,9 @@ export const PublishButton = (props: ButtonProps) => {
restorePublishedTypebot,
typebot,
isSavingLoading,
+ updateTypebot,
+ unpublishTypebot,
+ save,
} = useTypebot()
const hasInputFile = typebot?.groups
@@ -46,6 +54,16 @@ export const PublishButton = (props: ButtonProps) => {
if (!publishedTypebot) push(`/typebots/${query.typebotId}/share`)
}
+ const closeTypebot = async () => {
+ updateTypebot({ isClosed: true })
+ await save()
+ }
+
+ const openTypebot = async () => {
+ updateTypebot({ isClosed: false })
+ await save()
+ }
+
return (
{
}
- isDisabled={isNotDefined(publishedTypebot)}
+ isDisabled={isNotDefined(publishedTypebot) || isPublished}
>
- {publishedTypebot && !isPublished && (
+ {publishedTypebot && (
diff --git a/apps/builder/contexts/TypebotContext/TypebotContext.tsx b/apps/builder/contexts/TypebotContext/TypebotContext.tsx
index 945f4d8a2a3..f1c2ec422f0 100644
--- a/apps/builder/contexts/TypebotContext/TypebotContext.tsx
+++ b/apps/builder/contexts/TypebotContext/TypebotContext.tsx
@@ -44,6 +44,7 @@ import { saveWebhook } from 'services/webhook'
import { stringify } from 'qs'
import cuid from 'cuid'
import { useToast } from 'components/shared/hooks/useToast'
+import { deletePublishedTypebotQuery } from 'services/typebots/deletePublishedTypebotQuery'
const autoSaveTimeout = 10000
type UpdateTypebotPayload = Partial<{
@@ -55,6 +56,7 @@ type UpdateTypebotPayload = Partial<{
icon: string
customDomain: string
resultsTablePreferences: ResultsTablePreferences
+ isClosed: boolean
}>
export type SetTypebot = (
@@ -81,6 +83,7 @@ const typebotContext = createContext<
) => Promise
updateTypebot: (updates: UpdateTypebotPayload) => void
publishTypebot: () => void
+ unpublishTypebot: () => void
restorePublishedTypebot: () => void
} & GroupsActions &
BlocksActions &
@@ -314,6 +317,21 @@ export const TypebotContext = ({
}
}
+ const unpublishTypebot = async () => {
+ if (!publishedTypebot || !localTypebot) return
+ setIsPublishing(true)
+ const { error } = await deletePublishedTypebotQuery({
+ publishedTypebotId: publishedTypebot.id,
+ typebotId: localTypebot.id,
+ })
+ setIsPublishing(false)
+ if (error) showToast({ description: error.message })
+ mutate({
+ typebot: localTypebot,
+ webhooks: webhooks ?? [],
+ })
+ }
+
const restorePublishedTypebot = () => {
if (!publishedTypebot || !localTypebot) return
setLocalTypebot(parsePublicTypebotToTypebot(publishedTypebot, localTypebot))
@@ -351,6 +369,7 @@ export const TypebotContext = ({
canUndo,
canRedo,
publishTypebot,
+ unpublishTypebot,
isPublishing,
isPublished,
updateTypebot: updateLocalTypebot,
diff --git a/apps/builder/pages/api/publicTypebots/[id].ts b/apps/builder/pages/api/publicTypebots/[id].ts
index c40b371c840..d8cafe1a850 100644
--- a/apps/builder/pages/api/publicTypebots/[id].ts
+++ b/apps/builder/pages/api/publicTypebots/[id].ts
@@ -2,7 +2,7 @@ import { withSentry } from '@sentry/nextjs'
import prisma from 'libs/prisma'
import { InputBlockType, PublicTypebot } from 'models'
import { NextApiRequest, NextApiResponse } from 'next'
-import { canPublishFileInput } from 'services/api/dbRules'
+import { canPublishFileInput, canWriteTypebot } from 'services/api/dbRules'
import { getAuthenticatedUser } from 'services/api/utils'
import { badRequest, methodNotAllowed, notAuthenticated } from 'utils/api'
@@ -32,6 +32,18 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
})
return res.send({ typebots })
}
+ if (req.method === 'DELETE') {
+ const publishedTypebotId = req.query.id as string
+ const typebotId = req.query.typebotId as string | undefined
+ if (!typebotId) return badRequest(res, 'typebotId is required')
+ await prisma.publicTypebot.deleteMany({
+ where: {
+ id: publishedTypebotId,
+ typebot: canWriteTypebot(typebotId, user),
+ },
+ })
+ return res.send({ success: true })
+ }
return methodNotAllowed(res)
}
diff --git a/apps/builder/pages/api/typebots/[typebotId].ts b/apps/builder/pages/api/typebots/[typebotId].ts
index 668318c15e2..01f3a07a329 100644
--- a/apps/builder/pages/api/typebots/[typebotId].ts
+++ b/apps/builder/pages/api/typebots/[typebotId].ts
@@ -38,15 +38,18 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
}
if (req.method === 'DELETE') {
- const typebots = await prisma.typebot.updateMany({
- where: canWriteTypebot(typebotId, user),
- data: { isArchived: true },
- })
await archiveResults(res)({
typebotId,
user,
resultsFilter: { typebotId },
})
+ await prisma.publicTypebot.deleteMany({
+ where: { typebot: canWriteTypebot(typebotId, user) },
+ })
+ const typebots = await prisma.typebot.updateMany({
+ where: canWriteTypebot(typebotId, user),
+ data: { isArchived: true },
+ })
return res.send({ typebots })
}
if (req.method === 'PUT') {
diff --git a/apps/builder/playwright/global-setup.ts b/apps/builder/playwright/global-setup.ts
index d32ac00308c..60711f0403d 100644
--- a/apps/builder/playwright/global-setup.ts
+++ b/apps/builder/playwright/global-setup.ts
@@ -1,5 +1,5 @@
import { FullConfig } from '@playwright/test'
-import { setupDatabase, teardownDatabase } from './services/database'
+import { setupDatabase, teardownDatabase } from 'utils/playwright/databaseSetup'
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('dotenv').config({ path: '.env' })
diff --git a/apps/builder/playwright/services/browser.ts b/apps/builder/playwright/services/browser.ts
index 96e9055347f..245a9d284d6 100644
--- a/apps/builder/playwright/services/browser.ts
+++ b/apps/builder/playwright/services/browser.ts
@@ -1,18 +1,5 @@
-import { Page } from '@playwright/test'
-
export const refreshUser = async () => {
await fetch('/api/auth/session?update')
const event = new Event('visibilitychange')
document.dispatchEvent(event)
}
-
-export const mockSessionResponsesToOtherUser = async (page: Page) =>
- page.route('/api/auth/session', (route) => {
- if (route.request().method() === 'GET') {
- return route.fulfill({
- status: 200,
- body: '{"user":{"id":"otherUserId","name":"James Doe","email":"other-user@email.com","emailVerified":null,"image":"https://avatars.githubusercontent.com/u/16015833?v=4","stripeId":null,"graphNavigation": "TRACKPAD"}}',
- })
- }
- return route.continue()
- })
diff --git a/apps/builder/playwright/services/database.ts b/apps/builder/playwright/services/database.ts
deleted file mode 100644
index 057b01a4427..00000000000
--- a/apps/builder/playwright/services/database.ts
+++ /dev/null
@@ -1,391 +0,0 @@
-import {
- CredentialsType,
- defaultSettings,
- defaultTheme,
- PublicTypebot,
- Block,
- Typebot,
- Webhook,
-} from 'models'
-import {
- CollaborationType,
- DashboardFolder,
- GraphNavigation,
- Plan,
- PrismaClient,
- User,
- WorkspaceRole,
- Workspace,
-} from 'db'
-import { readFileSync } from 'fs'
-import { injectFakeResults } from 'utils'
-import { encrypt } from 'utils/api'
-import Stripe from 'stripe'
-import cuid from 'cuid'
-
-const prisma = new PrismaClient()
-
-const stripe = new Stripe(process.env.STRIPE_TEST_SECRET_KEY ?? '', {
- apiVersion: '2022-08-01',
-})
-
-export const userId = 'userId'
-const otherUserId = 'otherUserId'
-export const freeWorkspaceId = 'freeWorkspace'
-export const starterWorkspaceId = 'starterWorkspace'
-export const proWorkspaceId = 'proWorkspace'
-const lifetimeWorkspaceId = 'lifetimeWorkspaceId'
-
-export const teardownDatabase = async () => {
- await prisma.workspace.deleteMany({
- where: {
- members: {
- some: { userId: { in: [userId, otherUserId] } },
- },
- },
- })
- await prisma.user.deleteMany({
- where: { id: { in: [userId, otherUserId] } },
- })
- return prisma.webhook.deleteMany()
-}
-
-export const deleteWorkspaces = async (workspaceIds: string[]) => {
- await prisma.workspace.deleteMany({
- where: { id: { in: workspaceIds } },
- })
-}
-
-export const addSubscriptionToWorkspace = async (
- workspaceId: string,
- items: Stripe.SubscriptionCreateParams.Item[],
- metadata: Pick<
- Workspace,
- 'additionalChatsIndex' | 'additionalStorageIndex' | 'plan'
- >
-) => {
- const { id: stripeId } = await stripe.customers.create({
- email: 'test-user@gmail.com',
- name: 'Test User',
- })
- const { id: paymentId } = await stripe.paymentMethods.create({
- card: {
- number: '4242424242424242',
- exp_month: 12,
- exp_year: 2022,
- cvc: '123',
- },
- type: 'card',
- })
- await stripe.paymentMethods.attach(paymentId, { customer: stripeId })
- await stripe.subscriptions.create({
- customer: stripeId,
- items,
- default_payment_method: paymentId,
- currency: 'eur',
- })
- await stripe.customers.update(stripeId, {
- invoice_settings: { default_payment_method: paymentId },
- })
- await prisma.workspace.update({
- where: { id: workspaceId },
- data: {
- stripeId,
- ...metadata,
- },
- })
-}
-
-export const setupDatabase = async () => {
- await setupWorkspaces()
- await setupUsers()
- return setupCredentials()
-}
-
-export const setupWorkspaces = async () => {
- await prisma.workspace.create({
- data: {
- id: freeWorkspaceId,
- name: 'Free workspace',
- plan: Plan.FREE,
- },
- })
- await prisma.workspace.createMany({
- data: [
- {
- id: starterWorkspaceId,
- name: 'Starter workspace',
- stripeId: 'cus_LnPDugJfa18N41',
- plan: Plan.STARTER,
- },
- {
- id: proWorkspaceId,
- name: 'Pro workspace',
- plan: Plan.PRO,
- },
- {
- id: lifetimeWorkspaceId,
- name: 'Lifetime workspace',
- plan: Plan.LIFETIME,
- },
- ],
- })
-}
-
-export const createWorkspaces = async (workspaces: Partial[]) => {
- const workspaceIds = workspaces.map((workspace) => workspace.id ?? cuid())
- await prisma.workspace.createMany({
- data: workspaces.map((workspace, index) => ({
- id: workspaceIds[index],
- name: 'Free workspace',
- plan: Plan.FREE,
- ...workspace,
- })),
- })
- await prisma.memberInWorkspace.createMany({
- data: workspaces.map((_, index) => ({
- userId,
- workspaceId: workspaceIds[index],
- role: WorkspaceRole.ADMIN,
- })),
- })
- return workspaceIds
-}
-
-export const setupUsers = async () => {
- await prisma.user.create({
- data: {
- id: userId,
- email: 'user@email.com',
- name: 'John Doe',
- graphNavigation: GraphNavigation.TRACKPAD,
- apiTokens: {
- createMany: {
- data: [
- {
- name: 'Token 1',
- token: 'jirowjgrwGREHEtoken1',
- createdAt: new Date(2022, 1, 1),
- },
- {
- name: 'Github',
- token: 'jirowjgrwGREHEgdrgithub',
- createdAt: new Date(2022, 1, 2),
- },
- {
- name: 'N8n',
- token: 'jirowjgrwGREHrgwhrwn8n',
- createdAt: new Date(2022, 1, 3),
- },
- ],
- },
- },
- },
- })
- await prisma.user.create({
- data: { id: otherUserId, email: 'other-user@email.com', name: 'James Doe' },
- })
- return prisma.memberInWorkspace.createMany({
- data: [
- {
- role: WorkspaceRole.ADMIN,
- userId,
- workspaceId: freeWorkspaceId,
- },
- {
- role: WorkspaceRole.ADMIN,
- userId,
- workspaceId: starterWorkspaceId,
- },
- {
- role: WorkspaceRole.ADMIN,
- userId,
- workspaceId: proWorkspaceId,
- },
- {
- role: WorkspaceRole.ADMIN,
- userId,
- workspaceId: lifetimeWorkspaceId,
- },
- ],
- })
-}
-
-export const createWebhook = async (
- typebotId: string,
- webhookProps?: Partial
-) => {
- try {
- await prisma.webhook.delete({ where: { id: 'webhook1' } })
- } catch {}
- return prisma.webhook.create({
- data: { method: 'GET', typebotId, id: 'webhook1', ...webhookProps },
- })
-}
-
-export const createCollaboration = (
- userId: string,
- typebotId: string,
- type: CollaborationType
-) =>
- prisma.collaboratorsOnTypebots.create({ data: { userId, typebotId, type } })
-
-export const getSignedInUser = (email: string) =>
- prisma.user.findFirst({ where: { email } })
-
-export const createTypebots = async (partialTypebots: Partial[]) => {
- const typebotsWithId = partialTypebots.map((typebot) => ({
- ...typebot,
- id: typebot.id ?? cuid(),
- }))
- await prisma.typebot.createMany({
- data: typebotsWithId.map(parseTestTypebot),
- })
- return prisma.publicTypebot.createMany({
- data: typebotsWithId.map((t) =>
- parseTypebotToPublicTypebot(t.id + '-public', parseTestTypebot(t))
- ),
- })
-}
-
-export const createFolders = (partialFolders: Partial[]) =>
- prisma.dashboardFolder.createMany({
- data: partialFolders.map((folder) => ({
- workspaceId: proWorkspaceId,
- name: 'Folder #1',
- ...folder,
- })),
- })
-
-const setupCredentials = () => {
- const { encryptedData, iv } = encrypt({
- expiry_date: 1642441058842,
- access_token:
- 'ya29.A0ARrdaM--PV_87ebjywDJpXKb77NBFJl16meVUapYdfNv6W6ZzqqC47fNaPaRjbDbOIIcp6f49cMaX5ndK9TAFnKwlVqz3nrK9nLKqgyDIhYsIq47smcAIZkK56SWPx3X3DwAFqRu2UPojpd2upWwo-3uJrod',
- // This token is linked to a test Google account (typebot.test.user@gmail.com)
- refresh_token:
- '1//039xWRt8YaYa3CgYIARAAGAMSNwF-L9Iru9FyuTrDSa7lkSceggPho83kJt2J29G69iEhT1C6XV1vmo6bQS9puL_R2t8FIwR3gek',
- })
- return prisma.credentials.createMany({
- data: [
- {
- name: 'pro-user@email.com',
- type: CredentialsType.GOOGLE_SHEETS,
- data: encryptedData,
- workspaceId: proWorkspaceId,
- iv,
- },
- ],
- })
-}
-
-export const updateUser = (data: Partial) =>
- prisma.user.update({
- data,
- where: {
- id: userId,
- },
- })
-
-export const createResults = injectFakeResults(prisma)
-
-export const createFolder = (workspaceId: string, name: string) =>
- prisma.dashboardFolder.create({
- data: {
- workspaceId,
- name,
- },
- })
-
-const parseTypebotToPublicTypebot = (
- id: string,
- typebot: Typebot
-): Omit => ({
- id,
- groups: typebot.groups,
- typebotId: typebot.id,
- theme: typebot.theme,
- settings: typebot.settings,
- variables: typebot.variables,
- edges: typebot.edges,
-})
-
-const parseTestTypebot = (partialTypebot: Partial): Typebot => ({
- id: cuid(),
- workspaceId: proWorkspaceId,
- folderId: null,
- name: 'My typebot',
- theme: defaultTheme,
- settings: defaultSettings,
- publicId: null,
- updatedAt: new Date().toISOString(),
- createdAt: new Date().toISOString(),
- publishedTypebotId: null,
- customDomain: null,
- icon: null,
- variables: [{ id: 'var1', name: 'var1' }],
- ...partialTypebot,
- edges: [
- {
- id: 'edge1',
- from: { groupId: 'block0', blockId: 'block0' },
- to: { groupId: 'block1' },
- },
- ],
- groups: [
- {
- id: 'block0',
- title: 'Group #0',
- blocks: [
- {
- id: 'block0',
- type: 'start',
- groupId: 'block0',
- label: 'Start',
- outgoingEdgeId: 'edge1',
- },
- ],
- graphCoordinates: { x: 0, y: 0 },
- },
- ...(partialTypebot.groups ?? []),
- ],
-})
-
-export const parseDefaultGroupWithBlock = (
- block: Partial
-): Pick => ({
- groups: [
- {
- graphCoordinates: { x: 200, y: 200 },
- id: 'block1',
- blocks: [
- {
- id: 'block1',
- groupId: 'block1',
- ...block,
- } as Block,
- ],
- title: 'Group #1',
- },
- ],
-})
-
-export const importTypebotInDatabase = async (
- path: string,
- updates?: Partial
-) => {
- const typebot: Typebot = {
- ...JSON.parse(readFileSync(path).toString()),
- workspaceId: proWorkspaceId,
- ...updates,
- }
- await prisma.typebot.create({
- data: typebot,
- })
- return prisma.publicTypebot.create({
- data: parseTypebotToPublicTypebot(
- updates?.id ? `${updates?.id}-public` : 'publicBot',
- typebot
- ),
- })
-}
diff --git a/apps/builder/playwright/services/databaseActions.ts b/apps/builder/playwright/services/databaseActions.ts
new file mode 100644
index 00000000000..2790d98e857
--- /dev/null
+++ b/apps/builder/playwright/services/databaseActions.ts
@@ -0,0 +1,76 @@
+import { CollaborationType, DashboardFolder, PrismaClient, Workspace } from 'db'
+import Stripe from 'stripe'
+import { proWorkspaceId } from 'utils/playwright/databaseSetup'
+
+const prisma = new PrismaClient()
+
+const stripe = new Stripe(process.env.STRIPE_TEST_SECRET_KEY ?? '', {
+ apiVersion: '2022-08-01',
+})
+
+export const addSubscriptionToWorkspace = async (
+ workspaceId: string,
+ items: Stripe.SubscriptionCreateParams.Item[],
+ metadata: Pick<
+ Workspace,
+ 'additionalChatsIndex' | 'additionalStorageIndex' | 'plan'
+ >
+) => {
+ const { id: stripeId } = await stripe.customers.create({
+ email: 'test-user@gmail.com',
+ name: 'Test User',
+ })
+ const { id: paymentId } = await stripe.paymentMethods.create({
+ card: {
+ number: '4242424242424242',
+ exp_month: 12,
+ exp_year: 2022,
+ cvc: '123',
+ },
+ type: 'card',
+ })
+ await stripe.paymentMethods.attach(paymentId, { customer: stripeId })
+ await stripe.subscriptions.create({
+ customer: stripeId,
+ items,
+ default_payment_method: paymentId,
+ currency: 'eur',
+ })
+ await stripe.customers.update(stripeId, {
+ invoice_settings: { default_payment_method: paymentId },
+ })
+ await prisma.workspace.update({
+ where: { id: workspaceId },
+ data: {
+ stripeId,
+ ...metadata,
+ },
+ })
+}
+
+export const createCollaboration = (
+ userId: string,
+ typebotId: string,
+ type: CollaborationType
+) =>
+ prisma.collaboratorsOnTypebots.create({ data: { userId, typebotId, type } })
+
+export const getSignedInUser = (email: string) =>
+ prisma.user.findFirst({ where: { email } })
+
+export const createFolders = (partialFolders: Partial[]) =>
+ prisma.dashboardFolder.createMany({
+ data: partialFolders.map((folder) => ({
+ workspaceId: proWorkspaceId,
+ name: 'Folder #1',
+ ...folder,
+ })),
+ })
+
+export const createFolder = (workspaceId: string, name: string) =>
+ prisma.dashboardFolder.create({
+ data: {
+ workspaceId,
+ name,
+ },
+ })
diff --git a/apps/builder/playwright/services/selectorUtils.ts b/apps/builder/playwright/services/selectorUtils.ts
index ca894900cb7..5c276ce81ef 100644
--- a/apps/builder/playwright/services/selectorUtils.ts
+++ b/apps/builder/playwright/services/selectorUtils.ts
@@ -3,9 +3,6 @@ import { Page } from '@playwright/test'
export const deleteButtonInConfirmDialog = (page: Page) =>
page.locator('section[role="alertdialog"] button:has-text("Delete")')
-export const typebotViewer = (page: Page) =>
- page.frameLocator('#typebot-iframe')
-
export const stripePaymentForm = (page: Page) =>
page
.frameLocator('#typebot-iframe')
diff --git a/apps/builder/playwright/tests/accountSettings.spec.ts b/apps/builder/playwright/tests/accountSettings.spec.ts
index 99daaf04273..abf8d0beba6 100644
--- a/apps/builder/playwright/tests/accountSettings.spec.ts
+++ b/apps/builder/playwright/tests/accountSettings.spec.ts
@@ -1,6 +1,6 @@
import test, { expect } from '@playwright/test'
import path from 'path'
-import { userId } from 'playwright/services/database'
+import { userId } from 'utils/playwright/databaseSetup'
test.describe.configure({ mode: 'parallel' })
diff --git a/apps/builder/playwright/tests/analytics.spec.ts b/apps/builder/playwright/tests/analytics.spec.ts
index 500e888acf0..8ff41c598f6 100644
--- a/apps/builder/playwright/tests/analytics.spec.ts
+++ b/apps/builder/playwright/tests/analytics.spec.ts
@@ -1,10 +1,8 @@
import test, { expect } from '@playwright/test'
import cuid from 'cuid'
import path from 'path'
-import {
- importTypebotInDatabase,
- starterWorkspaceId,
-} from '../services/database'
+import { importTypebotInDatabase } from 'utils/playwright/databaseActions'
+import { starterWorkspaceId } from 'utils/playwright/databaseSetup'
test('analytics are not available for non-pro workspaces', async ({ page }) => {
const typebotId = cuid()
diff --git a/apps/builder/playwright/tests/billing.spec.ts b/apps/builder/playwright/tests/billing.spec.ts
index 251222f424f..8f106312e99 100644
--- a/apps/builder/playwright/tests/billing.spec.ts
+++ b/apps/builder/playwright/tests/billing.spec.ts
@@ -1,13 +1,13 @@
import test, { expect } from '@playwright/test'
import cuid from 'cuid'
import { Plan } from 'db'
+import { addSubscriptionToWorkspace } from 'playwright/services/databaseActions'
import {
- addSubscriptionToWorkspace,
- createResults,
createTypebots,
createWorkspaces,
deleteWorkspaces,
-} from '../services/database'
+ injectFakeResults,
+} from 'utils/playwright/databaseActions'
const usageWorkspaceId = cuid()
const usageTypebotId = cuid()
@@ -48,7 +48,7 @@ test('should display valid usage', async ({ page }) => {
await expect(page.locator('text="Storage"')).toBeHidden()
await page.click('text=Free workspace', { force: true })
- await createResults({
+ await injectFakeResults({
count: 10,
typebotId: usageTypebotId,
fakeStorage: 1100 * 1024 * 1024,
@@ -70,7 +70,7 @@ test('should display valid usage', async ({ page }) => {
'54'
)
- await createResults({
+ await injectFakeResults({
typebotId: usageTypebotId,
count: 1090,
fakeStorage: 1200 * 1024 * 1024,
diff --git a/apps/builder/playwright/tests/bubbles/embed.spec.ts b/apps/builder/playwright/tests/bubbles/embed.spec.ts
index 3d63d0c67ed..46a9cf3b53e 100644
--- a/apps/builder/playwright/tests/bubbles/embed.spec.ts
+++ b/apps/builder/playwright/tests/bubbles/embed.spec.ts
@@ -1,11 +1,9 @@
import test, { expect } from '@playwright/test'
-import {
- createTypebots,
- parseDefaultGroupWithBlock,
-} from '../../services/database'
import { BubbleBlockType, defaultEmbedBubbleContent } from 'models'
-import { typebotViewer } from '../../services/selectorUtils'
import cuid from 'cuid'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
+import { typebotViewer } from 'utils/playwright/testHelpers'
const pdfSrc = 'https://www.orimi.com/pdf-test.pdf'
const siteSrc = 'https://app.cal.com/baptistearno/15min'
diff --git a/apps/builder/playwright/tests/bubbles/image.spec.ts b/apps/builder/playwright/tests/bubbles/image.spec.ts
index 7f13a4881fc..33ffc3d09bf 100644
--- a/apps/builder/playwright/tests/bubbles/image.spec.ts
+++ b/apps/builder/playwright/tests/bubbles/image.spec.ts
@@ -1,12 +1,10 @@
import test, { expect } from '@playwright/test'
-import {
- createTypebots,
- parseDefaultGroupWithBlock,
-} from '../../services/database'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import { BubbleBlockType, defaultImageBubbleContent } from 'models'
-import { typebotViewer } from '../../services/selectorUtils'
import path from 'path'
import cuid from 'cuid'
+import { typebotViewer } from 'utils/playwright/testHelpers'
const unsplashImageSrc =
'https://images.unsplash.com/photo-1504297050568-910d24c426d3?ixlib=rb-1.2.1&ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80'
diff --git a/apps/builder/playwright/tests/bubbles/text.spec.ts b/apps/builder/playwright/tests/bubbles/text.spec.ts
index 85f84e7d73e..4fa4f7faaa3 100644
--- a/apps/builder/playwright/tests/bubbles/text.spec.ts
+++ b/apps/builder/playwright/tests/bubbles/text.spec.ts
@@ -1,11 +1,9 @@
import test, { expect } from '@playwright/test'
-import {
- createTypebots,
- parseDefaultGroupWithBlock,
-} from '../../services/database'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import { BubbleBlockType, defaultTextBubbleContent } from 'models'
-import { typebotViewer } from '../../services/selectorUtils'
import cuid from 'cuid'
+import { typebotViewer } from 'utils/playwright/testHelpers'
test.describe('Text bubble block', () => {
test('rich text features should work', async ({ page }) => {
diff --git a/apps/builder/playwright/tests/bubbles/video.spec.ts b/apps/builder/playwright/tests/bubbles/video.spec.ts
index 4c8fffd8838..da4e313683b 100644
--- a/apps/builder/playwright/tests/bubbles/video.spec.ts
+++ b/apps/builder/playwright/tests/bubbles/video.spec.ts
@@ -1,15 +1,13 @@
import test, { expect } from '@playwright/test'
-import {
- createTypebots,
- parseDefaultGroupWithBlock,
-} from '../../services/database'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import {
BubbleBlockType,
defaultVideoBubbleContent,
VideoBubbleContentType,
} from 'models'
-import { typebotViewer } from '../../services/selectorUtils'
import cuid from 'cuid'
+import { typebotViewer } from 'utils/playwright/testHelpers'
const videoSrc =
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4'
diff --git a/apps/builder/playwright/tests/collaboration.spec.ts b/apps/builder/playwright/tests/collaboration.spec.ts
index 30fda9f648d..b9c5b0f6962 100644
--- a/apps/builder/playwright/tests/collaboration.spec.ts
+++ b/apps/builder/playwright/tests/collaboration.spec.ts
@@ -3,13 +3,13 @@ import cuid from 'cuid'
import { CollaborationType, Plan, WorkspaceRole } from 'db'
import prisma from 'libs/prisma'
import { InputBlockType, defaultTextInputOptions } from 'models'
+import { createFolder } from 'playwright/services/databaseActions'
import {
- createFolder,
- createResults,
createTypebots,
- parseDefaultGroupWithBlock,
- userId,
-} from '../services/database'
+ injectFakeResults,
+} from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
+import { userId } from 'utils/playwright/databaseSetup'
test.describe('Typebot owner', () => {
test('Can invite collaborators', async ({ page }) => {
@@ -103,7 +103,7 @@ test.describe('Guest', () => {
},
})
await createFolder(guestWorkspaceId, 'Guest folder')
- await createResults({ typebotId, count: 10 })
+ await injectFakeResults({ typebotId, count: 10 })
await page.goto(`/typebots`)
await page.click('text=Pro workspace')
await page.click('text=Guest workspace #2')
diff --git a/apps/builder/playwright/tests/customDomains.spec.ts b/apps/builder/playwright/tests/customDomains.spec.ts
index e7227342777..12c5fa7a351 100644
--- a/apps/builder/playwright/tests/customDomains.spec.ts
+++ b/apps/builder/playwright/tests/customDomains.spec.ts
@@ -1,11 +1,9 @@
import test, { expect } from '@playwright/test'
import { InputBlockType, defaultTextInputOptions } from 'models'
-import {
- createTypebots,
- parseDefaultGroupWithBlock,
- starterWorkspaceId,
-} from '../services/database'
import cuid from 'cuid'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
+import { starterWorkspaceId } from 'utils/playwright/databaseSetup'
test('should be able to connect custom domain', async ({ page }) => {
const typebotId = cuid()
diff --git a/apps/builder/playwright/tests/dashboard.spec.ts b/apps/builder/playwright/tests/dashboard.spec.ts
index f2430d00909..73b668eee8c 100644
--- a/apps/builder/playwright/tests/dashboard.spec.ts
+++ b/apps/builder/playwright/tests/dashboard.spec.ts
@@ -1,6 +1,7 @@
-import test, { expect, Page } from '@playwright/test'
+import test, { expect } from '@playwright/test'
import cuid from 'cuid'
-import { createFolders, createTypebots } from '../services/database'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { createFolders } from '../services/databaseActions'
import { deleteButtonInConfirmDialog } from '../services/selectorUtils'
test('folders navigation should work', async ({ page }) => {
diff --git a/apps/builder/playwright/tests/editor.spec.ts b/apps/builder/playwright/tests/editor.spec.ts
index 7965d46bf58..1a765ca769e 100644
--- a/apps/builder/playwright/tests/editor.spec.ts
+++ b/apps/builder/playwright/tests/editor.spec.ts
@@ -1,13 +1,18 @@
import test, { expect } from '@playwright/test'
-import {
- createTypebots,
- importTypebotInDatabase,
- parseDefaultGroupWithBlock,
-} from '../services/database'
import { defaultTextInputOptions, InputBlockType } from 'models'
import path from 'path'
import cuid from 'cuid'
-import { typebotViewer } from '../services/selectorUtils'
+import {
+ createTypebots,
+ importTypebotInDatabase,
+} from 'utils/playwright/databaseActions'
+import {
+ typebotViewer,
+ waitForSuccessfulDeleteRequest,
+ waitForSuccessfulPostRequest,
+ waitForSuccessfulPutRequest,
+} from 'utils/playwright/testHelpers'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
test.describe.configure({ mode: 'parallel' })
@@ -180,3 +185,42 @@ test('Preview from group should work', async ({ page }) => {
typebotViewer(page).locator('text="Hello this is group 1"')
).toBeVisible()
})
+
+test('Published typebot menu should work', async ({ page }) => {
+ const typebotId = cuid()
+ await createTypebots([
+ {
+ id: typebotId,
+ name: 'My awesome typebot',
+ ...parseDefaultGroupWithBlock({
+ type: InputBlockType.TEXT,
+ options: defaultTextInputOptions,
+ }),
+ },
+ ])
+ await page.goto(`/typebots/${typebotId}/edit`)
+ await expect(page.locator("text='Start'")).toBeVisible()
+ await expect(page.locator('button >> text="Published"')).toBeVisible()
+ await page.click('[aria-label="Show published typebot menu"]')
+ await Promise.all([
+ waitForSuccessfulPutRequest(page),
+ page.click('text="Close typebot to new responses"'),
+ ])
+ await expect(page.locator('button >> text="Closed"')).toBeDisabled()
+ await page.click('[aria-label="Show published typebot menu"]')
+ await Promise.all([
+ waitForSuccessfulPutRequest(page),
+ page.click('text="Reopen typebot to new responses"'),
+ ])
+ await expect(page.locator('button >> text="Published"')).toBeDisabled()
+ await page.click('[aria-label="Show published typebot menu"]')
+ await Promise.all([
+ waitForSuccessfulDeleteRequest(page),
+ page.click('button >> text="Unpublish typebot"'),
+ ])
+ await Promise.all([
+ waitForSuccessfulPostRequest(page),
+ page.click('button >> text="Publish"'),
+ ])
+ await expect(page.locator('button >> text="Published"')).toBeVisible()
+})
diff --git a/apps/builder/playwright/tests/inputs/buttons.spec.ts b/apps/builder/playwright/tests/inputs/buttons.spec.ts
index f93820c255a..e3ba7db58d8 100644
--- a/apps/builder/playwright/tests/inputs/buttons.spec.ts
+++ b/apps/builder/playwright/tests/inputs/buttons.spec.ts
@@ -2,12 +2,12 @@ import test, { expect } from '@playwright/test'
import {
createTypebots,
importTypebotInDatabase,
- parseDefaultGroupWithBlock,
-} from '../../services/database'
+} from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import { defaultChoiceInputOptions, InputBlockType, ItemType } from 'models'
-import { typebotViewer } from '../../services/selectorUtils'
import cuid from 'cuid'
import path from 'path'
+import { typebotViewer } from 'utils/playwright/testHelpers'
test.describe.parallel('Buttons input block', () => {
test('can edit button items', async ({ page }) => {
diff --git a/apps/builder/playwright/tests/inputs/date.spec.ts b/apps/builder/playwright/tests/inputs/date.spec.ts
index d2f84f3d022..fefc173ff91 100644
--- a/apps/builder/playwright/tests/inputs/date.spec.ts
+++ b/apps/builder/playwright/tests/inputs/date.spec.ts
@@ -1,10 +1,8 @@
import test, { expect } from '@playwright/test'
-import {
- createTypebots,
- parseDefaultGroupWithBlock,
-} from '../../services/database'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import { defaultDateInputOptions, InputBlockType } from 'models'
-import { typebotViewer } from '../../services/selectorUtils'
+import { typebotViewer } from 'utils/playwright/testHelpers'
import cuid from 'cuid'
test.describe('Date input block', () => {
diff --git a/apps/builder/playwright/tests/inputs/email.spec.ts b/apps/builder/playwright/tests/inputs/email.spec.ts
index 1e2a7c2fb4a..40bd17b418f 100644
--- a/apps/builder/playwright/tests/inputs/email.spec.ts
+++ b/apps/builder/playwright/tests/inputs/email.spec.ts
@@ -1,10 +1,8 @@
import test, { expect } from '@playwright/test'
-import {
- createTypebots,
- parseDefaultGroupWithBlock,
-} from '../../services/database'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import { defaultEmailInputOptions, InputBlockType } from 'models'
-import { typebotViewer } from '../../services/selectorUtils'
+import { typebotViewer } from 'utils/playwright/testHelpers'
import cuid from 'cuid'
test.describe('Email input block', () => {
diff --git a/apps/builder/playwright/tests/inputs/file.spec.ts b/apps/builder/playwright/tests/inputs/file.spec.ts
index 7db06f3436b..4e61de7ab6b 100644
--- a/apps/builder/playwright/tests/inputs/file.spec.ts
+++ b/apps/builder/playwright/tests/inputs/file.spec.ts
@@ -1,13 +1,11 @@
import test, { expect } from '@playwright/test'
-import {
- createTypebots,
- freeWorkspaceId,
- parseDefaultGroupWithBlock,
-} from '../../services/database'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import { defaultFileInputOptions, InputBlockType } from 'models'
-import { typebotViewer } from '../../services/selectorUtils'
+import { typebotViewer } from 'utils/playwright/testHelpers'
import cuid from 'cuid'
import path from 'path'
+import { freeWorkspaceId } from 'utils/playwright/databaseSetup'
test.describe.configure({ mode: 'parallel' })
diff --git a/apps/builder/playwright/tests/inputs/number.spec.ts b/apps/builder/playwright/tests/inputs/number.spec.ts
index b67a31d52b2..892d2469165 100644
--- a/apps/builder/playwright/tests/inputs/number.spec.ts
+++ b/apps/builder/playwright/tests/inputs/number.spec.ts
@@ -1,10 +1,8 @@
import test, { expect } from '@playwright/test'
-import {
- createTypebots,
- parseDefaultGroupWithBlock,
-} from '../../services/database'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import { defaultNumberInputOptions, InputBlockType } from 'models'
-import { typebotViewer } from '../../services/selectorUtils'
+import { typebotViewer } from 'utils/playwright/testHelpers'
import cuid from 'cuid'
test.describe('Number input block', () => {
diff --git a/apps/builder/playwright/tests/inputs/payment.spec.ts b/apps/builder/playwright/tests/inputs/payment.spec.ts
index 393a512cfa6..756cab3172a 100644
--- a/apps/builder/playwright/tests/inputs/payment.spec.ts
+++ b/apps/builder/playwright/tests/inputs/payment.spec.ts
@@ -1,11 +1,10 @@
import test, { expect } from '@playwright/test'
-import {
- createTypebots,
- parseDefaultGroupWithBlock,
-} from '../../services/database'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import { defaultPaymentInputOptions, InputBlockType } from 'models'
import cuid from 'cuid'
-import { stripePaymentForm, typebotViewer } from '../../services/selectorUtils'
+import { stripePaymentForm } from '../../services/selectorUtils'
+import { typebotViewer } from 'utils/playwright/testHelpers'
test.describe('Payment input block', () => {
test('Can configure Stripe account', async ({ page }) => {
diff --git a/apps/builder/playwright/tests/inputs/phone.spec.ts b/apps/builder/playwright/tests/inputs/phone.spec.ts
index 3a49473d155..b882b596fc8 100644
--- a/apps/builder/playwright/tests/inputs/phone.spec.ts
+++ b/apps/builder/playwright/tests/inputs/phone.spec.ts
@@ -1,10 +1,8 @@
import test, { expect } from '@playwright/test'
-import {
- createTypebots,
- parseDefaultGroupWithBlock,
-} from '../../services/database'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import { defaultPhoneInputOptions, InputBlockType } from 'models'
-import { typebotViewer } from '../../services/selectorUtils'
+import { typebotViewer } from 'utils/playwright/testHelpers'
import cuid from 'cuid'
test.describe('Phone input block', () => {
diff --git a/apps/builder/playwright/tests/inputs/rating.spec.ts b/apps/builder/playwright/tests/inputs/rating.spec.ts
index 0c6f50e52a1..3e58884ed86 100644
--- a/apps/builder/playwright/tests/inputs/rating.spec.ts
+++ b/apps/builder/playwright/tests/inputs/rating.spec.ts
@@ -1,10 +1,8 @@
import test, { expect } from '@playwright/test'
-import {
- createTypebots,
- parseDefaultGroupWithBlock,
-} from '../../services/database'
+import { createTypebots } from 'utils/playwright/databaseActions'
+import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import { defaultRatingInputOptions, InputBlockType } from 'models'
-import { typebotViewer } from '../../services/selectorUtils'
+import { typebotViewer } from 'utils/playwright/testHelpers'
import cuid from 'cuid'
const boxSvg = `