Add Playwright for end-to-end testing and update configuration
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- Added Playwright as a dependency for end-to-end testing, including necessary scripts in package.json. - Created a Playwright configuration file to define test settings and browser options. - Implemented a new GitHub Actions workflow for automated end-to-end testing on push and pull request events. - Updated .gitignore to exclude Playwright test results and reports. - Added initial end-to-end tests for mail functionalities, including composing, sending, and searching messages.
This commit is contained in:
parent
bd50a4a54c
commit
9d0fb2766b
50
.github/workflows/e2e.yml
vendored
Normal file
50
.github/workflows/e2e.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
name: E2E
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master, main]
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: e2e-${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
playwright:
|
||||||
|
name: Playwright e2e
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
cache: pnpm
|
||||||
|
cache-dependency-path: pnpm-lock.yaml
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Install Playwright browsers
|
||||||
|
run: pnpm exec playwright install --with-deps chromium
|
||||||
|
env:
|
||||||
|
PLAYWRIGHT_BROWSERS_PATH: "0"
|
||||||
|
|
||||||
|
- name: Run e2e tests
|
||||||
|
run: pnpm run e2e
|
||||||
|
env:
|
||||||
|
CI: true
|
||||||
|
PLAYWRIGHT_BROWSERS_PATH: "0"
|
||||||
|
|
||||||
|
- name: Upload Playwright report
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: playwright-report/
|
||||||
|
retention-days: 7
|
||||||
8
.gitignore
vendored
8
.gitignore
vendored
@ -13,4 +13,10 @@ __v0_jsx-dev-runtime.ts
|
|||||||
# Common ignores
|
# Common ignores
|
||||||
node_modules
|
node_modules
|
||||||
.next/
|
.next/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# Playwright
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/blob-report/
|
||||||
|
/playwright/.cache/
|
||||||
56
e2e/helpers/mail-app.ts
Normal file
56
e2e/helpers/mail-app.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { expect, type Locator, type Page } from "@playwright/test"
|
||||||
|
|
||||||
|
export async function gotoInbox(page: Page) {
|
||||||
|
await page.goto("/mail/inbox")
|
||||||
|
await expect(page.locator("[data-email-row-id]").first()).toBeVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function composeWindow(page: Page): Locator {
|
||||||
|
return page.locator("[data-compose-window]").last()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function openCompose(page: Page) {
|
||||||
|
const composeButton = page
|
||||||
|
.getByRole("button", { name: "Nouveau message" })
|
||||||
|
.first()
|
||||||
|
await composeButton.click()
|
||||||
|
await expect(composeWindow(page)).toBeVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fillCompose(
|
||||||
|
page: Page,
|
||||||
|
{
|
||||||
|
to,
|
||||||
|
subject,
|
||||||
|
body,
|
||||||
|
}: {
|
||||||
|
to: string
|
||||||
|
subject: string
|
||||||
|
body: string
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const compose = composeWindow(page)
|
||||||
|
const toField = compose.locator('input[type="text"]').first()
|
||||||
|
|
||||||
|
await toField.fill(to)
|
||||||
|
await toField.press("Enter")
|
||||||
|
|
||||||
|
await compose.getByPlaceholder("Objet").fill(subject)
|
||||||
|
await compose.locator(".ProseMirror").click()
|
||||||
|
await compose.locator(".ProseMirror").fill(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendComposeNow(page: Page) {
|
||||||
|
const compose = composeWindow(page)
|
||||||
|
await compose.getByRole("button", { name: "Envoyer", exact: true }).click()
|
||||||
|
await page.getByRole("button", { name: "Envoyer maintenant" }).click()
|
||||||
|
await expect(page.getByText("Message envoyé")).toBeVisible()
|
||||||
|
await expect(compose).toHaveCount(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function scheduleComposeInOneHour(page: Page) {
|
||||||
|
const compose = composeWindow(page)
|
||||||
|
await compose.locator("button.rounded-r-full").click()
|
||||||
|
await page.getByRole("menuitem", { name: "Envoyer dans une heure" }).click()
|
||||||
|
await expect(page.getByText(/Ce mail sera envoyé le/)).toBeVisible()
|
||||||
|
}
|
||||||
74
e2e/mail-journeys.spec.ts
Normal file
74
e2e/mail-journeys.spec.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { test, expect } from "@playwright/test"
|
||||||
|
import {
|
||||||
|
fillCompose,
|
||||||
|
gotoInbox,
|
||||||
|
openCompose,
|
||||||
|
scheduleComposeInOneHour,
|
||||||
|
sendComposeNow,
|
||||||
|
} from "./helpers/mail-app"
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.addInitScript(() => {
|
||||||
|
window.localStorage.clear()
|
||||||
|
window.sessionStorage.clear()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test.describe("mail journeys (mock data)", () => {
|
||||||
|
test("reads a message from inbox", async ({ page }) => {
|
||||||
|
await gotoInbox(page)
|
||||||
|
|
||||||
|
await page.locator('[data-email-row-id="1"]').click()
|
||||||
|
|
||||||
|
await expect(page).toHaveURL(/\/mail\/inbox\/message\/1/)
|
||||||
|
await expect(page.getByTitle("Sujet du message")).toBeVisible()
|
||||||
|
await expect(page.getByLabel("Répondre").first()).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("composes and sends a message", async ({ page }) => {
|
||||||
|
const subject = `E2E send ${Date.now()}`
|
||||||
|
|
||||||
|
await gotoInbox(page)
|
||||||
|
await openCompose(page)
|
||||||
|
await fillCompose(page, {
|
||||||
|
to: "test@example.com",
|
||||||
|
subject,
|
||||||
|
body: "Playwright send journey",
|
||||||
|
})
|
||||||
|
await sendComposeNow(page)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("schedules a message for later", async ({ page }) => {
|
||||||
|
const subject = `E2E schedule ${Date.now()}`
|
||||||
|
|
||||||
|
await gotoInbox(page)
|
||||||
|
await openCompose(page)
|
||||||
|
await fillCompose(page, {
|
||||||
|
to: "scheduled@example.com",
|
||||||
|
subject,
|
||||||
|
body: "Playwright schedule journey",
|
||||||
|
})
|
||||||
|
await scheduleComposeInOneHour(page)
|
||||||
|
|
||||||
|
await expect(page.getByText(/Ce mail sera envoyé le/)).toBeVisible()
|
||||||
|
await page.waitForFunction(
|
||||||
|
(expectedSubject) => {
|
||||||
|
const raw = localStorage.getItem("ultimail-scheduled-state")
|
||||||
|
return raw?.includes(expectedSubject) ?? false
|
||||||
|
},
|
||||||
|
subject,
|
||||||
|
{ timeout: 10_000 }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("searches messages", async ({ page }) => {
|
||||||
|
await gotoInbox(page)
|
||||||
|
|
||||||
|
const search = page.getByPlaceholder("Rechercher dans les messages")
|
||||||
|
await search.fill("Uber")
|
||||||
|
await search.press("Enter")
|
||||||
|
|
||||||
|
await expect(page).toHaveURL(/\/mail\/search/)
|
||||||
|
await expect(page.locator("[data-email-row-id]").first()).toContainText("Uber")
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -2,9 +2,18 @@
|
|||||||
* Copyright (c) 2026 Eliott Guillaumin
|
* Copyright (c) 2026 Eliott Guillaumin
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
import path from "node:path"
|
||||||
|
import { fileURLToPath } from "node:url"
|
||||||
|
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
|
const projectRoot = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
output: 'standalone',
|
output: "standalone",
|
||||||
|
outputFileTracingRoot: projectRoot,
|
||||||
|
turbopack: {
|
||||||
|
root: projectRoot,
|
||||||
|
},
|
||||||
allowedDevOrigins: ['192.168.0.20', '127.0.0.1', 'localhost', '100.120.4.66'],
|
allowedDevOrigins: ['192.168.0.20', '127.0.0.1', 'localhost', '100.120.4.66'],
|
||||||
typescript: {
|
typescript: {
|
||||||
ignoreBuildErrors: true,
|
ignoreBuildErrors: true,
|
||||||
|
|||||||
@ -7,6 +7,10 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
|
"e2e": "playwright test",
|
||||||
|
"e2e:ui": "playwright test --ui",
|
||||||
|
"e2e:headed": "playwright test --headed",
|
||||||
|
"e2e:server": "pnpm build && cp -r public .next/standalone/ && cp -r .next/static .next/standalone/.next/static && node .next/standalone/server.js",
|
||||||
"expose": "source .env && cloudflared tunnel --loglevel error run --token $CLOUDFLARE_TUNNEL_TOKEN",
|
"expose": "source .env && cloudflared tunnel --loglevel error run --token $CLOUDFLARE_TUNNEL_TOKEN",
|
||||||
"brand:raster": "node scripts/rasterize-ultimail-brand.mjs",
|
"brand:raster": "node scripts/rasterize-ultimail-brand.mjs",
|
||||||
"brand:vectorize": "node scripts/vectorize-ultimail-brand.mjs",
|
"brand:vectorize": "node scripts/vectorize-ultimail-brand.mjs",
|
||||||
@ -86,6 +90,7 @@
|
|||||||
"zustand": "^5.0.13"
|
"zustand": "^5.0.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.52.0",
|
||||||
"@tailwindcss/postcss": "^4.2.0",
|
"@tailwindcss/postcss": "^4.2.0",
|
||||||
"@types/node": "^22",
|
"@types/node": "^22",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
|
|||||||
32
playwright.config.ts
Normal file
32
playwright.config.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { defineConfig, devices } from "@playwright/test"
|
||||||
|
|
||||||
|
const PORT = Number(process.env.PLAYWRIGHT_PORT ?? 3099)
|
||||||
|
const baseURL = `http://127.0.0.1:${PORT}`
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: "./e2e",
|
||||||
|
fullyParallel: false,
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
workers: 1,
|
||||||
|
reporter: process.env.CI ? "github" : "list",
|
||||||
|
timeout: 60_000,
|
||||||
|
use: {
|
||||||
|
baseURL,
|
||||||
|
trace: "on-first-retry",
|
||||||
|
screenshot: "only-on-failure",
|
||||||
|
video: "retain-on-failure",
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: "chromium",
|
||||||
|
use: { ...devices["Desktop Chrome"] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
webServer: {
|
||||||
|
command: `HOSTNAME=127.0.0.1 PORT=${PORT} pnpm run e2e:server`,
|
||||||
|
url: baseURL,
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
timeout: 300_000,
|
||||||
|
},
|
||||||
|
})
|
||||||
@ -145,7 +145,7 @@ importers:
|
|||||||
version: 3.23.2
|
version: 3.23.2
|
||||||
'@vercel/analytics':
|
'@vercel/analytics':
|
||||||
specifier: 1.6.1
|
specifier: 1.6.1
|
||||||
version: 1.6.1(next@16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
|
version: 1.6.1(next@16.2.6(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
|
||||||
autoprefixer:
|
autoprefixer:
|
||||||
specifier: ^10.4.20
|
specifier: ^10.4.20
|
||||||
version: 10.4.24(postcss@8.5.6)
|
version: 10.4.24(postcss@8.5.6)
|
||||||
@ -184,7 +184,7 @@ importers:
|
|||||||
version: 0.564.0(react@19.2.4)
|
version: 0.564.0(react@19.2.4)
|
||||||
next:
|
next:
|
||||||
specifier: 16.2.6
|
specifier: 16.2.6
|
||||||
version: 16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 16.2.6(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
next-themes:
|
next-themes:
|
||||||
specifier: ^0.4.6
|
specifier: ^0.4.6
|
||||||
version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
@ -222,6 +222,9 @@ importers:
|
|||||||
specifier: ^5.0.13
|
specifier: ^5.0.13
|
||||||
version: 5.0.13(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))
|
version: 5.0.13(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@playwright/test':
|
||||||
|
specifier: ^1.52.0
|
||||||
|
version: 1.60.0
|
||||||
'@tailwindcss/postcss':
|
'@tailwindcss/postcss':
|
||||||
specifier: ^4.2.0
|
specifier: ^4.2.0
|
||||||
version: 4.2.0
|
version: 4.2.0
|
||||||
@ -522,6 +525,11 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
|
'@playwright/test@1.60.0':
|
||||||
|
resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
'@radix-ui/number@1.1.1':
|
'@radix-ui/number@1.1.1':
|
||||||
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
|
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
|
||||||
|
|
||||||
@ -1655,6 +1663,11 @@ packages:
|
|||||||
fraction.js@5.3.4:
|
fraction.js@5.3.4:
|
||||||
resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
|
resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
|
||||||
|
|
||||||
|
fsevents@2.3.2:
|
||||||
|
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
fuse.js@7.3.0:
|
fuse.js@7.3.0:
|
||||||
resolution: {integrity: sha512-plz8RVjfcDedTGfVngWH1jmJvBvAwi1v2jecfDerbEnMcmOYUEEwKFTHbNoCiYyzaK2Ws8lABkTCcRSqCY1q4w==}
|
resolution: {integrity: sha512-plz8RVjfcDedTGfVngWH1jmJvBvAwi1v2jecfDerbEnMcmOYUEEwKFTHbNoCiYyzaK2Ws8lABkTCcRSqCY1q4w==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -1816,6 +1829,16 @@ packages:
|
|||||||
picocolors@1.1.1:
|
picocolors@1.1.1:
|
||||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||||
|
|
||||||
|
playwright-core@1.60.0:
|
||||||
|
resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
playwright@1.60.0:
|
||||||
|
resolution: {integrity: sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
postcss-value-parser@4.2.0:
|
postcss-value-parser@4.2.0:
|
||||||
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
|
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
|
||||||
|
|
||||||
@ -2289,6 +2312,10 @@ snapshots:
|
|||||||
'@next/swc-win32-x64-msvc@16.2.6':
|
'@next/swc-win32-x64-msvc@16.2.6':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@playwright/test@1.60.0':
|
||||||
|
dependencies:
|
||||||
|
playwright: 1.60.0
|
||||||
|
|
||||||
'@radix-ui/number@1.1.1': {}
|
'@radix-ui/number@1.1.1': {}
|
||||||
|
|
||||||
'@radix-ui/primitive@1.1.3': {}
|
'@radix-ui/primitive@1.1.3': {}
|
||||||
@ -3292,9 +3319,9 @@ snapshots:
|
|||||||
|
|
||||||
'@types/use-sync-external-store@0.0.6': {}
|
'@types/use-sync-external-store@0.0.6': {}
|
||||||
|
|
||||||
'@vercel/analytics@1.6.1(next@16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)':
|
'@vercel/analytics@1.6.1(next@16.2.6(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
next: 16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
next: 16.2.6(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
|
|
||||||
aria-hidden@1.2.6:
|
aria-hidden@1.2.6:
|
||||||
@ -3432,6 +3459,9 @@ snapshots:
|
|||||||
|
|
||||||
fraction.js@5.3.4: {}
|
fraction.js@5.3.4: {}
|
||||||
|
|
||||||
|
fsevents@2.3.2:
|
||||||
|
optional: true
|
||||||
|
|
||||||
fuse.js@7.3.0: {}
|
fuse.js@7.3.0: {}
|
||||||
|
|
||||||
get-nonce@1.0.1: {}
|
get-nonce@1.0.1: {}
|
||||||
@ -3521,7 +3551,7 @@ snapshots:
|
|||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
react-dom: 19.2.4(react@19.2.4)
|
react-dom: 19.2.4(react@19.2.4)
|
||||||
|
|
||||||
next@16.2.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
next@16.2.6(@playwright/test@1.60.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 16.2.6
|
'@next/env': 16.2.6
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
@ -3540,6 +3570,7 @@ snapshots:
|
|||||||
'@next/swc-linux-x64-musl': 16.2.6
|
'@next/swc-linux-x64-musl': 16.2.6
|
||||||
'@next/swc-win32-arm64-msvc': 16.2.6
|
'@next/swc-win32-arm64-msvc': 16.2.6
|
||||||
'@next/swc-win32-x64-msvc': 16.2.6
|
'@next/swc-win32-x64-msvc': 16.2.6
|
||||||
|
'@playwright/test': 1.60.0
|
||||||
sharp: 0.34.5
|
sharp: 0.34.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
@ -3553,6 +3584,14 @@ snapshots:
|
|||||||
|
|
||||||
picocolors@1.1.1: {}
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
|
playwright-core@1.60.0: {}
|
||||||
|
|
||||||
|
playwright@1.60.0:
|
||||||
|
dependencies:
|
||||||
|
playwright-core: 1.60.0
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
|
||||||
postcss-value-parser@4.2.0: {}
|
postcss-value-parser@4.2.0: {}
|
||||||
|
|
||||||
postcss@8.4.31:
|
postcss@8.4.31:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user