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
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
- name: Install dependencies
run: yarn
- name: Run tests
run: yarn turbo run test --scope=builder
- name: Install Playwright
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:
GOOGLE_REFRESH_TOKEN_TEST: ${{ secrets.GOOGLE_REFRESH_TOKEN_TEST }}
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
if: always()
uses: actions/upload-artifact@v2

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ const config: PlaywrightTestConfig = {
expect: {
timeout: 5000,
},
retries: process.env.CI ? 2 : 0,
retries: 2,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
maxFailures: process.env.CI ? 10 : undefined,
@ -18,6 +18,7 @@ const config: PlaywrightTestConfig = {
trace: 'on-first-retry',
storageState: path.join(__dirname, 'playwright/authenticatedState.json'),
video: 'retain-on-failure',
locale: 'en-US',
},
outputDir: path.join(__dirname, 'playwright/test-results/'),
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 { 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) {
const { baseURL } = config.projects[0].use
if (!baseURL) throw new Error('baseURL is missing')
await teardownDatabase()
await setupDatabase()
// Skip auth if debugging
if (process.env.PWDEBUG === '1') return
const browser = await chromium.launch()
const page = await browser.newPage()
await signIn(page, user.email)
await signIn(page)
await page.context().storageState({
path: './playwright/authenticatedState.json',
})
await setupDatabase(process.env.GITHUB_EMAIL as string)
}
const signIn = async (page: Page, email: string) => {
await page.goto('http://localhost:3000/api/auth/signin')
await page.fill('[placeholder="credentials\\@email\\.com"]', email)
await Promise.all([
page.waitForNavigation({ url: 'http://localhost:3000/typebots' }),
page.press('[placeholder="credentials\\@email\\.com"]', 'Enter'),
])
const signIn = async (page: Page) => {
if (!process.env.GITHUB_EMAIL || !process.env.GITHUB_PASSWORD)
throw new Error(
'GITHUB_USERNAME or GITHUB_PASSWORD are missing in the environment. They are required to log in.'
)
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

View File

@ -5,20 +5,23 @@ import {
Step,
Typebot,
} from 'models'
import { CredentialsType, DashboardFolder, Plan, PrismaClient, User } from 'db'
import { CredentialsType, DashboardFolder, PrismaClient, User } from 'db'
import { readFileSync } from 'fs'
export const user = { id: 'user1', email: 'test1@gmail.com' }
const prisma = new PrismaClient()
export const teardownDatabase = async () => prisma.user.deleteMany()
export const setupDatabase = async () => {
await createUsers()
export const setupDatabase = async (userEmail: string) => {
const createdUser = await getSignedInUser(userEmail)
if (!createdUser) throw new Error("Couldn't find user")
process.env.PLAYWRIGHT_USER_ID = createdUser.id
return createCredentials()
}
const getSignedInUser = (email: string) =>
prisma.user.findFirst({ where: { email } })
export const createTypebots = async (partialTypebots: Partial<Typebot>[]) => {
await prisma.typebot.createMany({
data: partialTypebots.map(parseTestTypebot) as any[],
@ -33,47 +36,37 @@ export const createTypebots = async (partialTypebots: Partial<Typebot>[]) => {
export const createFolders = (partialFolders: Partial<DashboardFolder>[]) =>
prisma.dashboardFolder.createMany({
data: partialFolders.map((folder) => ({
ownerId: user.id,
ownerId: process.env.PLAYWRIGHT_USER_ID as string,
name: 'Folder #1',
id: 'folder',
...folder,
})),
})
const createUsers = () =>
prisma.user.create({
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({
const createCredentials = () =>
prisma.credentials.createMany({
data: [
{
name: 'test2@gmail.com',
ownerId: user.id,
ownerId: process.env.PLAYWRIGHT_USER_ID as string,
type: CredentialsType.GOOGLE_SHEETS,
data: {
expiry_date: 1642441058842,
access_token:
'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>) =>
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 }) => {
await prisma.result.createMany({
@ -129,7 +122,7 @@ export const loadRawTypebotInDatabase = (typebot: Typebot) =>
data: {
...typebot,
id: 'typebot4',
ownerId: user.id,
ownerId: process.env.PLAYWRIGHT_USER_ID,
} as any,
})
@ -137,7 +130,7 @@ const parseTestTypebot = (partialTypebot: Partial<Typebot>): Typebot => ({
id: partialTypebot.id ?? 'typebot',
folderId: null,
name: 'My typebot',
ownerId: user.id,
ownerId: process.env.PLAYWRIGHT_USER_ID as string,
theme: defaultTheme,
settings: defaultSettings,
createdAt: new Date(),
@ -224,7 +217,13 @@ export const importTypebotInDatabase = (
updates?: Partial<Typebot>
) => {
const typebot: Typebot = JSON.parse(readFileSync(path).toString())
return prisma.typebot.create({
data: { ...typebot, ...updates, ownerId: user.id } as any,
})
try {
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 { Plan } from 'db'
import path from 'path'
import { updateUser, user } from '../services/database'
import { updateUser } from '../services/database'
test.describe('Account page', () => {
test('should edit user info properly', async ({ page }) => {
@ -14,17 +14,14 @@ test.describe('Account page', () => {
).toBeDefined()
await page.fill('#name', 'John Doe')
expect(saveButton).toBeVisible()
const avatarImg = page.locator('img')
await expect(page.locator('text=JD')).toBeVisible()
await expect(avatarImg).toBeHidden()
await page.setInputFiles(
'input[type="file"]',
path.join(__dirname, '../fixtures/avatar.jpg')
)
await expect(avatarImg).toHaveAttribute(
await expect(page.locator('img')).toHaveAttribute(
'src',
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'
)
)
@ -43,7 +40,6 @@ test.describe('Account page', () => {
)
await expect(manageSubscriptionButton).toBeHidden()
await updateUser({ plan: Plan.PRO, stripeId: 'stripeId' })
await page.evaluate(refreshUser)
await page.reload()
await expect(page.locator('text=Pro plan')).toBeVisible()
await expect(manageSubscriptionButton).toBeVisible()

View File

@ -16,7 +16,10 @@ test.describe('Condition step', () => {
await page.goto(`/typebots/${typebotId}/edit`)
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("Select an operator")')
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.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("Select an operator")')
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'
test.describe('Set variable step', () => {
test('its configuration should work', async ({ page, context }) => {
test('its configuration should work', async ({ page }) => {
await importTypebotInDatabase(
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.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=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.fill('textarea', '1000 * {{Num}}')
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.fill('textarea', 'Custom value')

View File

@ -41,14 +41,18 @@ test.describe.parallel('Theme page', () => {
test.describe('Chat', () => {
test('should reflect change in real-time', async ({ page }) => {
const typebotId = 'chat-theme-typebot'
await importTypebotInDatabase(
path.join(__dirname, '../fixtures/typebots/theme.json'),
{
id: typebotId,
}
)
try {
await importTypebotInDatabase(
path.join(__dirname, '../fixtures/typebots/theme.json'),
{
id: typebotId,
}
)
} catch {}
await page.goto(`/typebots/${typebotId}/theme`)
await page.click('button:has-text("Chat")')
await page.waitForTimeout(300)
// Host bubbles
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",
"dev": "dotenv -e .env yarn docker:up && dotenv -e .env turbo run dev --parallel",
"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"
},
"devDependencies": {
"dotenv-cli": "^4.1.1",
"turbo": "^1.1.1"
},
"turbo": {
"baseBranch": "origin/main",
"pipeline": {
"build": {
"dependsOn": [
"^build"
],
"outputs": [
".next/**"
]
},
"test": {
"dependsOn": [],
"outputs": []
},
"lint": {
"outputs": []
},
"dev": {
"cache": false
}
}
}
"packageManager": "yarn@1.22.17"
}

View File

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

View File

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

View File

@ -10,14 +10,14 @@
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-node-resolve": "^13.1.3",
"@rollup/plugin-typescript": "^8.3.0",
"rollup": "^2.63.0",
"rollup": "^2.66.1",
"rollup-plugin-dts": "^4.1.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"typescript": "^4.5.4"
"typescript": "^4.5.5"
},
"dependencies": {
"models": "*",
"next": "^12.0.7"
"next": "^12.0.9"
},
"scripts": {
"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