feat(editor): ✨ Add send email integration
This commit is contained in:
@ -2,4 +2,11 @@ PLAYWRIGHT_BUILDER_TEST_BASE_URL=http://localhost:3000
|
||||
|
||||
# For auth
|
||||
GITHUB_EMAIL=
|
||||
GITHUB_PASSWORD=
|
||||
GITHUB_PASSWORD=
|
||||
|
||||
# SMTP Credentials (Generated on https://ethereal.email/)
|
||||
SMTP_HOST=smtp.ethereal.email
|
||||
SMTP_PORT=587
|
||||
SMTP_SECURE=true
|
||||
SMTP_USERNAME=tobin.tillman65@ethereal.email
|
||||
SMTP_PASSWORD=Ty9BcwCBrK6w8AG2hx
|
@ -0,0 +1,112 @@
|
||||
{
|
||||
"id": "ckzcj4tfu1686gg1ae4fdj8uv",
|
||||
"createdAt": "2022-02-07T10:06:35.274Z",
|
||||
"updatedAt": "2022-02-07T10:06:35.274Z",
|
||||
"name": "My typebot",
|
||||
"ownerId": "ckz6t9iep0006k31a22j05fwq",
|
||||
"publishedTypebotId": null,
|
||||
"folderId": null,
|
||||
"blocks": [
|
||||
{
|
||||
"id": "kSDJqC9TmM25eAM3a2yn3o",
|
||||
"steps": [
|
||||
{
|
||||
"id": "phSmjJU2gYq7b11hpima8b",
|
||||
"type": "start",
|
||||
"label": "Start",
|
||||
"blockId": "kSDJqC9TmM25eAM3a2yn3o",
|
||||
"outgoingEdgeId": "vKtpPmbmqgeGC4vwCfPEdv"
|
||||
}
|
||||
],
|
||||
"title": "Start",
|
||||
"graphCoordinates": { "x": 0, "y": 0 }
|
||||
},
|
||||
{
|
||||
"id": "b5r2MMyftV1nv9vyr6VkZh",
|
||||
"graphCoordinates": { "x": 242, "y": 174 },
|
||||
"title": "Block #2",
|
||||
"steps": [
|
||||
{
|
||||
"id": "sb7ibhNAKfvs8yy8fz3XRMT",
|
||||
"blockId": "b5r2MMyftV1nv9vyr6VkZh",
|
||||
"type": "text",
|
||||
"content": {
|
||||
"html": "<div>Send email</div>",
|
||||
"richText": [
|
||||
{ "type": "p", "children": [{ "text": "Send email" }] }
|
||||
],
|
||||
"plainText": "Send email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "svM58drFcdtdJ7DaJCfTLXm",
|
||||
"blockId": "b5r2MMyftV1nv9vyr6VkZh",
|
||||
"type": "choice input",
|
||||
"options": { "buttonLabel": "Send", "isMultipleChoice": false },
|
||||
"items": [
|
||||
{
|
||||
"id": "nxQEmdaQXc9eFjrbrVBavH",
|
||||
"stepId": "svM58drFcdtdJ7DaJCfTLXm",
|
||||
"type": 0,
|
||||
"content": "Go"
|
||||
}
|
||||
],
|
||||
"outgoingEdgeId": "ioB4s1iRBb8wXiRam8Pp4s"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "6jr7XM9GbVkJ2Ru1WyL45v",
|
||||
"graphCoordinates": { "x": 609, "y": 429 },
|
||||
"title": "Block #2",
|
||||
"steps": [
|
||||
{
|
||||
"id": "sr2sdAzN5dGao1gCiDWCG8i",
|
||||
"blockId": "6jr7XM9GbVkJ2Ru1WyL45v",
|
||||
"type": "Email",
|
||||
"options": { "credentialsId": "default", "recipients": [] }
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"variables": [],
|
||||
"edges": [
|
||||
{
|
||||
"from": {
|
||||
"blockId": "kSDJqC9TmM25eAM3a2yn3o",
|
||||
"stepId": "phSmjJU2gYq7b11hpima8b"
|
||||
},
|
||||
"to": { "blockId": "b5r2MMyftV1nv9vyr6VkZh" },
|
||||
"id": "vKtpPmbmqgeGC4vwCfPEdv"
|
||||
},
|
||||
{
|
||||
"from": {
|
||||
"blockId": "b5r2MMyftV1nv9vyr6VkZh",
|
||||
"stepId": "svM58drFcdtdJ7DaJCfTLXm"
|
||||
},
|
||||
"to": { "blockId": "6jr7XM9GbVkJ2Ru1WyL45v" },
|
||||
"id": "ioB4s1iRBb8wXiRam8Pp4s"
|
||||
}
|
||||
],
|
||||
"theme": {
|
||||
"chat": {
|
||||
"inputs": {
|
||||
"color": "#303235",
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"placeholderColor": "#9095A0"
|
||||
},
|
||||
"buttons": { "color": "#FFFFFF", "backgroundColor": "#0042DA" },
|
||||
"hostBubbles": { "color": "#303235", "backgroundColor": "#F7F8FF" },
|
||||
"guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" }
|
||||
},
|
||||
"general": { "font": "Open Sans", "background": { "type": "None" } }
|
||||
},
|
||||
"settings": {
|
||||
"general": { "isBrandingEnabled": true },
|
||||
"metadata": {
|
||||
"description": "Build beautiful conversational forms and embed them directly in your applications without a line of code. Triple your response rate and collect answers that has more value compared to a traditional form."
|
||||
},
|
||||
"typingEmulation": { "speed": 300, "enabled": true, "maxDelay": 1.5 }
|
||||
},
|
||||
"publicId": null
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
@ -1,5 +1,6 @@
|
||||
import {
|
||||
Block,
|
||||
CredentialsType,
|
||||
defaultSettings,
|
||||
defaultTheme,
|
||||
PublicBlock,
|
||||
@ -7,8 +8,9 @@ import {
|
||||
Step,
|
||||
Typebot,
|
||||
} from 'models'
|
||||
import { CredentialsType, DashboardFolder, PrismaClient, User } from 'db'
|
||||
import { DashboardFolder, PrismaClient, User } from 'db'
|
||||
import { readFileSync } from 'fs'
|
||||
import { encrypt } from 'utils'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
@ -48,24 +50,27 @@ export const createFolders = (partialFolders: Partial<DashboardFolder>[]) =>
|
||||
})),
|
||||
})
|
||||
|
||||
const createCredentials = () =>
|
||||
prisma.credentials.createMany({
|
||||
const createCredentials = () => {
|
||||
const { encryptedData, iv } = encrypt({
|
||||
expiry_date: 1642441058842,
|
||||
access_token:
|
||||
'ya29.A0ARrdaM--PV_87ebjywDJpXKb77NBFJl16meVUapYdfNv6W6ZzqqC47fNaPaRjbDbOIIcp6f49cMaX5ndK9TAFnKwlVqz3nrK9nLKqgyDIhYsIq47smcAIZkK56SWPx3X3DwAFqRu2UPojpd2upWwo-3uJrod',
|
||||
// This token is linked to a mock Google account (typebot.test.user@gmail.com)
|
||||
refresh_token:
|
||||
'1//0379tIHBxszeXCgYIARAAGAMSNwF-L9Ir0zhkzhblwXqn3_jYqRP3pajcUpqkjRU3fKZZ_eQakOa28amUHSQ-Q9fMzk89MpRTvkc',
|
||||
})
|
||||
return prisma.credentials.createMany({
|
||||
data: [
|
||||
{
|
||||
name: 'test2@gmail.com',
|
||||
ownerId: process.env.PLAYWRIGHT_USER_ID as string,
|
||||
type: CredentialsType.GOOGLE_SHEETS,
|
||||
data: {
|
||||
expiry_date: 1642441058842,
|
||||
access_token:
|
||||
'ya29.A0ARrdaM--PV_87ebjywDJpXKb77NBFJl16meVUapYdfNv6W6ZzqqC47fNaPaRjbDbOIIcp6f49cMaX5ndK9TAFnKwlVqz3nrK9nLKqgyDIhYsIq47smcAIZkK56SWPx3X3DwAFqRu2UPojpd2upWwo-3uJrod',
|
||||
// This token is linked to a mock Google account (typebot.test.user@gmail.com)
|
||||
refresh_token:
|
||||
'1//0379tIHBxszeXCgYIARAAGAMSNwF-L9Ir0zhkzhblwXqn3_jYqRP3pajcUpqkjRU3fKZZ_eQakOa28amUHSQ-Q9fMzk89MpRTvkc',
|
||||
},
|
||||
data: encryptedData,
|
||||
iv,
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
export const updateUser = (data: Partial<User>) =>
|
||||
prisma.user.update({
|
||||
|
80
apps/builder/playwright/tests/integrations/sendEmail.spec.ts
Normal file
80
apps/builder/playwright/tests/integrations/sendEmail.spec.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import test, { expect } from '@playwright/test'
|
||||
import { importTypebotInDatabase } from '../../services/database'
|
||||
import path from 'path'
|
||||
import { generate } from 'short-uuid'
|
||||
import { typebotViewer } from '../../services/selectorUtils'
|
||||
|
||||
const typebotId = generate()
|
||||
|
||||
test.describe('Send email step', () => {
|
||||
test('its configuration should work', async ({ page }) => {
|
||||
if (
|
||||
!process.env.SMTP_USERNAME ||
|
||||
!process.env.SMTP_PORT ||
|
||||
!process.env.SMTP_SECURE ||
|
||||
!process.env.SMTP_HOST ||
|
||||
!process.env.SMTP_PASSWORD
|
||||
)
|
||||
throw new Error('SMTP_ env vars are missing')
|
||||
await importTypebotInDatabase(
|
||||
path.join(
|
||||
__dirname,
|
||||
'../../fixtures/typebots/integrations/sendEmail.json'
|
||||
),
|
||||
{
|
||||
id: typebotId,
|
||||
}
|
||||
)
|
||||
|
||||
await page.goto(`/typebots/${typebotId}/edit`)
|
||||
await page.click('text=Configure...')
|
||||
await page.click(
|
||||
`text=${process.env.NEXT_PUBLIC_EMAIL_NOTIFICATIONS_FROM_EMAIL}`
|
||||
)
|
||||
await page.click('text=Connect new')
|
||||
const createButton = page.locator('button >> text=Create')
|
||||
await expect(createButton).toBeDisabled()
|
||||
await page.fill(
|
||||
'[placeholder="notifications@provider.com"]',
|
||||
process.env.SMTP_USERNAME
|
||||
)
|
||||
await page.fill('[placeholder="John Smith"]', 'John Smith')
|
||||
await page.fill('[placeholder="mail.provider.com"]', process.env.SMTP_HOST)
|
||||
await page.fill(
|
||||
'[placeholder="user@provider.com"]',
|
||||
process.env.SMTP_USERNAME
|
||||
)
|
||||
await page.fill('[type="password"]', process.env.SMTP_PASSWORD)
|
||||
if (process.env.SMTP_SECURE === 'true') await page.click('text=Use TLS?')
|
||||
await page.fill('input[role="spinbutton"]', process.env.SMTP_PORT)
|
||||
await expect(createButton).toBeEnabled()
|
||||
await createButton.click()
|
||||
|
||||
await expect(
|
||||
page.locator(`button >> text=${process.env.SMTP_USERNAME}`)
|
||||
).toBeVisible()
|
||||
|
||||
await page.fill(
|
||||
'[placeholder="email1@gmail.com, email2@gmail.com"]',
|
||||
'email1@gmail.com, email2@gmail.com'
|
||||
)
|
||||
await expect(page.locator('span >> text=email1@gmail.com')).toBeVisible()
|
||||
await expect(page.locator('span >> text=email2@gmail.com')).toBeVisible()
|
||||
|
||||
await page.fill(
|
||||
'[placeholder="email1@gmail.com, email2@gmail.com"]',
|
||||
'email1@gmail.com, email2@gmail.com'
|
||||
)
|
||||
await page.fill('[data-testid="subject-input"]', 'Email subject')
|
||||
await page.fill('[data-testid="body-input"]', 'Here is my email')
|
||||
|
||||
await page.click('text=Preview')
|
||||
await typebotViewer(page).locator('text=Go').click()
|
||||
await page.waitForResponse(
|
||||
(resp) =>
|
||||
resp.request().url().includes('/api/integrations/email') &&
|
||||
resp.status() === 200 &&
|
||||
resp.request().method() === 'POST'
|
||||
)
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user