Add e2e tests for account
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Stack, Heading, HStack, Button, Text } from '@chakra-ui/react'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import { useUser } from 'contexts/UserContext'
|
||||
import { Plan } from 'db'
|
||||
import React from 'react'
|
||||
import { SubscriptionTag } from './SubscriptionTag'
|
||||
|
||||
@@ -17,10 +18,13 @@ export const BillingSection = () => {
|
||||
<SubscriptionTag plan={user?.plan} />
|
||||
</HStack>
|
||||
{user?.stripeId && (
|
||||
<Button as={NextChakraLink} href="test">
|
||||
Billing portal
|
||||
<Button as={NextChakraLink} href="/api/stripe/customer-portal">
|
||||
Manage my subscription
|
||||
</Button>
|
||||
)}
|
||||
{user?.plan === Plan.FREE && (
|
||||
<Button colorScheme="blue">Upgrade</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
)
|
||||
|
||||
BIN
apps/builder/cypress/fixtures/avatar.jpg
Normal file
BIN
apps/builder/cypress/fixtures/avatar.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
@@ -1,5 +1,5 @@
|
||||
import { PrismaClient } from '.prisma/client'
|
||||
import { Block, StartBlock, StepType } from 'bot-engine'
|
||||
import { parseNewTypebot } from 'bot-engine'
|
||||
import { Plan, PrismaClient } from 'db'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
@@ -16,7 +16,13 @@ const createUsers = () =>
|
||||
prisma.user.createMany({
|
||||
data: [
|
||||
{ id: 'test1', email: 'test1@gmail.com', emailVerified: new Date() },
|
||||
{ id: 'test2', email: 'test2@gmail.com', emailVerified: new Date() },
|
||||
{
|
||||
id: 'test2',
|
||||
email: 'test2@gmail.com',
|
||||
emailVerified: new Date(),
|
||||
plan: Plan.PRO,
|
||||
stripeId: 'stripe-test2',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
@@ -26,52 +32,23 @@ const createFolders = () =>
|
||||
})
|
||||
|
||||
const createTypebots = () => {
|
||||
const startBlock: StartBlock = {
|
||||
graphCoordinates: { x: 0, y: 0 },
|
||||
id: 'start-block',
|
||||
steps: [
|
||||
{
|
||||
id: 'start-step',
|
||||
blockId: 'start-block',
|
||||
type: StepType.START,
|
||||
label: 'Start',
|
||||
},
|
||||
],
|
||||
title: 'Home',
|
||||
}
|
||||
const blocks: Block[] = [
|
||||
{
|
||||
id: 'block1',
|
||||
title: 'Block1',
|
||||
graphCoordinates: { x: 150, y: 150 },
|
||||
steps: [
|
||||
{ id: 'step1', blockId: 'block1', type: StepType.TEXT, content: '' },
|
||||
{
|
||||
id: 'step2',
|
||||
blockId: 'block1',
|
||||
type: StepType.DATE_PICKER,
|
||||
content: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'block2',
|
||||
title: 'Block2',
|
||||
graphCoordinates: { x: 300, y: 300 },
|
||||
steps: [
|
||||
{ id: 'step1', blockId: 'block2', type: StepType.TEXT, content: '' },
|
||||
],
|
||||
},
|
||||
]
|
||||
return prisma.typebot.createMany({
|
||||
data: [
|
||||
{ id: 'typebot1', name: 'Typebot #1', ownerId: 'test2', startBlock },
|
||||
{
|
||||
...parseNewTypebot({
|
||||
name: 'Typebot #1',
|
||||
ownerId: 'test2',
|
||||
folderId: null,
|
||||
}),
|
||||
id: 'typebot1',
|
||||
},
|
||||
{
|
||||
...parseNewTypebot({
|
||||
name: 'Typebot #2',
|
||||
ownerId: 'test2',
|
||||
folderId: null,
|
||||
}),
|
||||
id: 'typebot2',
|
||||
name: 'Typebot #2',
|
||||
ownerId: 'test2',
|
||||
startBlock,
|
||||
blocks,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
@@ -40,6 +40,7 @@ declare global {
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import '@testing-library/cypress/add-commands'
|
||||
import 'cypress-file-upload'
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
|
||||
67
apps/builder/cypress/tests/account.ts
Normal file
67
apps/builder/cypress/tests/account.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
describe('Dashboard page', () => {
|
||||
before(() => {
|
||||
cy.intercept({
|
||||
url: 'https://s3.eu-west-3.amazonaws.com/typebot',
|
||||
method: 'POST',
|
||||
}).as('postImage')
|
||||
cy.intercept({ url: '/api/auth/session?update', method: 'GET' }).as(
|
||||
'getUpdatedSession'
|
||||
)
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('should edit user info properly', () => {
|
||||
cy.signIn('test1@gmail.com')
|
||||
cy.visit('/account')
|
||||
cy.findByRole('button', { name: 'Save' }).should('not.exist')
|
||||
cy.findByRole('textbox', { name: 'Email address' }).should(
|
||||
'have.attr',
|
||||
'disabled'
|
||||
)
|
||||
cy.findByRole('textbox', { name: 'Name' })
|
||||
.should('have.value', '')
|
||||
.type('John Doe')
|
||||
|
||||
cy.findByRole('img').should('not.have.attr', 'src')
|
||||
cy.findByLabelText('Change photo').attachFile('avatar.jpg')
|
||||
cy.wait('@postImage')
|
||||
cy.findByRole('img')
|
||||
.should('have.attr', 'src')
|
||||
.should(
|
||||
'include',
|
||||
'https://s3.eu-west-3.amazonaws.com/typebot/test1/avatar'
|
||||
)
|
||||
cy.findByRole('button', { name: 'Save' }).should('exist').click()
|
||||
cy.wait('@getUpdatedSession')
|
||||
cy.reload()
|
||||
cy.findByRole('textbox', { name: 'Name' }).should('have.value', 'John Doe')
|
||||
cy.findByRole('img')
|
||||
.should('have.attr', 'src')
|
||||
.should(
|
||||
'include',
|
||||
'https://s3.eu-west-3.amazonaws.com/typebot/test1/avatar'
|
||||
)
|
||||
cy.findByRole('button', { name: 'Save' }).should('not.exist')
|
||||
})
|
||||
|
||||
it('should display valid plans', () => {
|
||||
cy.signIn('test1@gmail.com')
|
||||
cy.visit('/account')
|
||||
cy.findByText('Free plan').should('exist')
|
||||
cy.findByRole('link', { name: 'Manage my subscription' }).should(
|
||||
'not.exist'
|
||||
)
|
||||
cy.findByRole('button', { name: 'Upgrade' }).should('exist')
|
||||
cy.signOut()
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/account')
|
||||
cy.findByText('Pro plan').should('exist')
|
||||
cy.findByRole('link', { name: 'Manage my subscription' })
|
||||
.should('have.attr', 'href')
|
||||
.should('include', 'customer-portal')
|
||||
})
|
||||
})
|
||||
@@ -6,7 +6,7 @@ describe('BoardPage', () => {
|
||||
|
||||
it('steps should be droppable', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot1')
|
||||
cy.visit('/typebots/typebot1/edit')
|
||||
// Can't find an easy way to implement this
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"include": ["**/*.ts"],
|
||||
"exclude": [],
|
||||
"compilerOptions": {
|
||||
"types": ["cypress", "@testing-library/cypress"],
|
||||
"types": ["cypress", "@testing-library/cypress", "cypress-file-upload"],
|
||||
"lib": ["es2015", "dom"],
|
||||
"target": "es5",
|
||||
"isolatedModules": false,
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"@types/testing-library__cypress": "^5.0.9",
|
||||
"@typescript-eslint/eslint-plugin": "^5.8.0",
|
||||
"cypress": "^9.2.0",
|
||||
"cypress-file-upload": "^5.0.8",
|
||||
"cypress-social-logins": "^1.13.0",
|
||||
"eslint": "<8.0.0",
|
||||
"eslint-config-next": "12.0.7",
|
||||
|
||||
32
apps/builder/pages/api/stripe/customer-portal.ts
Normal file
32
apps/builder/pages/api/stripe/customer-portal.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { User } from 'db'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
import { methodNotAllowed } from 'services/api/utils'
|
||||
import Stripe from 'stripe'
|
||||
|
||||
const createCheckoutSession = async (
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) => {
|
||||
const session = await getSession({ req })
|
||||
if (!session?.user)
|
||||
return res.status(401).json({ message: 'Not authenticated' })
|
||||
const user = session.user as User
|
||||
if (!user.stripeId)
|
||||
return res.status(401).json({ message: 'Not authenticated' })
|
||||
if (req.method === 'GET') {
|
||||
if (!process.env.STRIPE_SECRET_KEY)
|
||||
throw Error('STRIPE_SECRET_KEY var is missing')
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||
apiVersion: '2020-08-27',
|
||||
})
|
||||
const session = await stripe.billingPortal.sessions.create({
|
||||
customer: user.stripeId,
|
||||
return_url: `${req.headers.origin}/account`,
|
||||
})
|
||||
res.status(201).redirect(session.url)
|
||||
}
|
||||
return methodNotAllowed(res)
|
||||
}
|
||||
|
||||
export default createCheckoutSession
|
||||
@@ -1,11 +1,5 @@
|
||||
import {
|
||||
BackgroundType,
|
||||
Settings,
|
||||
StartBlock,
|
||||
StepType,
|
||||
Theme,
|
||||
} from 'bot-engine'
|
||||
import { Typebot, User } from 'db'
|
||||
import { parseNewTypebot } from 'bot-engine'
|
||||
import { User } from 'db'
|
||||
import prisma from 'libs/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
@@ -30,35 +24,9 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
return res.send({ typebots })
|
||||
}
|
||||
if (req.method === 'POST') {
|
||||
const data = JSON.parse(req.body) as Typebot
|
||||
const startBlock: StartBlock = {
|
||||
id: 'start-block',
|
||||
title: 'Start',
|
||||
graphCoordinates: { x: 0, y: 0 },
|
||||
steps: [
|
||||
{
|
||||
id: 'start-step',
|
||||
blockId: 'start-block',
|
||||
label: 'Form starts here',
|
||||
type: StepType.START,
|
||||
},
|
||||
],
|
||||
}
|
||||
const theme: Theme = {
|
||||
general: {
|
||||
font: 'Open Sans',
|
||||
background: { type: BackgroundType.NONE, content: '#ffffff' },
|
||||
},
|
||||
}
|
||||
const settings: Settings = {
|
||||
typingEmulation: {
|
||||
enabled: true,
|
||||
speed: 300,
|
||||
maxDelay: 1.5,
|
||||
},
|
||||
}
|
||||
const data = JSON.parse(req.body)
|
||||
const typebot = await prisma.typebot.create({
|
||||
data: { ...data, ownerId: user.id, startBlock, theme, settings },
|
||||
data: parseNewTypebot({ ownerId: user.id, ...data }),
|
||||
})
|
||||
return res.send(typebot)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user