Compare commits

...

8 Commits

Author SHA1 Message Date
Matti Nannt
f5110fe9c1 fix: docker build errors (#1762) 2023-12-07 22:24:22 +00:00
Matti Nannt
8244a5fa48 fix: update next-config remotePatterns to work with http (#1761) 2023-12-07 14:37:03 +00:00
Dhruwang Jariwala
59936e54a0 feat: add playwright e2e tests infrastructure (#1742)
Co-authored-by: ShubhamPalriwala <spalriwalau@gmail.com>
2023-12-07 10:51:45 +00:00
Dhruwang Jariwala
5468287f9b fix: Survey editor caution text fix (#1755)
Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
2023-12-07 09:40:48 +00:00
Anjy Gupta
22e55677ae fix: Gitpod file upload and background overlay (#1756) 2023-12-07 09:28:54 +00:00
Filip Wojda
8d422eeda0 style: tiny BestPractice component styling fix (#1758) 2023-12-07 09:22:26 +00:00
Anshuman Pandey
62dbd9e121 fix: fixes safari in-app preview (#1759) 2023-12-07 09:19:17 +00:00
Shubham Palriwala
c950c96934 fix: code block formatting in self-hosting/configure page (#1750) 2023-12-06 13:30:37 +00:00
18 changed files with 365 additions and 63 deletions

37
.github/workflows/playwright.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: E2E Tests
on:
workflow_call:
jobs:
build:
name: Run E2E Tests
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20
- name: Install Docker Compose
run: sudo apt-get update && sudo apt-get install -y docker-compose
- name: Install dependencies
run: npm install -g pnpm && pnpm install
- name: Build Formbricks Image & Run
run: docker-compose up -d
- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps
- name: Run Playwright tests
run: pnpm test:e2e
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30

View File

@@ -27,8 +27,13 @@ jobs:
uses: ./.github/workflows/build-web.yml
secrets: inherit
e2e-test:
name: Run E2E Tests
uses: ./.github/workflows/playwright.yml
secrets: inherit
required:
needs: [lint, test, build]
needs: [lint, test, build, e2e-test]
if: always()
runs-on: ubuntu-latest
steps:

View File

@@ -52,19 +52,22 @@ jobs:
with:
cosign-release: "v2.1.1"
# Add support for more platforms with QEMU (optional)
# https://github.com/docker/setup-qemu-action
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
# Set up BuildKit Docker container builder to be able to build
# multi-platform images and export cache
# https://github.com/docker/setup-buildx-action
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
with:
platforms: linux/amd64,linux/arm64
uses: docker/setup-buildx-action@v3 # v3.0.0
# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
uses: docker/login-action@v3 # v3.0.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
@@ -74,7 +77,7 @@ jobs:
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
uses: docker/metadata-action@v5 # v5.0.0
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
@@ -82,10 +85,11 @@ jobs:
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
uses: docker/build-push-action@v5 # v5.0.0
with:
context: .
file: ./apps/web/Dockerfile
# platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

8
.gitignore vendored
View File

@@ -44,4 +44,10 @@ packages/database/zod
# nixos stuff
.direnv
Zone.Identifier
Zone.Identifier
# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/

View File

@@ -39,17 +39,24 @@ Integrating Google OAuth with your Formbricks instance allows users to log in us
- Select the application type **Web application** for your project and enter any additional information required.
- Ensure to specify authorized JavaScript origins and authorized redirect URIs.
```
<Col>
<CodeGroup title="Configuration URLs">
``` {{ title: 'Redirect & Origin URLs' }}
Authorized JavaScript origins: {WEBAPP_URL}
Authorized redirect URIs: {WEBAPP_URL}/api/auth/callback/google
```
</CodeGroup>
</Col>
5. **Update Environment Variables in Docker**:
- To integrate the Google OAuth, you have two options: either update the environment variables in the docker-compose file or directly add them to the running container.
- In your Docker setup directory, open the `.env` file, and add or update the following lines with the `Client ID` and `Client Secret` obtained from Google Cloud Platform:
- Alternatively, you can add the environment variables directly to the running container using the following commands (replace `container_id` with your actual Docker container ID):
```
<Col>
<CodeGroup title="Set env vars">
```sh {{ title: 'Shell commands' }}
docker exec -it container_id /bin/bash
export GOOGLE_AUTH_ENABLED=1
export GOOGLE_CLIENT_ID=your-client-id-here
@@ -57,12 +64,15 @@ export GOOGLE_CLIENT_SECRET=your-client-secret-here
exit
```
```
```sh {{ title: 'env file' }}
GOOGLE_AUTH_ENABLED=1
GOOGLE_CLIENT_ID=your-client-id-here
GOOGLE_CLIENT_SECRET=your-client-secret-here
```
</CodeGroup>
</Col>
6. **Restart Your Formbricks Instance**:
- **Note:** Restarting your Docker containers may cause a brief period of downtime. Plan accordingly.
- Once the environment variables have been updated, it's crucial to restart your Docker containers to apply the changes. This ensures that your Formbricks instance can utilize the new Google OAuth configuration for user authentication. Here's how you can do it:

View File

@@ -83,8 +83,8 @@ export default function BestPracticeNavigation() {
return (
<div className="mx-auto grid grid-cols-1 gap-6 px-2 md:grid-cols-3">
{BestPractices.map((bestPractice) => (
<Link href={bestPractice.href} key={bestPractice.name}>
<div className="drop-shadow-card duration-120 hover:border-brand-dark relative rounded-lg border border-slate-100 bg-slate-100 p-6 transition-all ease-in-out hover:scale-105 hover:cursor-pointer dark:border-slate-600 dark:bg-slate-800">
<Link className="relative block" href={bestPractice.href} key={bestPractice.name}>
<div className="drop-shadow-card duration-120 hover:border-brand-dark relative h-full rounded-lg border border-slate-100 bg-slate-100 p-6 transition-all ease-in-out hover:scale-105 hover:cursor-pointer dark:border-slate-600 dark:bg-slate-800">
<div
className={clsx(
// base styles independent what type of button it is
@@ -105,7 +105,9 @@ export default function BestPracticeNavigation() {
<h3 className="mb-1 mt-3 text-xl font-bold text-slate-700 dark:text-slate-200">
{bestPractice.name}
</h3>
<p className="text-sm text-slate-600 dark:text-slate-400">{bestPractice.description}</p>
<p className="flex self-end text-sm text-slate-600 dark:text-slate-400">
{bestPractice.description}
</p>
</div>
</Link>
))}

View File

@@ -1,13 +1,19 @@
# Installer stage: Building the application
FROM node:20-alpine AS installer
# Enable corepack and prepare pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Install necessary build tools and compilers
RUN apk update && apk add --no-cache g++ cmake make gcc python3 openssl-dev
# Install Supercronic (cron for containers without super user privileges)
RUN apk add --no-cache curl \
&& curl -fsSLo /tmp/supercronic \
"https://github.com/aptible/supercronic/releases/download/v0.2.27/supercronic-linux-amd64" \
&& chmod +x /tmp/supercronic
# Set environment variables
ARG DATABASE_URL
ENV DATABASE_URL=$DATABASE_URL
@@ -17,13 +23,19 @@ ENV NEXTAUTH_SECRET=$NEXTAUTH_SECRET
ARG ENCRYPTION_KEY
ENV ENCRYPTION_KEY=$ENCRYPTION_KEY
# Set the working directory
WORKDIR /app
# Copy the application files
COPY . .
# Create a .env file
RUN touch /app/apps/web/.env
# Install the dependencies
RUN pnpm install
# Build the project
RUN pnpm post-install --filter=web...
RUN pnpm turbo run build --filter=web...

View File

@@ -16,6 +16,7 @@ import toast from "react-hot-toast";
import { validateQuestion } from "./Validation";
import { deleteSurveyAction, updateSurveyAction } from "../actions";
import SurveyStatusDropdown from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/SurveyStatusDropdown";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@formbricks/ui/Tooltip";
interface SurveyMenuBarProps {
localSurvey: TSurvey;
@@ -48,6 +49,7 @@ export default function SurveyMenuBar({
const [isSurveySaving, setIsSurveySaving] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const cautionText = "This survey received responses, make changes with caution.";
let faultyQuestions: String[] = [];
@@ -295,11 +297,20 @@ export default function SurveyMenuBar({
/>
</div>
{responseCount > 0 && (
<div className="mx-auto flex items-center rounded-full border border-amber-200 bg-amber-100 p-2 text-amber-700 shadow-sm">
<ExclamationTriangleIcon className=" h-5 w-5 text-amber-400" />
<p className=" pl-1 text-xs lg:text-sm">
This survey received responses, make changes with caution.
</p>
<div className="ju flex items-center rounded-lg border border-amber-200 bg-amber-100 p-2 text-amber-700 shadow-sm lg:mx-auto">
<TooltipProvider delayDuration={50}>
<Tooltip>
<TooltipTrigger>
<ExclamationTriangleIcon className=" h-5 w-5 text-amber-400" />
</TooltipTrigger>
<TooltipContent side={"top"} className="lg:hidden">
<p className="py-2 text-center text-xs text-slate-500 dark:text-slate-400 ">
{cautionText}
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<p className=" hidden pl-1 text-xs md:text-sm lg:block">{cautionText}</p>
</div>
)}
<div className="mt-3 flex sm:ml-4 sm:mt-0">

View File

@@ -56,7 +56,7 @@ export default function Modal({
ref={modalRef}
style={highlightBorderColorStyle}
className={cn(
"pointer-events-auto absolute h-fit max-h-[90%] w-full max-w-sm overflow-y-auto rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-500 ease-in-out ",
"pointer-events-auto absolute h-auto max-h-[90%] w-full max-w-sm overflow-y-auto rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-500 ease-in-out ",
previewMode === "desktop" ? getPlacementStyle(placement) : "max-w-full ",
slidingAnimationClass
)}>

View File

@@ -20,8 +20,8 @@ export const MediaBackground: React.FC<MediaBackgroundProps> = ({
}) => {
const getFilterStyle = () => {
return survey.styling?.background?.brightness
? `brightness-${survey.styling?.background?.brightness}`
: "";
? `brightness(${survey.styling?.background?.brightness}%)`
: "brightness(100%)";
};
const renderBackground = () => {
@@ -32,21 +32,26 @@ export const MediaBackground: React.FC<MediaBackgroundProps> = ({
case "color":
return (
<div
className={`${baseClasses} ${filterStyle}`}
style={{ backgroundColor: survey.styling?.background?.bg || "#ffff" }}
className={`${baseClasses}`}
style={{ backgroundColor: survey.styling?.background?.bg || "#ffff", filter: `${filterStyle}` }}
/>
);
case "animation":
return (
<video muted loop autoPlay className={`${baseClasses} object-cover ${filterStyle}`}>
<video
muted
loop
autoPlay
className={`${baseClasses} object-cover`}
style={{ filter: `${filterStyle}` }}>
<source src={survey.styling?.background?.bg || ""} type="video/mp4" />
</video>
);
case "image":
return (
<div
className={`${baseClasses} bg-cover bg-center ${filterStyle}`}
style={{ backgroundImage: `url(${survey.styling?.background?.bg})` }}
className={`${baseClasses} bg-cover bg-center`}
style={{ backgroundImage: `url(${survey.styling?.background?.bg})`, filter: `${filterStyle}` }}
/>
);
default:

View File

@@ -4,6 +4,11 @@ import "@formbricks/lib/env.mjs";
/** @type {import('next').NextConfig} */
function getHostname(url) {
const urlObj = new URL(url);
return urlObj.hostname;
}
const nextConfig = {
assetPrefix: process.env.ASSET_PREFIX_URL || undefined,
output: "standalone",
@@ -107,6 +112,10 @@ if (process.env.WEBAPP_URL) {
nextConfig.experimental.serverActions = {
allowedOrigins: [process.env.WEBAPP_URL.replace(/https?:\/\//, "")],
};
nextConfig.images.remotePatterns.push({
protocol: "https",
hostname: getHostname(process.env.WEBAPP_URL),
});
}
const sentryOptions = {

View File

@@ -0,0 +1,17 @@
import { randomBytes } from "crypto";
let user: {
name: string;
email: string;
password: string;
} | null;
export const getUser = () => {
if (!user) {
const name = randomBytes(4).toString("hex");
const email = `${name}@gmail.com`;
const password = "Test@123";
user = { name, email, password };
}
return user;
};

View File

@@ -0,0 +1,95 @@
import { getUser } from "@/playwright/lib/user";
import { test } from "@playwright/test";
const { name, email, password } = getUser();
test.describe("Signup Flow Test", async () => {
test.describe.configure({ mode: "serial" });
test("Valid User", async ({ page }) => {
await page.goto("/auth/signup");
await page.getByText("Continue with Email").click();
await page.waitForSelector('input[name="name"]');
await page.fill('input[name="name"]', name);
await page.press('input[name="name"]', "Tab");
await page.fill('input[name="email"]', email);
await page.press('input[name="email"]', "Tab");
await page.fill('input[name="password"]', password);
await page.press('input[name="password"]', "Enter");
await page.waitForURL("/auth/signup-without-verification-success");
});
test("Email is taken", async ({ page }) => {
await page.goto("/auth/signup");
await page.getByText("Continue with Email").click();
await page.waitForSelector('input[name="name"]');
await page.fill('input[name="name"]', name);
await page.press('input[name="name"]', "Tab");
await page.fill('input[name="email"]', email);
await page.press('input[name="email"]', "Tab");
await page.fill('input[name="password"]', password);
await page.press('input[name="password"]', "Enter");
let alertMessage = "user with this email address already exists";
await (await page.waitForSelector(`text=${alertMessage}`)).isVisible();
});
test("No Name", async ({ page }) => {
await page.goto("/auth/signup");
await page.getByText("Continue with Email").click();
await page.waitForSelector('input[name="name"]');
await page.fill('input[name="name"]', "");
await page.press('input[name="name"]', "Tab");
await page.fill('input[name="email"]', email);
await page.press('input[name="email"]', "Tab");
await page.fill('input[name="password"]', password);
await page.press('input[name="password"]', "Enter");
await page.getByText("Continue with Email").isDisabled();
});
test("Invalid Email", async ({ page }) => {
await page.goto("/auth/signup");
await page.getByText("Continue with Email").click();
await page.waitForSelector('input[name="name"]');
await page.fill('input[name="name"]', name);
await page.press('input[name="name"]', "Tab");
await page.fill('input[name="email"]', "invalid");
await page.press('input[name="email"]', "Tab");
await page.fill('input[name="password"]', password);
await page.press('input[name="password"]', "Enter");
await page.getByText("Continue with Email").isDisabled();
});
test("Invalid Password", async ({ page }) => {
await page.goto("/auth/signup");
await page.getByText("Continue with Email").click();
await page.waitForSelector('input[name="name"]');
await page.fill('input[name="name"]', name);
await page.press('input[name="name"]', "Tab");
await page.fill('input[name="email"]', email);
await page.press('input[name="email"]', "Tab");
await page.fill('input[name="password"]', "invalid");
await page.press('input[name="password"]', "Enter");
await page.getByText("Continue with Email").isDisabled();
});
});

View File

@@ -24,18 +24,15 @@ x-encryption-key: &encryption_key 1b3d888592454d23b520040950654669
x-mail-from: &mail_from
x-smtp-host: &smtp_host
x-smtp-port: &smtp_port # Enable SMTP_SECURE_ENABLED for TLS (port 465)
x-smtp-port: &smtp_port
x-smtp-secure-enabled: &smtp_secure_enabled # Enable SMTP_SECURE_ENABLED for TLS (port 465)
x-smtp-secure-enabled: &smtp_secure_enabled
x-smtp-user: &smtp_user
x-smtp-password: &smtp_password
# Set the below value to your public-facing URL, e.g., https://example.com
# Set the below value if you have and want to share a shorter base URL than the x-survey-base-url
x-short-url-base:
&short_url_base # Email Verification. If you enable Email Verification you have to setup SMTP-Settings, too.
&short_url_base # Set the below value if you have and want to share a shorter base URL than the x-survey-base-url
x-email-verification-disabled: &email_verification_disabled 1
@@ -52,23 +49,21 @@ x-invite-disabled: &invite_disabled 0
# Set the below values to display privacy policy, imprint and terms of service links in the footer of signup & public pages.
x-privacy-url: &privacy_url
x-terms-url: &terms_url
x-imprint-url: &imprint_url # Configure Github Login
x-imprint-url: &imprint_url
x-github-auth-enabled: &github_auth_enabled 0
x-github-auth-enabled: &github_auth_enabled 0 # Configure Github Login
x-github-id: &github_id
x-github-secret: &github_secret # Configure Google Login
x-github-secret: &github_secret
x-google-auth-enabled: &google_auth_enabled 0
x-google-auth-enabled: &google_auth_enabled 0 # Configure Google Login
x-google-client-id: &google_client_id
x-google-client-secret: &google_client_secret # Disable Sentry warning
x-google-client-secret: &google_client_secret
x-sentry-ignore-api-resolution-error: &sentry_ignore_api_resolution_error # Enable Sentry Error Tracking
x-sentry-ignore-api-resolution-error: &sentry_ignore_api_resolution_error # Disable Sentry warning
x-next-public-sentry-dsn: &next_public_sentry_dsn # Cron Secret
x-next-public-sentry-dsn: &next_public_sentry_dsn # Enable Sentry Error Tracking
# Set this to a random string to secure your cron endpoints
x-cron-secret: &cron_secret YOUR_CRON_SECRET
x-cron-secret: &cron_secret YOUR_CRON_SECRET # Set this to a random string to secure your cron endpoints
services:
postgres:

View File

@@ -26,10 +26,12 @@
"lint": "turbo run lint",
"release": "turbo run build --filter=js... && turbo run build --filter=n8n-node... && changeset publish",
"test": "turbo run test",
"test:e2e": "playwright test",
"prepare": "husky install"
},
"devDependencies": {
"@changesets/cli": "^2.26.2",
"@playwright/test": "^1.40.1",
"eslint-config-formbricks": "workspace:*",
"husky": "^8.0.3",
"lint-staged": "^15.1.0",
@@ -58,5 +60,8 @@
"budgetPercentIncreaseRed": 20,
"minimumChangeThreshold": 0,
"showDetails": true
},
"dependencies": {
"playwright": "^1.40.1"
}
}

68
playwright.config.ts Normal file
View File

@@ -0,0 +1,68 @@
import { defineConfig, devices } from "@playwright/test";
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: "./apps/web/playwright",
/* Run tests in files in parallel */
fullyParallel: true,
/* Retry on CI only */
retries: 2,
/* Opt out of parallel tests on CI. */
workers: 1,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "http://localhost:3000",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
},
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
// {
// name: "firefox",
// use: { ...devices["Desktop Firefox"] },
// },
// {
// name: "webkit",
// use: { ...devices["Desktop Safari"] },
// },
/* Test against mobile viewports. */
// {
// name: "Mobile Chrome",
// use: { ...devices["Pixel 5"] },
// },
// {
// name: "Mobile Safari",
// use: { ...devices["iPhone 12"] },
// },
/* Test against branded browsers. */
// {
// name: "Microsoft Edge",
// use: { ...devices["Desktop Edge"], channel: "msedge" },
// },
// {
// name: "Google Chrome",
// use: { ...devices["Desktop Chrome"], channel: "chrome" },
// },
],
});

58
pnpm-lock.yaml generated
View File

@@ -7,10 +7,17 @@ settings:
importers:
.:
dependencies:
playwright:
specifier: ^1.40.1
version: 1.40.1
devDependencies:
'@changesets/cli':
specifier: ^2.26.2
version: 2.26.2
'@playwright/test':
specifier: ^1.40.1
version: 1.40.1
eslint-config-formbricks:
specifier: workspace:*
version: link:packages/eslint-config-formbricks
@@ -5147,6 +5154,14 @@ packages:
requiresBuild: true
optional: true
/@playwright/test@1.40.1:
resolution: {integrity: sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==}
engines: {node: '>=16'}
hasBin: true
dependencies:
playwright: 1.40.1
dev: true
/@preact/preset-vite@2.7.0(@babel/core@7.23.5)(preact@10.19.2)(vite@5.0.6):
resolution: {integrity: sha512-m5N0FVtxbCCDxNk55NGhsRpKJChYcupcuQHzMJc/Bll07IKZKn8amwYciyKFS9haU6AgzDAJ/ewvApr6Qg1DHw==}
peerDependencies:
@@ -8808,7 +8823,7 @@ packages:
/@types/jsonwebtoken@9.0.5:
resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==}
dependencies:
'@types/node': 20.9.0
'@types/node': 20.10.3
dev: true
/@types/keyv@3.1.4:
@@ -8891,18 +8906,6 @@ packages:
dependencies:
undici-types: 5.26.5
/@types/node@20.8.6:
resolution: {integrity: sha512-eWO4K2Ji70QzKUqRy6oyJWUeB7+g2cRagT3T/nxYibYcT4y2BDL8lqolRXjTHmkZCdJfIPaY73KbJAZmcryxTQ==}
dependencies:
undici-types: 5.25.3
dev: true
/@types/node@20.9.0:
resolution: {integrity: sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==}
dependencies:
undici-types: 5.26.5
dev: true
/@types/normalize-package-data@2.4.3:
resolution: {integrity: sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==}
@@ -8919,7 +8922,7 @@ packages:
/@types/qrcode@1.5.5:
resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
dependencies:
'@types/node': 20.8.6
'@types/node': 20.10.3
dev: true
/@types/qs@6.9.9:
@@ -12804,6 +12807,13 @@ packages:
/fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
/fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
optional: true
/fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -14359,7 +14369,7 @@ packages:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/jsdom': 20.0.1
'@types/node': 20.8.6
'@types/node': 20.10.3
jest-mock: 29.7.0
jest-util: 29.7.0
jsdom: 20.0.3
@@ -17174,6 +17184,20 @@ packages:
dependencies:
find-up: 5.0.0
/playwright-core@1.40.1:
resolution: {integrity: sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==}
engines: {node: '>=16'}
hasBin: true
/playwright@1.40.1:
resolution: {integrity: sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==}
engines: {node: '>=16'}
hasBin: true
dependencies:
playwright-core: 1.40.1
optionalDependencies:
fsevents: 2.3.2
/pngjs@5.0.0:
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
engines: {node: '>=10.13.0'}
@@ -20133,10 +20157,6 @@ packages:
which-boxed-primitive: 1.0.2
dev: true
/undici-types@5.25.3:
resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==}
dev: true
/undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}

View File

@@ -119,7 +119,8 @@
"S3_SECRET_KEY",
"S3_REGION",
"S3_BUCKET_NAME",
"ENTERPRISE_LICENSE_KEY"
"ENTERPRISE_LICENSE_KEY",
"PLAYWRIGHT_CI"
]
},
"post-install": {