2
0

chore(e2e): 👷 Fix e2e pipeline

This commit is contained in:
Baptiste Arnaud
2022-01-28 17:57:14 +01:00
parent 65209c2638
commit 02bd2b94ba
20 changed files with 967 additions and 911 deletions

View File

@ -37,7 +37,3 @@ STRIPE_WEBHOOK_SECRET=
# (Optional) Used for GIF search # (Optional) Used for GIF search
NEXT_PUBLIC_GIPHY_API_KEY= NEXT_PUBLIC_GIPHY_API_KEY=
# (Optional) for e2e tests
PLAYWRIGHT_BUILDER_TEST_BASE_URL=http://localhost:3000
GOOGLE_REFRESH_TOKEN_TEST=

View File

@ -14,11 +14,24 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install dependencies - name: Install dependencies
run: yarn run: yarn
- name: Run tests - name: Install Playwright
run: yarn turbo run test --scope=builder run: npx playwright install --with-deps
- name: Build db package
working-directory: ./packages/db
run: yarn build
env:
DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
- name: Build models package
working-directory: ./packages/models
run: yarn build
- name: Run tests
working-directory: ./apps/builder
run: yarn test
env: env:
GOOGLE_REFRESH_TOKEN_TEST: ${{ secrets.GOOGLE_REFRESH_TOKEN_TEST }}
PLAYWRIGHT_BUILDER_TEST_BASE_URL: ${{ github.event.deployment_status.target_url }} PLAYWRIGHT_BUILDER_TEST_BASE_URL: ${{ github.event.deployment_status.target_url }}
GITHUB_EMAIL: ${{ secrets.PLAYWRIGHT_GITHUB_EMAIL }}
GITHUB_PASSWORD: ${{ secrets.PLAYWRIGHT_GITHUB_PASSWORD }}
DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
- name: Upload test results - name: Upload test results
if: always() if: always()
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2

View File

