diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
new file mode 100644
index 0000000..0ecef4c
--- /dev/null
+++ b/.github/workflows/e2e.yml
@@ -0,0 +1,29 @@
+name: Playwright Tests
+on:
+ push:
+ branches: [ main, develop, feature/e2e ]
+ pull_request:
+ branches: [ main, develop, feature/e2e ]
+jobs:
+ test:
+ timeout-minutes: 60
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: lts/*
+ - name: Install dependencies
+ run: npm install -g pnpm && pnpm install
+ - name: Build vitepress-openapi
+ run: pnpm build
+ - name: Install Playwright Browsers
+ run: pnpm exec playwright install --with-deps
+ - name: Run Playwright tests
+ run: pnpm exec playwright test
+ - uses: actions/upload-artifact@v4
+ if: ${{ !cancelled() }}
+ with:
+ name: playwright-report
+ path: playwright-report/
+ retention-days: 30
diff --git a/.gitignore b/.gitignore
index fd80a1f..ab6e3f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,9 @@ docs/.vitepress/cache
docs/.vitepress/.temp
types
.idea
+
+# Playwright
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
diff --git a/e2e/dev/.gitignore b/e2e/dev/.gitignore
new file mode 100644
index 0000000..131cc58
--- /dev/null
+++ b/e2e/dev/.gitignore
@@ -0,0 +1,6 @@
+node_modules
+dist
+.temp
+cache
+.idea
+.vscode
diff --git a/e2e/dev/docs/.vitepress/config.ts b/e2e/dev/docs/.vitepress/config.ts
new file mode 100644
index 0000000..bdce816
--- /dev/null
+++ b/e2e/dev/docs/.vitepress/config.ts
@@ -0,0 +1,67 @@
+import { fileURLToPath } from 'node:url'
+import { defineConfig } from 'vitepress'
+import { useSidebar } from 'vitepress-openapi'
+import spec from '../../public/openapi.json'
+
+const sidebar = useSidebar({
+ spec,
+ // Optionally, you can specify a link prefix for all generated sidebar items.
+ linkPrefix: '/operations/',
+})
+
+// refer https://vitepress.dev/reference/site-config for details
+export default defineConfig({
+ lang: 'en-US',
+ title: 'VitePress OpenAPI',
+ description: 'Generate documentation from OpenAPI specifications.',
+
+ themeConfig: {
+ nav: [{ text: 'API Reference', link: '/introduction' }],
+
+ sidebar: [
+ {
+ text: 'By Tags',
+ items: [
+ {
+ text: 'Introduction',
+ link: '/introduction',
+ },
+ ...sidebar.itemsByTags(),
+ ],
+ },
+ {
+ text: 'By Operations',
+ items: [
+ ...sidebar.generateSidebarGroups(),
+ ],
+ },
+ {
+ text: 'By Paths',
+ items: [
+ ...sidebar.itemsByPaths(),
+ ],
+ },
+ {
+ text: 'One Page',
+ items: [
+ { text: 'One Page', link: '/one-page' },
+ { text: 'Without Sidebar', link: '/without-sidebar' },
+ ],
+ },
+ ],
+ },
+
+ vite: {
+ resolve: {
+ alias: {
+ ...(process.env.NODE_ENV === 'production'
+ ? {}
+ : {
+ 'vitepress-openapi/client': fileURLToPath(new URL('../../../../src/client', import.meta.url)),
+ 'vitepress-openapi/dist/style.css': fileURLToPath(new URL('../../../../dist/style.css', import.meta.url)),
+ 'vitepress-openapi': fileURLToPath(new URL('../../../../src/index', import.meta.url)),
+ }),
+ },
+ },
+ },
+})
diff --git a/e2e/dev/docs/.vitepress/theme/index.ts b/e2e/dev/docs/.vitepress/theme/index.ts
new file mode 100644
index 0000000..89c2ed9
--- /dev/null
+++ b/e2e/dev/docs/.vitepress/theme/index.ts
@@ -0,0 +1,18 @@
+import DefaultTheme from 'vitepress/theme';
+import type { Theme } from 'vitepress';
+
+import { theme, useOpenapi } from 'vitepress-openapi/client';
+import 'vitepress-openapi/dist/style.css';
+import spec from '../../../public/openapi.json'
+
+export default {
+ ...DefaultTheme,
+ async enhanceApp({ app, router, siteData }) {
+ const openapi = useOpenapi({
+ spec,
+ config: {},
+ });
+
+ theme.enhanceApp({ app, openapi });
+ },
+} satisfies Theme;
diff --git a/e2e/dev/docs/index.md b/e2e/dev/docs/index.md
new file mode 100644
index 0000000..49a15d9
--- /dev/null
+++ b/e2e/dev/docs/index.md
@@ -0,0 +1,14 @@
+---
+layout: home
+
+hero:
+ name: "VitePress OpenAPI"
+ tagline: "Generate documentation from OpenAPI specifications."
+ actions:
+ - theme: brand
+ text: API Reference
+ link: /introduction
+ - theme: alt
+ text: Documentation
+ link: https://vitepress-openapi.vercel.app/
+---
diff --git a/e2e/dev/docs/introduction.md b/e2e/dev/docs/introduction.md
new file mode 100644
index 0000000..6794c17
--- /dev/null
+++ b/e2e/dev/docs/introduction.md
@@ -0,0 +1,7 @@
+---
+title: vitepress-openapi
+---
+
+
+
+
diff --git a/e2e/dev/docs/one-page.md b/e2e/dev/docs/one-page.md
new file mode 100644
index 0000000..2dbfac0
--- /dev/null
+++ b/e2e/dev/docs/one-page.md
@@ -0,0 +1,12 @@
+---
+aside: false
+title: vitepress-openapi
+---
+
+
+
+
diff --git a/e2e/dev/docs/operations/[operationId].md b/e2e/dev/docs/operations/[operationId].md
new file mode 100644
index 0000000..428b256
--- /dev/null
+++ b/e2e/dev/docs/operations/[operationId].md
@@ -0,0 +1,17 @@
+---
+aside: false
+outline: false
+title: vitepress-openapi
+---
+
+
+
+
diff --git a/e2e/dev/docs/operations/[operationId].paths.js b/e2e/dev/docs/operations/[operationId].paths.js
new file mode 100644
index 0000000..7bdf784
--- /dev/null
+++ b/e2e/dev/docs/operations/[operationId].paths.js
@@ -0,0 +1,17 @@
+import { usePaths } from 'vitepress-openapi'
+import spec from '../../public/openapi.json'
+
+export default {
+ paths() {
+ return usePaths({ spec })
+ .getPathsByVerbs()
+ .map(({ operationId, summary }) => {
+ return {
+ params: {
+ operationId,
+ pageTitle: `${summary} - vitepress-openapi`,
+ },
+ }
+ })
+ },
+}
diff --git a/e2e/dev/docs/tags/[tag].md b/e2e/dev/docs/tags/[tag].md
new file mode 100644
index 0000000..d8f6641
--- /dev/null
+++ b/e2e/dev/docs/tags/[tag].md
@@ -0,0 +1,17 @@
+---
+aside: false
+outline: false
+title: vitepress-openapi
+---
+
+
+
+
diff --git a/e2e/dev/docs/tags/[tag].paths.js b/e2e/dev/docs/tags/[tag].paths.js
new file mode 100644
index 0000000..6c4e6b2
--- /dev/null
+++ b/e2e/dev/docs/tags/[tag].paths.js
@@ -0,0 +1,17 @@
+import { usePaths } from 'vitepress-openapi'
+import spec from '../../public/openapi.json'
+
+export default {
+ paths() {
+ return usePaths({ spec })
+ .getTags()
+ .map(({ name }) => {
+ return {
+ params: {
+ tag: name,
+ pageTitle: `${name} - vitepress-openapi`,
+ },
+ }
+ })
+ },
+}
diff --git a/e2e/dev/docs/without-sidebar.md b/e2e/dev/docs/without-sidebar.md
new file mode 100644
index 0000000..b42bc8e
--- /dev/null
+++ b/e2e/dev/docs/without-sidebar.md
@@ -0,0 +1,14 @@
+---
+sidebar: false
+aside: true
+outline: [1, 2]
+title: vitepress-openapi
+---
+
+
+
+
diff --git a/e2e/dev/package.json b/e2e/dev/package.json
new file mode 100644
index 0000000..516e1e3
--- /dev/null
+++ b/e2e/dev/package.json
@@ -0,0 +1,14 @@
+{
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vitepress dev docs",
+ "build": "vitepress build docs",
+ "preview": "vitepress preview docs",
+ "start": "vitepress dev docs"
+ },
+ "devDependencies": {
+ "vitepress": "latest",
+ "vitepress-openapi": "workspace:*"
+ }
+}
diff --git a/e2e/dev/public/openapi.json b/e2e/dev/public/openapi.json
new file mode 100644
index 0000000..2cb8cd4
--- /dev/null
+++ b/e2e/dev/public/openapi.json
@@ -0,0 +1,732 @@
+{
+ "openapi": "3.1.0",
+ "info": {
+ "title": "Argentine Rock Legends",
+ "description": "The Argentine Rock Legends is an example OpenAPI specification to test OpenAPI tools and libraries. Get all the data for [all artists](#getAllArtists).\n\n>Inspired by [Scalar Galaxy](https://galaxy.scalar.com/)\n\n## Resources\n\n* https://github.com/enzonotario/vitepress-openapi\n* https://github.com/OAI/OpenAPI-Specification\n\n## Markdown Support\n\nAll descriptions *can* contain ~~tons of text~~ **Markdown**. [If GitHub supports the syntax](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax), chances are we’re supporting it, too. You can even create [internal links to reference endpoints](#createArtist).\n\n\n Examples
\n\n **Blockquotes**\n\n > I love Argentine Rock. <3\n\n **Tables**\n\n | Feature | Availability |\n | ---------------- | ------------ |\n | Markdown Support | ✓ |\n\n **Accordion**\n\n ```html\n \n Using Details Tags
\n HTML Example
\n \n ```\n\n **Images**\n\n Yes, there’s support for images, too!\n\n \n\n \n",
+ "version": "1.0.0",
+ "contact": {
+ "name": "Enzo Notario",
+ "url": "https://enzonotario.me",
+ "email": "hi@enzonotario.me"
+ }
+ },
+ "servers": [
+ {
+ "url": "https://stoplight.io/mocks/enzonotario/argentine-rock/122547792",
+ "description": "Mock Server"
+ }
+ ],
+ "security": [
+ {
+ "bearerAuth": []
+ },
+ {
+ "apiKeyHeader": []
+ }
+ ],
+ "tags": [
+ {
+ "name": "Authentication",
+ "description": "Some endpoints are public, but some require authentication. We provide all the required endpoints to create an account and authorize yourself."
+ },
+ {
+ "name": "Artists",
+ "description": "Everything about Argentine Rock artists"
+ }
+ ],
+ "paths": {
+ "/api/v1/artists": {
+ "get": {
+ "tags": [
+ "Artists"
+ ],
+ "summary": "Get all artists",
+ "description": "Get a list of all legendary Argentine Rock artists and explore their contributions to the music scene.",
+ "operationId": "getAllArtists",
+ "security": [
+ {}
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/limit"
+ },
+ {
+ "$ref": "#/components/parameters/offset"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Artist"
+ }
+ }
+ }
+ },
+ {
+ "$ref": "#/components/schemas/PaginatedResource"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "tags": [
+ "Artists"
+ ],
+ "summary": "Add a new artist",
+ "description": "Add a new legendary Argentine Rock artist. Make sure they truly deserve the title!",
+ "operationId": "createArtist",
+ "requestBody": {
+ "description": "Artist data",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Artist"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Created",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Artist"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/artists/{artistId}": {
+ "get": {
+ "tags": [
+ "Artists"
+ ],
+ "summary": "Get an artist",
+ "description": "Learn more about a specific Argentine Rock artist and their legacy.",
+ "operationId": "getArtist",
+ "security": [
+ {}
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/artistId"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Artist Found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Artist"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Artist Not Found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "tags": [
+ "Artists"
+ ],
+ "summary": "Update an artist",
+ "description": "Update the information of a legendary Argentine Rock artist. Make sure to provide accurate data.",
+ "operationId": "updateArtist",
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/artistId"
+ }
+ ],
+ "requestBody": {
+ "description": "Artist data",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Artist"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Artist"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "Artists"
+ ],
+ "summary": "Delete an artist",
+ "operationId": "deleteArtist",
+ "description": "This endpoint was used to delete artists. Unfortunately, that caused a lot of controversy. So, this endpoint is now deprecated and should not be used anymore.",
+ "deprecated": true,
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/artistId"
+ }
+ ],
+ "responses": {
+ "400": {
+ "description": "Bad Request",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/artists/{artistId}/albums": {
+ "get": {
+ "tags": [
+ "Artists"
+ ],
+ "summary": "Get all albums",
+ "description": "Get a list of all albums from a legendary Argentine Rock artist.",
+ "operationId": "getAllAlbums",
+ "security": [
+ {}
+ ],
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/artistId"
+ },
+ {
+ "$ref": "#/components/parameters/limit"
+ },
+ {
+ "$ref": "#/components/parameters/offset"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Album"
+ }
+ }
+ }
+ },
+ {
+ "$ref": "#/components/schemas/PaginatedResource"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "tags": [
+ "Artists"
+ ],
+ "summary": "Add a new album",
+ "description": "Add a new album to a legendary Argentine Rock artist. Make sure it’s a masterpiece!",
+ "operationId": "createAlbum",
+ "parameters": [
+ {
+ "$ref": "#/components/parameters/artistId"
+ }
+ ],
+ "requestBody": {
+ "description": "Album data",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Album"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Created",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Album"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/user/signup": {
+ "post": {
+ "tags": [
+ "Authentication"
+ ],
+ "summary": "Create a user",
+ "description": "Create a user account to access exclusive content about Argentine Rock legends.",
+ "operationId": "createUser",
+ "security": [
+ {}
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/NewUser"
+ },
+ "examples": {
+ "Carlos": {
+ "value": {
+ "name": "Carlos",
+ "email": "carlos@rock-legends.com",
+ "password": "i-love-rock"
+ }
+ },
+ "Maria": {
+ "value": {
+ "name": "Maria",
+ "email": "maria@rock-legends.com",
+ "password": "rock-n-roll"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Created",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "securitySchemes": {
+ "bearerAuth": {
+ "type": "http",
+ "scheme": "bearer"
+ },
+ "apiKeyHeader": {
+ "type": "apiKey",
+ "in": "header",
+ "name": "X-API-Key"
+ }
+ },
+ "parameters": {
+ "artistId": {
+ "name": "artistId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64",
+ "examples": [
+ 1
+ ]
+ }
+ },
+ "limit": {
+ "name": "limit",
+ "in": "query",
+ "description": "The number of items to return",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "format": "int64",
+ "default": 10
+ }
+ },
+ "offset": {
+ "name": "offset",
+ "in": "query",
+ "description": "The number of items to skip before starting to collect the result set",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "format": "int64",
+ "default": 0
+ }
+ }
+ },
+ "responses": {
+ "BadRequest": {
+ "description": "Bad Request",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ },
+ "Forbidden": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ },
+ "NotFound": {
+ "description": "NotFound",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ }
+ },
+ "schemas": {
+ "NewUser": {
+ "type": "object",
+ "required": [
+ "name",
+ "email",
+ "password"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "examples": [
+ "Carlos",
+ "Maria"
+ ]
+ },
+ "email": {
+ "type": "string",
+ "format": "email",
+ "examples": [
+ "carlos@rock-legends.com",
+ "maria@rock-legends.com"
+ ]
+ },
+ "password": {
+ "type": "string",
+ "minLength": 8,
+ "examples": [
+ "i-love-rock",
+ "rock-n-roll"
+ ]
+ }
+ }
+ },
+ "User": {
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "email"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "examples": [
+ 1
+ ]
+ },
+ "name": {
+ "type": "string",
+ "examples": [
+ "Carlos"
+ ]
+ },
+ "email": {
+ "type": "string",
+ "format": "email",
+ "examples": [
+ "carlos@rock-legends.com"
+ ]
+ }
+ }
+ },
+ "Artist": {
+ "type": "object",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "examples": [
+ 1
+ ],
+ "x-variable": "artistId"
+ },
+ "name": {
+ "type": "string",
+ "examples": [
+ "Charly García"
+ ]
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "examples": [
+ "One of the most influential rock musicians in Argentine history."
+ ]
+ },
+ "image": {
+ "type": "string",
+ "nullable": true,
+ "examples": [
+ "https://cdn.rock-legends.com/photos/charly.jpg"
+ ]
+ },
+ "band": {
+ "type": "string",
+ "examples": [
+ "Sui Generis"
+ ]
+ }
+ }
+ },
+ "Album": {
+ "type": "object",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64",
+ "examples": [
+ 1
+ ]
+ },
+ "name": {
+ "type": "string",
+ "examples": [
+ "La Máquina de Hacer Pájaros"
+ ]
+ },
+ "year": {
+ "type": "integer",
+ "format": "int64",
+ "examples": [
+ 1976
+ ]
+ },
+ "image": {
+ "type": "string",
+ "nullable": true,
+ "examples": [
+ "https://cdn.rock-legends.com/photos/la-maquina.jpg"
+ ]
+ }
+ }
+ },
+ "PaginatedResource": {
+ "type": "object",
+ "properties": {
+ "meta": {
+ "type": "object",
+ "properties": {
+ "limit": {
+ "type": "integer",
+ "format": "int64",
+ "examples": [
+ 10
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "format": "int64",
+ "examples": [
+ 0
+ ]
+ },
+ "total": {
+ "type": "integer",
+ "format": "int64",
+ "examples": [
+ 100
+ ]
+ },
+ "next": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "examples": [
+ "/artists?limit=10&offset=10"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "Error": {
+ "type": "object",
+ "description": "RFC 7807 (https://datatracker.ietf.org/doc/html/rfc7807)",
+ "properties": {
+ "type": {
+ "type": "string",
+ "examples": [
+ "https://example.com/errors/generic-error"
+ ]
+ },
+ "title": {
+ "type": "string",
+ "examples": [
+ "Something went wrong here."
+ ]
+ },
+ "status": {
+ "type": "integer",
+ "format": "int64",
+ "examples": [
+ 403
+ ]
+ },
+ "detail": {
+ "type": "string",
+ "examples": [
+ "Unfortunately, we can’t provide further information."
+ ]
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/e2e/home.spec.ts b/e2e/home.spec.ts
new file mode 100644
index 0000000..cbad247
--- /dev/null
+++ b/e2e/home.spec.ts
@@ -0,0 +1,10 @@
+import { expect, test } from '@playwright/test'
+
+test('home', async ({ page }) => {
+ await page.goto('/')
+
+ await expect(page).toHaveTitle(/VitePress OpenAPI/)
+ await expect(page).toHaveScreenshot({
+ fullPage: true,
+ })
+})
diff --git a/e2e/home.spec.ts-snapshots/home-1-chromium-linux.png b/e2e/home.spec.ts-snapshots/home-1-chromium-linux.png
new file mode 100644
index 0000000..39dd2c7
Binary files /dev/null and b/e2e/home.spec.ts-snapshots/home-1-chromium-linux.png differ
diff --git a/e2e/home.spec.ts-snapshots/home-1-firefox-linux.png b/e2e/home.spec.ts-snapshots/home-1-firefox-linux.png
new file mode 100644
index 0000000..200b871
Binary files /dev/null and b/e2e/home.spec.ts-snapshots/home-1-firefox-linux.png differ
diff --git a/e2e/one-page.spec.ts b/e2e/one-page.spec.ts
new file mode 100644
index 0000000..693b6ab
--- /dev/null
+++ b/e2e/one-page.spec.ts
@@ -0,0 +1,10 @@
+import { expect, test } from '@playwright/test'
+
+test('one-page', async ({ page }) => {
+ await page.goto('/one-page')
+
+ await expect(page).toHaveScreenshot({
+ fullPage: true,
+ timeout: 20000,
+ })
+})
diff --git a/e2e/one-page.spec.ts-snapshots/one-page-1-chromium-linux.png b/e2e/one-page.spec.ts-snapshots/one-page-1-chromium-linux.png
new file mode 100644
index 0000000..6a0c47d
Binary files /dev/null and b/e2e/one-page.spec.ts-snapshots/one-page-1-chromium-linux.png differ
diff --git a/e2e/one-page.spec.ts-snapshots/one-page-1-firefox-linux.png b/e2e/one-page.spec.ts-snapshots/one-page-1-firefox-linux.png
new file mode 100644
index 0000000..680d8d0
Binary files /dev/null and b/e2e/one-page.spec.ts-snapshots/one-page-1-firefox-linux.png differ
diff --git a/package.json b/package.json
index 83de9b1..099e087 100644
--- a/package.json
+++ b/package.json
@@ -44,7 +44,8 @@
"docs:build": "pnpm run build && cd docs && pnpm run build",
"test": "vitest",
"test:run": "vitest --run",
- "typecheck": "vue-tsc --noEmit"
+ "typecheck": "vue-tsc --noEmit",
+ "e2e:dev": "cd e2e/dev && pnpm run dev"
},
"peerDependencies": {
"vitepress": ">=1.0.0",
@@ -61,6 +62,7 @@
},
"devDependencies": {
"@antfu/eslint-config": "^2.27.3",
+ "@playwright/test": "^1.50.0",
"@scalar/openapi-types": "^0.1.6",
"@sindresorhus/slugify": "^2.2.1",
"@trojs/openapi-dereference": "^0.2.5",
diff --git a/playwright.config.ts b/playwright.config.ts
new file mode 100644
index 0000000..6fa8a74
--- /dev/null
+++ b/playwright.config.ts
@@ -0,0 +1,72 @@
+import { defineConfig, devices } from '@playwright/test'
+
+const port = 4173
+
+const url = `http://127.0.0.1:${port}`
+
+export default defineConfig({
+ testDir: './e2e',
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: process.env.CI ? 'github' : '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: url,
+
+ /* 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' },
+ // },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ webServer: {
+ command: `cd e2e/dev && vitepress dev docs --port ${port}`,
+ port,
+ reuseExistingServer: !process.env.CI,
+ },
+})
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c728c93..0b74e13 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -36,6 +36,9 @@ importers:
'@antfu/eslint-config':
specifier: ^2.27.3
version: 2.27.3(@typescript-eslint/utils@8.21.0(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3))(@vue/compiler-sfc@3.5.13)(eslint-plugin-format@0.1.3(eslint@9.18.0(jiti@1.21.7)))(eslint@9.18.0(jiti@1.21.7))(svelte@4.2.19)(typescript@5.7.3)(vitest@2.1.8(@types/node@22.10.7)(sass@1.83.4))
+ '@playwright/test':
+ specifier: ^1.50.0
+ version: 1.50.0
'@scalar/openapi-types':
specifier: ^0.1.6
version: 0.1.6
@@ -200,22 +203,51 @@ importers:
specifier: ^3.5.13
version: 3.5.13(typescript@5.7.3)
+ e2e/dev:
+ devDependencies:
+ vitepress:
+ specifier: latest
+ version: 1.6.3(@algolia/client-search@5.20.0)(@types/node@22.10.7)(postcss@8.5.1)(sass@1.83.4)(search-insights@2.17.3)(typescript@5.7.3)
+ vitepress-openapi:
+ specifier: workspace:*
+ version: link:../..
+
packages:
+ '@algolia/autocomplete-core@1.17.7':
+ resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==}
+
'@algolia/autocomplete-core@1.17.9':
resolution: {integrity: sha512-O7BxrpLDPJWWHv/DLA9DRFWs+iY1uOJZkqUwjS5HSZAGcl0hIVCQ97LTLewiZmZ402JYUrun+8NqFP+hCknlbQ==}
+ '@algolia/autocomplete-plugin-algolia-insights@1.17.7':
+ resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==}
+ peerDependencies:
+ search-insights: '>= 1 < 3'
+
'@algolia/autocomplete-plugin-algolia-insights@1.17.9':
resolution: {integrity: sha512-u1fEHkCbWF92DBeB/KHeMacsjsoI0wFhjZtlCq2ddZbAehshbZST6Hs0Avkc0s+4UyBGbMDnSuXHLuvRWK5iDQ==}
peerDependencies:
search-insights: '>= 1 < 3'
+ '@algolia/autocomplete-preset-algolia@1.17.7':
+ resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==}
+ peerDependencies:
+ '@algolia/client-search': '>= 4.9.1 < 6'
+ algoliasearch: '>= 4.9.1 < 6'
+
'@algolia/autocomplete-preset-algolia@1.17.9':
resolution: {integrity: sha512-Na1OuceSJeg8j7ZWn5ssMu/Ax3amtOwk76u4h5J4eK2Nx2KB5qt0Z4cOapCsxot9VcEN11ADV5aUSlQF4RhGjQ==}
peerDependencies:
'@algolia/client-search': '>= 4.9.1 < 6'
algoliasearch: '>= 4.9.1 < 6'
+ '@algolia/autocomplete-shared@1.17.7':
+ resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==}
+ peerDependencies:
+ '@algolia/client-search': '>= 4.9.1 < 6'
+ algoliasearch: '>= 4.9.1 < 6'
+
'@algolia/autocomplete-shared@1.17.9':
resolution: {integrity: sha512-iDf05JDQ7I0b7JEA/9IektxN/80a2MZ1ToohfmNS3rfeuQnIKI3IJlIafD0xu4StbtQTghx9T3Maa97ytkXenQ==}
peerDependencies:
@@ -390,12 +422,35 @@ packages:
'@codemirror/view@6.36.2':
resolution: {integrity: sha512-DZ6ONbs8qdJK0fdN7AB82CgI6tYXf4HWk1wSVa0+9bhVznCuuvhQtX8bFBoy3dv8rZSQqUd8GvhVAcielcidrA==}
+ '@docsearch/css@3.8.2':
+ resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==}
+
'@docsearch/css@3.8.3':
resolution: {integrity: sha512-1nELpMV40JDLJ6rpVVFX48R1jsBFIQ6RnEQDsLFGmzOjPWTOMlZqUcXcvRx8VmYV/TqnS1l784Ofz+ZEb+wEOQ==}
+ '@docsearch/js@3.8.2':
+ resolution: {integrity: sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==}
+
'@docsearch/js@3.8.3':
resolution: {integrity: sha512-CQsX1zeoPJIWxN3IGoDSWOqzRc0JsOE9Bclegf9llwjYN2rzzJF93zagGcT3uI3tF31oCqTuUOVGW/mVFb7arw==}
+ '@docsearch/react@3.8.2':
+ resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==}
+ peerDependencies:
+ '@types/react': '>= 16.8.0 < 19.0.0'
+ react: '>= 16.8.0 < 19.0.0'
+ react-dom: '>= 16.8.0 < 19.0.0'
+ search-insights: '>= 1 < 3'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ search-insights:
+ optional: true
+
'@docsearch/react@3.8.3':
resolution: {integrity: sha512-6UNrg88K7lJWmuS6zFPL/xgL+n326qXqZ7Ybyy4E8P/6Rcblk3GE8RXxeol4Pd5pFpKMhOhBhzABKKwHtbJCIg==}
peerDependencies:
@@ -816,6 +871,11 @@ packages:
resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+ '@playwright/test@1.50.0':
+ resolution: {integrity: sha512-ZGNXbt+d65EGjBORQHuYKj+XhCewlwpnSd/EDuLPZGSiEWmgOJB5RmMCCYGy5aMfTs9wx61RivfDKi8H/hcMvw==}
+ engines: {node: '>=18'}
+ hasBin: true
+
'@replit/codemirror-indentation-markers@6.5.3':
resolution: {integrity: sha512-hL5Sfvw3C1vgg7GolLe/uxX5T3tmgOA3ZzqlMv47zjU1ON51pzNWiVbS22oh6crYhtVhv8b3gdXwoYp++2ilHw==}
peerDependencies:
@@ -1903,6 +1963,11 @@ packages:
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
+ fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -2414,6 +2479,16 @@ packages:
pkg-types@1.3.1:
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
+ playwright-core@1.50.0:
+ resolution: {integrity: sha512-CXkSSlr4JaZs2tZHI40DsZUN/NIwgaUPsyLuOAaIZp2CyF2sN5MM5NJsyB188lFSSozFxQ5fPT4qM+f0tH/6wQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ playwright@1.50.0:
+ resolution: {integrity: sha512-+GinGfGTrd2IfX1TA4N2gNmeIksSb+IAe589ZH+FlmpV3MYTx6+buChGIuDLQwrGNCw2lWibqV50fU510N7S+w==}
+ engines: {node: '>=18'}
+ hasBin: true
+
pluralize@8.0.0:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
@@ -2909,6 +2984,18 @@ packages:
postcss:
optional: true
+ vitepress@1.6.3:
+ resolution: {integrity: sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==}
+ hasBin: true
+ peerDependencies:
+ markdown-it-mathjax3: ^4
+ postcss: ^8
+ peerDependenciesMeta:
+ markdown-it-mathjax3:
+ optional: true
+ postcss:
+ optional: true
+
vitest@2.1.8:
resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -3047,6 +3134,15 @@ packages:
snapshots:
+ '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)':
+ dependencies:
+ '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)
+ '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - algoliasearch
+ - search-insights
+
'@algolia/autocomplete-core@1.17.9(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)':
dependencies:
'@algolia/autocomplete-plugin-algolia-insights': 1.17.9(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)
@@ -3056,6 +3152,14 @@ snapshots:
- algoliasearch
- search-insights
+ '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)':
+ dependencies:
+ '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)
+ search-insights: 2.17.3
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - algoliasearch
+
'@algolia/autocomplete-plugin-algolia-insights@1.17.9(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)':
dependencies:
'@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)
@@ -3064,12 +3168,23 @@ snapshots:
- '@algolia/client-search'
- algoliasearch
+ '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)':
+ dependencies:
+ '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)
+ '@algolia/client-search': 5.20.0
+ algoliasearch: 5.20.0
+
'@algolia/autocomplete-preset-algolia@1.17.9(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)':
dependencies:
'@algolia/autocomplete-shared': 1.17.9(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)
'@algolia/client-search': 5.20.0
algoliasearch: 5.20.0
+ '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)':
+ dependencies:
+ '@algolia/client-search': 5.20.0
+ algoliasearch: 5.20.0
+
'@algolia/autocomplete-shared@1.17.9(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)':
dependencies:
'@algolia/client-search': 5.20.0
@@ -3297,8 +3412,21 @@ snapshots:
style-mod: 4.1.2
w3c-keyname: 2.2.8
+ '@docsearch/css@3.8.2': {}
+
'@docsearch/css@3.8.3': {}
+ '@docsearch/js@3.8.2(@algolia/client-search@5.20.0)(search-insights@2.17.3)':
+ dependencies:
+ '@docsearch/react': 3.8.2(@algolia/client-search@5.20.0)(search-insights@2.17.3)
+ preact: 10.25.4
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - '@types/react'
+ - react
+ - react-dom
+ - search-insights
+
'@docsearch/js@3.8.3(@algolia/client-search@5.20.0)(search-insights@2.17.3)':
dependencies:
'@docsearch/react': 3.8.3(@algolia/client-search@5.20.0)(search-insights@2.17.3)
@@ -3310,6 +3438,17 @@ snapshots:
- react-dom
- search-insights
+ '@docsearch/react@3.8.2(@algolia/client-search@5.20.0)(search-insights@2.17.3)':
+ dependencies:
+ '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)
+ '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)
+ '@docsearch/css': 3.8.2
+ algoliasearch: 5.20.0
+ optionalDependencies:
+ search-insights: 2.17.3
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+
'@docsearch/react@3.8.3(@algolia/client-search@5.20.0)(search-insights@2.17.3)':
dependencies:
'@algolia/autocomplete-core': 1.17.9(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)
@@ -3646,6 +3785,10 @@ snapshots:
'@pkgr/core@0.1.1': {}
+ '@playwright/test@1.50.0':
+ dependencies:
+ playwright: 1.50.0
+
'@replit/codemirror-indentation-markers@6.5.3(@codemirror/language@6.10.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)':
dependencies:
'@codemirror/language': 6.10.8
@@ -4867,6 +5010,9 @@ snapshots:
fraction.js@4.3.7: {}
+ fsevents@2.3.2:
+ optional: true
+
fsevents@2.3.3:
optional: true
@@ -5344,6 +5490,14 @@ snapshots:
mlly: 1.7.4
pathe: 2.0.2
+ playwright-core@1.50.0: {}
+
+ playwright@1.50.0:
+ dependencies:
+ playwright-core: 1.50.0
+ optionalDependencies:
+ fsevents: 2.3.2
+
pluralize@8.0.0: {}
postcss-import@15.1.0(postcss@8.5.1):
@@ -5936,6 +6090,55 @@ snapshots:
- typescript
- universal-cookie
+ vitepress@1.6.3(@algolia/client-search@5.20.0)(@types/node@22.10.7)(postcss@8.5.1)(sass@1.83.4)(search-insights@2.17.3)(typescript@5.7.3):
+ dependencies:
+ '@docsearch/css': 3.8.2
+ '@docsearch/js': 3.8.2(@algolia/client-search@5.20.0)(search-insights@2.17.3)
+ '@iconify-json/simple-icons': 1.2.21
+ '@shikijs/core': 2.1.0
+ '@shikijs/transformers': 2.1.0
+ '@shikijs/types': 2.1.0
+ '@types/markdown-it': 14.1.2
+ '@vitejs/plugin-vue': 5.2.1(vite@5.4.14(@types/node@22.10.7)(sass@1.83.4))(vue@3.5.13(typescript@5.7.3))
+ '@vue/devtools-api': 7.7.0
+ '@vue/shared': 3.5.13
+ '@vueuse/core': 12.5.0(typescript@5.7.3)
+ '@vueuse/integrations': 12.5.0(focus-trap@7.6.4)(typescript@5.7.3)
+ focus-trap: 7.6.4
+ mark.js: 8.11.1
+ minisearch: 7.1.1
+ shiki: 2.1.0
+ vite: 5.4.14(@types/node@22.10.7)(sass@1.83.4)
+ vue: 3.5.13(typescript@5.7.3)
+ optionalDependencies:
+ postcss: 8.5.1
+ transitivePeerDependencies:
+ - '@algolia/client-search'
+ - '@types/node'
+ - '@types/react'
+ - async-validator
+ - axios
+ - change-case
+ - drauu
+ - fuse.js
+ - idb-keyval
+ - jwt-decode
+ - less
+ - lightningcss
+ - nprogress
+ - qrcode
+ - react
+ - react-dom
+ - sass
+ - sass-embedded
+ - search-insights
+ - sortablejs
+ - stylus
+ - sugarss
+ - terser
+ - typescript
+ - universal-cookie
+
vitest@2.1.8(@types/node@22.10.7)(sass@1.83.4):
dependencies:
'@vitest/expect': 2.1.8
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 28756fa..2f0aa75 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,2 +1,3 @@
packages:
- docs
+ - e2e/dev