2
0

feat(editor): Add send email integration

This commit is contained in:
Baptiste Arnaud
2022-02-07 18:06:37 +01:00
parent f4336b83cc
commit d6238b3474
48 changed files with 2119 additions and 2606 deletions

View File

@ -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

View File

@ -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

View File

@ -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({

View 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'
)
})
})