@ -15,6 +15,7 @@ import { UploadIcon } from 'assets/icons'
import { UploadButton } from 'components/shared/buttons/UploadButton' import { UploadButton } from 'components/shared/buttons/UploadButton'
import { useUser } from 'contexts/UserContext' import { useUser } from 'contexts/UserContext'
import React, { ChangeEvent, useState } from 'react' import React, { ChangeEvent, useState } from 'react'
import { isDefined } from 'utils'
export const PersonalInfoForm = () => { export const PersonalInfoForm = () => {
const { const {
@ -75,28 +76,29 @@ export const PersonalInfoForm = () => {
onChange={handleNameChange} onChange={handleNameChange}
/> />
</FormControl> </FormControl>
<Tooltip {isDefined(user?.email) && (
label="Can't update the email because it is linked to an OAuth service" <Tooltip
placement="left" label="Updating email is not available."
hasArrow placement="left"
isDisabled={!isOAuthProvider} hasArrow
> >
<FormControl> <FormControl>
<FormLabel <FormLabel
htmlFor="email" htmlFor="email"
color={isOAuthProvider ? 'gray.500' : 'current'} color={isOAuthProvider ? 'gray.500' : 'current'}
> >
Email address Email address
</FormLabel> </FormLabel>
<Input <Input
id="email" id="email"
type="email" type="email"
isDisabled={isOAuthProvider} isDisabled
value={user?.email ?? ''} value={user?.email ?? ''}
onChange={handleEmailChange} onChange={handleEmailChange}
/> />
</FormControl> </FormControl>
</Tooltip> </Tooltip>
)}
{hasUnsavedChanges && ( {hasUnsavedChanges && (
<Flex justifyContent="flex-end"> <Flex justifyContent="flex-end">

View File

@ -64,6 +64,7 @@ export const UserContext = ({ children }: { children: ReactNode }) => {
if (status === 'loading') return if (status === 'loading') return
if (status === 'unauthenticated' && !isSigningIn()) if (status === 'unauthenticated' && !isSigningIn())
router.replace('/signin') router.replace('/signin')
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [status, router]) }, [status, router])
const isSigningIn = () => ['/signin', '/register'].includes(router.pathname) const isSigningIn = () => ['/signin', '/register'].includes(router.pathname)

View File

@ -7,12 +7,12 @@
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",
"test": "yarn playwright test", "test": "dotenv -e ./playwright/.env -- yarn playwright test",
"test:open": "PWDEBUG=1 yarn playwright test" "test:open": "dotenv -e ./playwright/.env -v PWDEBUG=1 -- yarn playwright test"
}, },
"dependencies": { "dependencies": {
"@chakra-ui/css-reset": "^1.1.1", "@chakra-ui/css-reset": "^1.1.1",
"@chakra-ui/react": "^1.7.4", "@chakra-ui/react": "^1.8.1",
"@codemirror/basic-setup": "^0.19.1", "@codemirror/basic-setup": "^0.19.1",
"@codemirror/lang-css": "^0.19.3", "@codemirror/lang-css": "^0.19.3",
"@codemirror/lang-json": "^0.19.1", "@codemirror/lang-json": "^0.19.1",
@ -23,14 +23,14 @@
"@giphy/js-types": "^4.1.0", "@giphy/js-types": "^4.1.0",
"@giphy/react-components": "^5.4.0", "@giphy/react-components": "^5.4.0",
"@googleapis/drive": "^2.1.0", "@googleapis/drive": "^2.1.0",
"@next-auth/prisma-adapter": "next", "@next-auth/prisma-adapter": "1.0.1",
"@udecode/plate-basic-marks": "^9.0.0", "@udecode/plate-basic-marks": "^9.2.1",
"@udecode/plate-common": "^7.0.2", "@udecode/plate-common": "^7.0.2",
"@udecode/plate-core": "^9.0.0", "@udecode/plate-core": "^9.2.1",
"@udecode/plate-link": "^9.0.0", "@udecode/plate-link": "^9.2.1",
"@udecode/plate-ui-link": "^9.0.0", "@udecode/plate-ui-link": "^9.2.1",
"@udecode/plate-ui-toolbar": "^9.0.0", "@udecode/plate-ui-toolbar": "^9.2.1",
"aws-sdk": "^2.1051.0", "aws-sdk": "^2.1065.0",
"bot-engine": "*", "bot-engine": "*",
"browser-image-compression": "^1.0.17", "browser-image-compression": "^1.0.17",
"db": "*", "db": "*",
@ -41,18 +41,18 @@
"google-spreadsheet": "^3.2.0", "google-spreadsheet": "^3.2.0",
"got": "^12.0.1", "got": "^12.0.1",
"htmlparser2": "^7.2.0", "htmlparser2": "^7.2.0",
"immer": "^9.0.7", "immer": "^9.0.12",
"js-video-url-parser": "^0.5.1", "js-video-url-parser": "^0.5.1",
"kbar": "^0.1.0-beta.24", "kbar": "^0.1.0-beta.24",
"micro": "^9.3.4", "micro": "^9.3.4",
"micro-cors": "^0.1.1", "micro-cors": "^0.1.1",
"models": "*", "models": "*",
"next": "^12.0.7", "next": "^12.0.9",
"next-auth": "beta", "next-auth": "4.1.2",
"nodemailer": "^6.7.2", "nodemailer": "^6.7.2",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"papaparse": "^5.3.1", "papaparse": "^5.3.1",
"qs": "^6.10.2", "qs": "^6.10.3",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-frame-component": "^5.2.1", "react-frame-component": "^5.2.1",
@ -61,17 +61,17 @@
"slate": "^0.72.3", "slate": "^0.72.3",
"slate-history": "^0.66.0", "slate-history": "^0.66.0",
"slate-hyperscript": "^0.67.0", "slate-hyperscript": "^0.67.0",
"slate-react": "^0.72.1", "slate-react": "^0.72.7",
"stripe": "^8.195.0", "stripe": "^8.200.0",
"styled-components": "^5.3.3", "styled-components": "^5.3.3",
"svg-round-corners": "^0.3.0", "svg-round-corners": "^0.3.0",
"swr": "^1.1.2", "swr": "^1.2.0",
"use-debounce": "^7.0.1", "use-debounce": "^7.0.1",
"use-immer": "^0.6.0", "use-immer": "^0.6.0",
"utils": "*" "utils": "*"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.18.0", "@playwright/test": "^1.18.1",
"@testing-library/cypress": "^8.0.2", "@testing-library/cypress": "^8.0.2",
"@types/google-spreadsheet": "^3.1.5", "@types/google-spreadsheet": "^3.1.5",
"@types/micro-cors": "^0.1.2", "@types/micro-cors": "^0.1.2",
@ -82,17 +82,15 @@
"@types/react": "^17.0.38", "@types/react": "^17.0.38",
"@types/react-table": "^7.7.9", "@types/react-table": "^7.7.9",
"@types/testing-library__cypress": "^5.0.9", "@types/testing-library__cypress": "^5.0.9",
"@typescript-eslint/eslint-plugin": "^5.9.0", "@typescript-eslint/eslint-plugin": "^5.10.1",
"cypress": "^9.2.0", "dotenv": "^14.3.2",
"cypress-file-upload": "^5.0.8",
"cypress-social-logins": "^1.13.0",
"eslint": "<8.0.0", "eslint": "<8.0.0",
"eslint-config-next": "12.0.7", "eslint-config-next": "12.0.9",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",
"eslint-plugin-cypress": "^2.12.1", "eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"next-transpile-modules": "^9.0.0", "next-transpile-modules": "^9.0.0",
"prettier": "^2.5.1", "prettier": "^2.5.1",
"typescript": "^4.5.4" "typescript": "^4.5.5"
} }
} }

View File

@ -1,13 +1,11 @@
import NextAuth, { NextAuthOptions } from 'next-auth' import NextAuth from 'next-auth'
import { PrismaAdapter } from '@next-auth/prisma-adapter' import { PrismaAdapter } from '@next-auth/prisma-adapter'
import EmailProvider from 'next-auth/providers/email' import EmailProvider from 'next-auth/providers/email'
import GitHubProvider from 'next-auth/providers/github' import GitHubProvider from 'next-auth/providers/github'
import GoogleProvider from 'next-auth/providers/google' import GoogleProvider from 'next-auth/providers/google'
import FacebookProvider from 'next-auth/providers/facebook' import FacebookProvider from 'next-auth/providers/facebook'
import CredentialsProvider from 'next-auth/providers/credentials'
import prisma from 'libs/prisma' import prisma from 'libs/prisma'
import { Provider } from 'next-auth/providers' import { Provider } from 'next-auth/providers'
import { User } from 'db'
import { NextApiRequest, NextApiResponse } from 'next' import { NextApiRequest, NextApiResponse } from 'next'
const providers: Provider[] = [ const providers: Provider[] = [
@ -48,54 +46,18 @@ if (process.env.FACEBOOK_CLIENT_ID && process.env.FACEBOOK_CLIENT_SECRET)
}) })
) )
if (process.env.NODE_ENV !== 'production')
providers.push(
CredentialsProvider({
name: 'Credentials',
credentials: {
email: {
label: 'Email',
type: 'email',
placeholder: 'credentials@email.com',
},
},
async authorize(credentials) {
const user = await prisma.user.findUnique({
where: { email: credentials?.email },
})
return user
},
})
)
const createOptions = (req: NextApiRequest): NextAuthOptions => ({
adapter: PrismaAdapter(prisma),
secret: process.env.SECRET,
providers,
session: {
strategy: process.env.NODE_ENV === 'production' ? 'database' : 'jwt',
},
callbacks: {
jwt: async ({ token, user, account }) => {
if (req.url === '/api/auth/session?update' && token.user) {
token.user = await prisma.user.findUnique({
where: { id: (token.user as User).id },
})
} else if (user) {
token.user = user
}
account?.type && token && (token.providerType = account?.type)
return token
},
session: async ({ session, token, user }) => {
token?.user ? (session.user = token.user as User) : (session.user = user)
if (token?.providerType) session.providerType = token.providerType
return session
},
},
})
const handler = (req: NextApiRequest, res: NextApiResponse) => { const handler = (req: NextApiRequest, res: NextApiResponse) => {
NextAuth(req, res, createOptions(req)) NextAuth(req, res, {
adapter: PrismaAdapter(prisma),
secret: process.env.SECRET,
providers,
session: {
strategy: 'database',
},
callbacks: {
session: async ({ session, user }) => ({ ...session, user }),
},
})
} }
export default handler export default handler

View File

@ -8,7 +8,7 @@ const config: PlaywrightTestConfig = {
expect: { expect: {
timeout: 5000, timeout: 5000,
}, },
retries: process.env.CI ? 2 : 0, retries: 2,
workers: process.env.CI ? 1 : undefined, workers: process.env.CI ? 1 : undefined,
reporter: 'html', reporter: 'html',
maxFailures: process.env.CI ? 10 : undefined, maxFailures: process.env.CI ? 10 : undefined,
@ -18,6 +18,7 @@ const config: PlaywrightTestConfig = {
trace: 'on-first-retry', trace: 'on-first-retry',
storageState: path.join(__dirname, 'playwright/authenticatedState.json'), storageState: path.join(__dirname, 'playwright/authenticatedState.json'),
video: 'retain-on-failure', video: 'retain-on-failure',
locale: 'en-US',
}, },
outputDir: path.join(__dirname, 'playwright/test-results/'), outputDir: path.join(__dirname, 'playwright/test-results/'),
projects: [ projects: [

View File

@ -0,0 +1,5 @@
PLAYWRIGHT_BUILDER_TEST_BASE_URL=http://localhost:3000
# For auth
GITHUB_EMAIL=
GITHUB_PASSWORD=

View File

@ -1,31 +1,43 @@
import { chromium, FullConfig, Page } from '@playwright/test' import { chromium, FullConfig, Page } from '@playwright/test'
import { setupDatabase, teardownDatabase, user } from './services/database' import { setupDatabase, teardownDatabase } from './services/database'
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('dotenv').config({ path: '.env' })
async function globalSetup(config: FullConfig) { async function globalSetup(config: FullConfig) {
const { baseURL } = config.projects[0].use const { baseURL } = config.projects[0].use
if (!baseURL) throw new Error('baseURL is missing') if (!baseURL) throw new Error('baseURL is missing')
await teardownDatabase() await teardownDatabase()
await setupDatabase()
// Skip auth if debugging
if (process.env.PWDEBUG === '1') return
const browser = await chromium.launch() const browser = await chromium.launch()
const page = await browser.newPage() const page = await browser.newPage()
await signIn(page, user.email) await signIn(page)
await page.context().storageState({ await page.context().storageState({
path: './playwright/authenticatedState.json', path: './playwright/authenticatedState.json',
}) })
await setupDatabase(process.env.GITHUB_EMAIL as string)
} }
const signIn = async (page: Page, email: string) => { const signIn = async (page: Page) => {
await page.goto('http://localhost:3000/api/auth/signin') if (!process.env.GITHUB_EMAIL || !process.env.GITHUB_PASSWORD)
await page.fill('[placeholder="credentials\\@email\\.com"]', email) throw new Error(
await Promise.all([ 'GITHUB_USERNAME or GITHUB_PASSWORD are missing in the environment. They are required to log in.'
page.waitForNavigation({ url: 'http://localhost:3000/typebots' }), )
page.press('[placeholder="credentials\\@email\\.com"]', 'Enter'), await page.goto(`${process.env.PLAYWRIGHT_BUILDER_TEST_BASE_URL}/signin`)
]) await page.click('text=Continue with GitHub')
await page.fill('input[name="login"]', process.env.GITHUB_EMAIL)
await page.fill('input[name="password"]', process.env.GITHUB_PASSWORD)
await page.press('input[name="password"]', 'Enter')
try {
await page.locator('text=Authorize baptisteArno').click({ timeout: 3000 })
} catch {
return
}
await page.waitForNavigation({
url: `${process.env.PLAYWRIGHT_BUILDER_TEST_BASE_URL}/typebots`,
})
} }
export default globalSetup export default globalSetup

View File

@ -5,20 +5,23 @@ import {
Step, Step,
Typebot, Typebot,
} from 'models' } from 'models'
import { CredentialsType, DashboardFolder, Plan, PrismaClient, User } from 'db' import { CredentialsType, DashboardFolder, PrismaClient, User } from 'db'
import { readFileSync } from 'fs' import { readFileSync } from 'fs'
export const user = { id: 'user1', email: 'test1@gmail.com' }
const prisma = new PrismaClient() const prisma = new PrismaClient()
export const teardownDatabase = async () => prisma.user.deleteMany() export const teardownDatabase = async () => prisma.user.deleteMany()
export const setupDatabase = async () => { export const setupDatabase = async (userEmail: string) => {
await createUsers() const createdUser = await getSignedInUser(userEmail)
if (!createdUser) throw new Error("Couldn't find user")
process.env.PLAYWRIGHT_USER_ID = createdUser.id
return createCredentials() return createCredentials()
} }
const getSignedInUser = (email: string) =>
prisma.user.findFirst({ where: { email } })
export const createTypebots = async (partialTypebots: Partial<Typebot>[]) => { export const createTypebots = async (partialTypebots: Partial<Typebot>[]) => {
await prisma.typebot.createMany({ await prisma.typebot.createMany({
data: partialTypebots.map(parseTestTypebot) as any[], data: partialTypebots.map(parseTestTypebot) as any[],
@ -33,47 +36,37 @@ export const createTypebots = async (partialTypebots: Partial<Typebot>[]) => {
export const createFolders = (partialFolders: Partial<DashboardFolder>[]) => export const createFolders = (partialFolders: Partial<DashboardFolder>[]) =>
prisma.dashboardFolder.createMany({ prisma.dashboardFolder.createMany({
data: partialFolders.map((folder) => ({ data: partialFolders.map((folder) => ({
ownerId: user.id, ownerId: process.env.PLAYWRIGHT_USER_ID as string,
name: 'Folder #1', name: 'Folder #1',
id: 'folder', id: 'folder',
...folder, ...folder,
})), })),
}) })
const createUsers = () => const createCredentials = () =>
prisma.user.create({ prisma.credentials.createMany({
data: {
...user,
emailVerified: new Date(),
plan: Plan.FREE,
stripeId: 'stripe-test2',
},
})
const createCredentials = () => {
if (!process.env.GOOGLE_REFRESH_TOKEN_TEST)
console.warn(
'GOOGLE_REFRESH_TOKEN_TEST env var is missing. It is required to run Google Sheets tests'
)
return prisma.credentials.createMany({
data: [ data: [
{ {
name: 'test2@gmail.com', name: 'test2@gmail.com',
ownerId: user.id, ownerId: process.env.PLAYWRIGHT_USER_ID as string,
type: CredentialsType.GOOGLE_SHEETS, type: CredentialsType.GOOGLE_SHEETS,
data: { data: {
expiry_date: 1642441058842, expiry_date: 1642441058842,
access_token: access_token:
'ya29.A0ARrdaM--PV_87ebjywDJpXKb77NBFJl16meVUapYdfNv6W6ZzqqC47fNaPaRjbDbOIIcp6f49cMaX5ndK9TAFnKwlVqz3nrK9nLKqgyDIhYsIq47smcAIZkK56SWPx3X3DwAFqRu2UPojpd2upWwo-3uJrod', 'ya29.A0ARrdaM--PV_87ebjywDJpXKb77NBFJl16meVUapYdfNv6W6ZzqqC47fNaPaRjbDbOIIcp6f49cMaX5ndK9TAFnKwlVqz3nrK9nLKqgyDIhYsIq47smcAIZkK56SWPx3X3DwAFqRu2UPojpd2upWwo-3uJrod',
refresh_token: process.env.GOOGLE_REFRESH_TOKEN_TEST, // This token is linked to a mock Google account (typebot.test.user@gmail.com)
refresh_token:
'1//03W5-TyIxXd7nCgYIARAAGAMSNwF-L9IrAGAmp5MG8RqVyk6YYmqDDn9x-4nHTkSUj4xZWuMs6mNeyjdS_bgO0CWuZEfJoAd_zIw',
}, },
}, },
], ],
}) })
}
export const updateUser = (data: Partial<User>) => export const updateUser = (data: Partial<User>) =>
prisma.user.update({ data, where: { id: user.id } }) prisma.user.update({
data,
where: { id: process.env.PLAYWRIGHT_USER_ID as string },
})
export const createResults = async ({ typebotId }: { typebotId: string }) => { export const createResults = async ({ typebotId }: { typebotId: string }) => {
await prisma.result.createMany({ await prisma.result.createMany({
@ -129,7 +122,7 @@ export const loadRawTypebotInDatabase = (typebot: Typebot) =>
data: { data: {
...typebot, ...typebot,
id: 'typebot4', id: 'typebot4',
ownerId: user.id, ownerId: process.env.PLAYWRIGHT_USER_ID,
} as any, } as any,
}) })
@ -137,7 +130,7 @@ const parseTestTypebot = (partialTypebot: Partial<Typebot>): Typebot => ({
id: partialTypebot.id ?? 'typebot', id: partialTypebot.id ?? 'typebot',
folderId: null, folderId: null,
name: 'My typebot', name: 'My typebot',
ownerId: user.id, ownerId: process.env.PLAYWRIGHT_USER_ID as string,
theme: defaultTheme, theme: defaultTheme,
settings: defaultSettings, settings: defaultSettings,
createdAt: new Date(), createdAt: new Date(),
@ -224,7 +217,13 @@ export const importTypebotInDatabase = (
updates?: Partial<Typebot> updates?: Partial<Typebot>
) => { ) => {
const typebot: Typebot = JSON.parse(readFileSync(path).toString()) const typebot: Typebot = JSON.parse(readFileSync(path).toString())
return prisma.typebot.create({ try {
data: { ...typebot, ...updates, ownerId: user.id } as any, return prisma.typebot.create({
}) data: {
...typebot,
...updates,
ownerId: process.env.PLAYWRIGHT_USER_ID,
} as any,
})
} catch {}
} }

View File

@ -2,7 +2,7 @@ import test, { expect } from '@playwright/test'
import { refreshUser } from '../services/browser' import { refreshUser } from '../services/browser'
import { Plan } from 'db' import { Plan } from 'db'
import path from 'path' import path from 'path'
import { updateUser, user } from '../services/database' import { updateUser } from '../services/database'
test.describe('Account page', () => { test.describe('Account page', () => {
test('should edit user info properly', async ({ page }) => { test('should edit user info properly', async ({ page }) => {
@ -14,17 +14,14 @@ test.describe('Account page', () => {
).toBeDefined() ).toBeDefined()
await page.fill('#name', 'John Doe') await page.fill('#name', 'John Doe')
expect(saveButton).toBeVisible() expect(saveButton).toBeVisible()
const avatarImg = page.locator('img')
await expect(page.locator('text=JD')).toBeVisible()
await expect(avatarImg).toBeHidden()
await page.setInputFiles( await page.setInputFiles(
'input[type="file"]', 'input[type="file"]',
path.join(__dirname, '../fixtures/avatar.jpg') path.join(__dirname, '../fixtures/avatar.jpg')
) )
await expect(avatarImg).toHaveAttribute( await expect(page.locator('img')).toHaveAttribute(
'src', 'src',
new RegExp( new RegExp(
`https://s3.eu-west-3.amazonaws.com/typebot/users/${user.id}/avatar`, `https://s3.eu-west-3.amazonaws.com/typebot/users/${process.env.PLAYWRIGHT_USER_ID}/avatar`,
'gm' 'gm'
) )
) )
@ -43,7 +40,6 @@ test.describe('Account page', () => {
) )
await expect(manageSubscriptionButton).toBeHidden() await expect(manageSubscriptionButton).toBeHidden()
await updateUser({ plan: Plan.PRO, stripeId: 'stripeId' }) await updateUser({ plan: Plan.PRO, stripeId: 'stripeId' })
await page.evaluate(refreshUser)
await page.reload() await page.reload()
await expect(page.locator('text=Pro plan')).toBeVisible() await expect(page.locator('text=Pro plan')).toBeVisible()
await expect(manageSubscriptionButton).toBeVisible() await expect(manageSubscriptionButton).toBeVisible()

View File

@ -16,7 +16,10 @@ test.describe('Condition step', () => {
await page.goto(`/typebots/${typebotId}/edit`) await page.goto(`/typebots/${typebotId}/edit`)
await page.click('text=Configure...') await page.click('text=Configure...')
await page.fill('input[placeholder="Search for a variable"]', 'Age') await page.fill(
'input[placeholder="Search for a variable"] >> nth=-1',
'Age'
)
await page.click('button:has-text("Age")') await page.click('button:has-text("Age")')
await page.click('button:has-text("Select an operator")') await page.click('button:has-text("Select an operator")')
await page.click('button:has-text("Greater than")', { force: true }) await page.click('button:has-text("Greater than")', { force: true })
@ -37,7 +40,10 @@ test.describe('Condition step', () => {
) )
await page.click('text=Configure...') await page.click('text=Configure...')
await page.fill('input[placeholder="Search for a variable"]', 'Age') await page.fill(
'input[placeholder="Search for a variable"] >> nth=-1',
'Age'
)
await page.click('button:has-text("Age")') await page.click('button:has-text("Age")')
await page.click('button:has-text("Select an operator")') await page.click('button:has-text("Select an operator")')
await page.click('button:has-text("Greater than")', { force: true }) await page.click('button:has-text("Greater than")', { force: true })

View File

@ -6,7 +6,7 @@ import { importTypebotInDatabase } from '../../services/database'
const typebotId = 'set-variable-step' const typebotId = 'set-variable-step'
test.describe('Set variable step', () => { test.describe('Set variable step', () => {
test('its configuration should work', async ({ page, context }) => { test('its configuration should work', async ({ page }) => {
await importTypebotInDatabase( await importTypebotInDatabase(
path.join(__dirname, '../../fixtures/typebots/logic/setVariable.json'), path.join(__dirname, '../../fixtures/typebots/logic/setVariable.json'),
{ {
@ -16,16 +16,19 @@ test.describe('Set variable step', () => {
await page.goto(`/typebots/${typebotId}/edit`) await page.goto(`/typebots/${typebotId}/edit`)
await page.click('text=Type a number...') await page.click('text=Type a number...')
await page.fill('input[placeholder="Select a variable"]', 'Num') await page.fill('input[placeholder="Select a variable"] >> nth=-1', 'Num')
await page.click('text=Create "Num"') await page.click('text=Create "Num"')
await page.click('text=Click to edit... >> nth = 0') await page.click('text=Click to edit... >> nth = 0')
await page.fill('input[placeholder="Select a variable"]', 'Total') await page.fill('input[placeholder="Select a variable"] >> nth=-1', 'Total')
await page.click('text=Create "Total"') await page.click('text=Create "Total"')
await page.fill('textarea', '1000 * {{Num}}') await page.fill('textarea', '1000 * {{Num}}')
await page.click('text=Click to edit...') await page.click('text=Click to edit...')
await page.fill('input[placeholder="Select a variable"]', 'Custom var') await page.fill(
'input[placeholder="Select a variable"] >> nth=-1',
'Custom var'
)
await page.click('text=Create "Custom var"') await page.click('text=Create "Custom var"')
await page.fill('textarea', 'Custom value') await page.fill('textarea', 'Custom value')

View File

@ -41,14 +41,18 @@ test.describe.parallel('Theme page', () => {
test.describe('Chat', () => { test.describe('Chat', () => {
test('should reflect change in real-time', async ({ page }) => { test('should reflect change in real-time', async ({ page }) => {
const typebotId = 'chat-theme-typebot' const typebotId = 'chat-theme-typebot'
await importTypebotInDatabase( try {
path.join(__dirname, '../fixtures/typebots/theme.json'), await importTypebotInDatabase(
{ path.join(__dirname, '../fixtures/typebots/theme.json'),
id: typebotId, {
} id: typebotId,
) }
)
} catch {}
await page.goto(`/typebots/${typebotId}/theme`) await page.goto(`/typebots/${typebotId}/theme`)
await page.click('button:has-text("Chat")') await page.click('button:has-text("Chat")')
await page.waitForTimeout(300)
// Host bubbles // Host bubbles
await page.click(':nth-match([aria-label="Pick a color"], 1)') await page.click(':nth-match([aria-label="Pick a color"], 1)')

View File

@ -11,34 +11,12 @@
"db:nuke": "docker-compose down --volumes --remove-orphans", "db:nuke": "docker-compose down --volumes --remove-orphans",
"dev": "dotenv -e .env yarn docker:up && dotenv -e .env turbo run dev --parallel", "dev": "dotenv -e .env yarn docker:up && dotenv -e .env turbo run dev --parallel",
"build": "dotenv -e .env turbo run build", "build": "dotenv -e .env turbo run build",
"test": "dotenv -e .env turbo run test", "test:builder": "cd apps/builder && yarn test",
"lint": "turbo run lint" "lint": "turbo run lint"
}, },
"devDependencies": { "devDependencies": {
"dotenv-cli": "^4.1.1", "dotenv-cli": "^4.1.1",
"turbo": "^1.1.1" "turbo": "^1.1.1"
}, },
"turbo": { "packageManager": "yarn@1.22.17"
"baseBranch": "origin/main",
"pipeline": {
"build": {
"dependsOn": [
"^build"
],
"outputs": [
".next/**"
]
},
"test": {
"dependsOn": [],
"outputs": []
},
"lint": {
"outputs": []
},
"dev": {
"cache": false
}
}
}
} }

View File

@ -11,7 +11,7 @@
"models": "*", "models": "*",
"qs": "^6.10.3", "qs": "^6.10.3",
"react-frame-component": "5.2.2-alpha.0", "react-frame-component": "5.2.2-alpha.0",
"react-phone-number-input": "^3.1.44", "react-phone-number-input": "^3.1.45",
"react-scroll": "^1.8.4", "react-scroll": "^1.8.4",
"react-transition-group": "^4.4.2", "react-transition-group": "^4.4.2",
"utils": "*" "utils": "*"
@ -25,19 +25,19 @@
"@types/react-phone-number-input": "^3.0.13", "@types/react-phone-number-input": "^3.0.13",
"@types/react-scroll": "^1.8.3", "@types/react-scroll": "^1.8.3",
"@types/react-transition-group": "^4.4.4", "@types/react-transition-group": "^4.4.4",
"autoprefixer": "^10.4.1", "autoprefixer": "^10.4.2",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"postcss": "^8.4.5", "postcss": "^8.4.5",
"rollup": "^2.63.0", "rollup": "^2.66.1",
"rollup-plugin-dts": "^4.1.0", "rollup-plugin-dts": "^4.1.0",
"rollup-plugin-peer-deps-external": "^2.2.4", "rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.2", "rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"tailwindcss": "^3.0.11", "tailwindcss": "^3.0.17",
"typescript": "^4.5.4", "typescript": "^4.5.5",
"@typescript-eslint/eslint-plugin": "^5.9.0", "@typescript-eslint/eslint-plugin": "^5.10.1",
"eslint": "<8.0.0", "eslint": "<8.0.0",
"eslint-config-next": "12.0.7", "eslint-config-next": "12.0.9",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",
"eslint-plugin-cypress": "^2.12.1", "eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-prettier": "^4.0.0" "eslint-plugin-prettier": "^4.0.0"

View File

@ -6,10 +6,10 @@
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"typescript": "^4.5.4" "typescript": "^4.5.5"
}, },
"dependencies": { "dependencies": {
"next": "^12.0.7", "next": "^12.0.9",
"db": "*" "db": "*"
}, },
"scripts": { "scripts": {

View File

@ -10,14 +10,14 @@
"@rollup/plugin-commonjs": "^21.0.1", "@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-node-resolve": "^13.1.3", "@rollup/plugin-node-resolve": "^13.1.3",
"@rollup/plugin-typescript": "^8.3.0", "@rollup/plugin-typescript": "^8.3.0",
"rollup": "^2.63.0", "rollup": "^2.66.1",
"rollup-plugin-dts": "^4.1.0", "rollup-plugin-dts": "^4.1.0",
"rollup-plugin-peer-deps-external": "^2.2.4", "rollup-plugin-peer-deps-external": "^2.2.4",
"typescript": "^4.5.4" "typescript": "^4.5.5"
}, },
"dependencies": { "dependencies": {
"models": "*", "models": "*",
"next": "^12.0.7" "next": "^12.0.9"
}, },
"scripts": { "scripts": {
"build": "yarn rollup -c", "build": "yarn rollup -c",

23
turbo.json Normal file
View File

@ -0,0 +1,23 @@
{
"baseBranch": "origin/main",
"pipeline": {
"build": {
"dependsOn": [
"^build"
],
"outputs": [
".next/**"
]
},
"test": {
"dependsOn": [],
"outputs": []
},
"lint": {
"outputs": []
},
"dev": {
"cache": false
}
}
}

1471
yarn.lock

File diff suppressed because it is too large Load Diff