phos 1.0.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +3 -1
  2. package/package.json +1 -1
  3. package/src/templates/backend/elysia/src/api/user_api.ts +313 -0
  4. package/src/templates/backend/elysia/src/function/helper.ts +111 -0
  5. package/src/templates/backend/elysia/src/service/user_service.ts +109 -0
  6. package/src/templates/backend/elysia/src/sql/user_sql.ts +191 -0
  7. package/src/templates/backend/elysia/src/types/user_type.ts +54 -0
  8. package/src/templates/backend/fastapi/src/api/user_api.py +163 -0
  9. package/src/templates/backend/fastapi/src/function/__init__.py +0 -0
  10. package/src/templates/backend/fastapi/src/function/helper.py +107 -0
  11. package/src/templates/backend/fastapi/src/service/user_service.py +94 -0
  12. package/src/templates/backend/fastapi/src/sql/user_sql.py +197 -0
  13. package/src/templates/backend/fastapi/src/types/user_type.py +64 -0
  14. package/src/templates/frontend/vue/.editorconfig +8 -0
  15. package/src/templates/frontend/vue/.gitattributes +1 -0
  16. package/src/templates/frontend/vue/.oxlintrc.json +10 -0
  17. package/src/templates/frontend/vue/.prettierrc.json +6 -0
  18. package/src/templates/frontend/vue/.vscode/extensions.json +11 -0
  19. package/src/templates/frontend/vue/README.md +73 -0
  20. package/src/templates/frontend/vue/e2e/tsconfig.json +4 -0
  21. package/src/templates/frontend/vue/e2e/vue.spec.ts +8 -0
  22. package/src/templates/frontend/vue/env.d.ts +1 -0
  23. package/src/templates/frontend/vue/eslint.config.ts +38 -0
  24. package/src/templates/frontend/vue/index.html +13 -0
  25. package/src/templates/frontend/vue/package.json +54 -0
  26. package/src/templates/frontend/vue/playwright.config.ts +110 -0
  27. package/src/templates/frontend/vue/public/favicon.ico +0 -0
  28. package/src/templates/frontend/vue/src/App.vue +11 -0
  29. package/src/templates/frontend/vue/src/__tests__/App.spec.ts +11 -0
  30. package/src/templates/frontend/vue/src/main.ts +12 -0
  31. package/src/templates/frontend/vue/src/router/index.ts +8 -0
  32. package/src/templates/frontend/vue/src/stores/counter.ts +12 -0
  33. package/src/templates/frontend/vue/tsconfig.app.json +12 -0
  34. package/src/templates/frontend/vue/tsconfig.json +14 -0
  35. package/src/templates/frontend/vue/tsconfig.node.json +19 -0
  36. package/src/templates/frontend/vue/tsconfig.vitest.json +11 -0
  37. package/src/templates/frontend/vue/vite.config.ts +20 -0
  38. package/src/templates/frontend/vue/vitest.config.ts +14 -0
@@ -0,0 +1,197 @@
1
+ import bcrypt
2
+ from datetime import datetime
3
+ from src.db import get_db
4
+ from src.types.user_type import User, CreateUserDto, UpdateUserDto
5
+
6
+ SALT_ROUNDS = 10
7
+
8
+
9
+ async def get_users():
10
+ query = """
11
+ SELECT id, email, username, full_name, password, avatar_url, bio, role, is_active, created_at, updated_at
12
+ FROM users
13
+ WHERE is_active = true
14
+ ORDER BY created_at DESC
15
+ """
16
+ conn = await get_db()
17
+ result = await conn.fetch(query)
18
+ return result
19
+
20
+
21
+ async def get_user_by_id(user_id: int):
22
+ query = """
23
+ SELECT id, email, username, full_name, password, avatar_url, bio, role, is_active, created_at, updated_at
24
+ FROM users
25
+ WHERE id = $1
26
+ """
27
+ conn = await get_db()
28
+ result = await conn.fetchrow(query, user_id)
29
+ return result
30
+
31
+
32
+ async def get_user_by_email(email: str):
33
+ query = """
34
+ SELECT id, email, username, full_name, password, avatar_url, bio, role, is_active, created_at, updated_at
35
+ FROM users
36
+ WHERE email = $1
37
+ """
38
+ conn = await get_db()
39
+ result = await conn.fetchrow(query, email)
40
+ return result
41
+
42
+
43
+ async def get_user_by_username(username: str):
44
+ query = """
45
+ SELECT id, email, username, full_name, password, avatar_url, bio, role, is_active, created_at, updated_at
46
+ FROM users
47
+ WHERE username = $1
48
+ """
49
+ conn = await get_db()
50
+ result = await conn.fetchrow(query, username)
51
+ return result
52
+
53
+
54
+ async def create_user(data: CreateUserDto):
55
+ hashed_password = bcrypt.hashpw(data.password.encode("utf-8"), bcrypt.gensalt(SALT_ROUNDS)).decode("utf-8")
56
+
57
+ query = """
58
+ INSERT INTO users (email, username, full_name, password, avatar_url, bio, role, is_active, created_at, updated_at)
59
+ VALUES ($1, $2, $3, $4, $5, $6, $7, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
60
+ RETURNING id, email, username, full_name, password, avatar_url, bio, role, is_active, created_at, updated_at
61
+ """
62
+ conn = await get_db()
63
+ result = await conn.fetchrow(
64
+ query,
65
+ data.email,
66
+ data.username,
67
+ data.full_name,
68
+ hashed_password,
69
+ data.avatar_url,
70
+ data.bio,
71
+ data.role or "user",
72
+ )
73
+ return result
74
+
75
+
76
+ async def update_user(user_id: int, data: UpdateUserDto):
77
+ set_parts = []
78
+ values = []
79
+ param_index = 1
80
+
81
+ if data.email is not None:
82
+ set_parts.append(f"email = ${param_index}")
83
+ values.append(data.email)
84
+ param_index += 1
85
+
86
+ if data.username is not None:
87
+ set_parts.append(f"username = ${param_index}")
88
+ values.append(data.username)
89
+ param_index += 1
90
+
91
+ if data.full_name is not None:
92
+ set_parts.append(f"full_name = ${param_index}")
93
+ values.append(data.full_name)
94
+ param_index += 1
95
+
96
+ if data.password is not None:
97
+ hashed_password = bcrypt.hashpw(data.password.encode("utf-8"), bcrypt.gensalt(SALT_ROUNDS)).decode("utf-8")
98
+ set_parts.append(f"password = ${param_index}")
99
+ values.append(hashed_password)
100
+ param_index += 1
101
+
102
+ if data.avatar_url is not None:
103
+ set_parts.append(f"avatar_url = ${param_index}")
104
+ values.append(data.avatar_url)
105
+ param_index += 1
106
+
107
+ if data.bio is not None:
108
+ set_parts.append(f"bio = ${param_index}")
109
+ values.append(data.bio)
110
+ param_index += 1
111
+
112
+ if data.role is not None:
113
+ set_parts.append(f"role = ${param_index}")
114
+ values.append(data.role)
115
+ param_index += 1
116
+
117
+ if data.is_active is not None:
118
+ set_parts.append(f"is_active = ${param_index}")
119
+ values.append(data.is_active)
120
+ param_index += 1
121
+
122
+ if not set_parts:
123
+ return await get_user_by_id(user_id)
124
+
125
+ set_parts.append("updated_at = CURRENT_TIMESTAMP")
126
+ query = f"""
127
+ UPDATE users
128
+ SET {', '.join(set_parts)}
129
+ WHERE id = ${param_index}
130
+ RETURNING id, email, username, full_name, password, avatar_url, bio, role, is_active, created_at, updated_at
131
+ """
132
+ values.append(user_id)
133
+
134
+ conn = await get_db()
135
+ result = await conn.fetchrow(query, *values)
136
+ return result
137
+
138
+
139
+ async def delete_user(user_id: int) -> bool:
140
+ query = """
141
+ DELETE FROM users
142
+ WHERE id = $1
143
+ """
144
+ conn = await get_db()
145
+ result = await conn.execute(query, user_id)
146
+ return result == "DELETE 1"
147
+
148
+
149
+ async def soft_delete_user(user_id: int):
150
+ query = """
151
+ UPDATE users
152
+ SET is_active = false, updated_at = CURRENT_TIMESTAMP
153
+ WHERE id = $1
154
+ RETURNING id, email, username, full_name, password, avatar_url, bio, role, is_active, created_at, updated_at
155
+ """
156
+ conn = await get_db()
157
+ result = await conn.fetchrow(query, user_id)
158
+ return result
159
+
160
+
161
+ async def search_users(keyword: str):
162
+ query = """
163
+ SELECT id, email, username, full_name, password, avatar_url, bio, role, is_active, created_at, updated_at
164
+ FROM users
165
+ WHERE is_active = true
166
+ AND (email ILIKE $1 OR username ILIKE $1 OR full_name ILIKE $1)
167
+ ORDER BY created_at DESC
168
+ """
169
+ conn = await get_db()
170
+ result = await conn.fetch(query, f"%{keyword}%")
171
+ return result
172
+
173
+
174
+ def verify_password(plain_password: str, hashed_password: str) -> bool:
175
+ return bcrypt.checkpw(plain_password.encode("utf-8"), hashed_password.encode("utf-8"))
176
+
177
+
178
+ user_table_schema = """
179
+ CREATE TABLE IF NOT EXISTS users (
180
+ id SERIAL PRIMARY KEY,
181
+ email VARCHAR(255) UNIQUE NOT NULL,
182
+ username VARCHAR(100) UNIQUE NOT NULL,
183
+ full_name VARCHAR(255) NOT NULL,
184
+ password VARCHAR(255) NOT NULL,
185
+ avatar_url TEXT,
186
+ bio TEXT,
187
+ role VARCHAR(50) DEFAULT 'user',
188
+ is_active BOOLEAN DEFAULT true,
189
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
190
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
191
+ );
192
+
193
+ CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
194
+ CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
195
+ CREATE INDEX IF NOT EXISTS idx_users_role ON users(role);
196
+ CREATE INDEX IF NOT EXISTS idx_users_is_active ON users(is_active);
197
+ """
@@ -0,0 +1,64 @@
1
+ from datetime import datetime
2
+ from typing import Optional
3
+ from pydantic import BaseModel, EmailStr, Field
4
+
5
+
6
+ class User(BaseModel):
7
+ id: int
8
+ email: str
9
+ username: str
10
+ full_name: str
11
+ password: str
12
+ avatar_url: Optional[str] = None
13
+ bio: Optional[str] = None
14
+ role: str = "user"
15
+ is_active: bool = True
16
+ created_at: datetime
17
+ updated_at: datetime
18
+
19
+ class Config:
20
+ from_attributes = True
21
+
22
+
23
+ class CreateUserDto(BaseModel):
24
+ email: EmailStr
25
+ username: str = Field(..., min_length=3, max_length=100)
26
+ full_name: str = Field(..., min_length=1, max_length=255)
27
+ password: str = Field(..., min_length=6, max_length=255)
28
+ avatar_url: Optional[str] = Field(None, max_length=1000)
29
+ bio: Optional[str] = Field(None, max_length=1000)
30
+ role: Optional[str] = Field(None, max_length=50)
31
+
32
+
33
+ class UpdateUserDto(BaseModel):
34
+ email: Optional[EmailStr] = None
35
+ username: Optional[str] = Field(None, min_length=3, max_length=100)
36
+ full_name: Optional[str] = Field(None, min_length=1, max_length=255)
37
+ password: Optional[str] = Field(None, min_length=6, max_length=255)
38
+ avatar_url: Optional[str] = Field(None, max_length=1000)
39
+ bio: Optional[str] = Field(None, max_length=1000)
40
+ role: Optional[str] = Field(None, max_length=50)
41
+ is_active: Optional[bool] = None
42
+
43
+
44
+ class UserResponse(BaseModel):
45
+ id: int
46
+ email: str
47
+ username: str
48
+ full_name: str
49
+ avatar_url: Optional[str] = None
50
+ bio: Optional[str] = None
51
+ role: str
52
+ is_active: bool
53
+ created_at: datetime
54
+ updated_at: datetime
55
+
56
+ class Config:
57
+ from_attributes = True
58
+
59
+
60
+ class ApiResponse(BaseModel):
61
+ success: bool
62
+ data: Optional[any] = None
63
+ message: Optional[str] = None
64
+ error: Optional[str] = None
@@ -0,0 +1,8 @@
1
+ [*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
2
+ charset = utf-8
3
+ indent_size = 2
4
+ indent_style = space
5
+ insert_final_newline = true
6
+ trim_trailing_whitespace = true
7
+ end_of_line = lf
8
+ max_line_length = 100
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "plugins": ["eslint", "typescript", "unicorn", "oxc", "vue"],
4
+ "env": {
5
+ "browser": true
6
+ },
7
+ "categories": {
8
+ "correctness": "error"
9
+ }
10
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/prettierrc",
3
+ "semi": false,
4
+ "singleQuote": true,
5
+ "printWidth": 100
6
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "recommendations": [
3
+ "Vue.volar",
4
+ "vitest.explorer",
5
+ "ms-playwright.playwright",
6
+ "dbaeumer.vscode-eslint",
7
+ "EditorConfig.EditorConfig",
8
+ "oxc.oxc-vscode",
9
+ "esbenp.prettier-vscode"
10
+ ]
11
+ }
@@ -0,0 +1,73 @@
1
+ # vue
2
+
3
+ This template should help get you started developing with Vue 3 in Vite.
4
+
5
+ ## Recommended IDE Setup
6
+
7
+ [VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
8
+
9
+ ## Recommended Browser Setup
10
+
11
+ - Chromium-based browsers (Chrome, Edge, Brave, etc.):
12
+ - [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd)
13
+ - [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters)
14
+ - Firefox:
15
+ - [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/)
16
+ - [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/)
17
+
18
+ ## Type Support for `.vue` Imports in TS
19
+
20
+ TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
21
+
22
+ ## Customize configuration
23
+
24
+ See [Vite Configuration Reference](https://vite.dev/config/).
25
+
26
+ ## Project Setup
27
+
28
+ ```sh
29
+ npm install
30
+ ```
31
+
32
+ ### Compile and Hot-Reload for Development
33
+
34
+ ```sh
35
+ npm run dev
36
+ ```
37
+
38
+ ### Type-Check, Compile and Minify for Production
39
+
40
+ ```sh
41
+ npm run build
42
+ ```
43
+
44
+ ### Run Unit Tests with [Vitest](https://vitest.dev/)
45
+
46
+ ```sh
47
+ npm run test:unit
48
+ ```
49
+
50
+ ### Run End-to-End Tests with [Playwright](https://playwright.dev)
51
+
52
+ ```sh
53
+ # Install browsers for the first run
54
+ npx playwright install
55
+
56
+ # When testing on CI, must build the project first
57
+ npm run build
58
+
59
+ # Runs the end-to-end tests
60
+ npm run test:e2e
61
+ # Runs the tests only on Chromium
62
+ npm run test:e2e -- --project=chromium
63
+ # Runs the tests of a specific file
64
+ npm run test:e2e -- tests/example.spec.ts
65
+ # Runs the tests in debug mode
66
+ npm run test:e2e -- --debug
67
+ ```
68
+
69
+ ### Lint with [ESLint](https://eslint.org/)
70
+
71
+ ```sh
72
+ npm run lint
73
+ ```
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "@tsconfig/node24/tsconfig.json",
3
+ "include": ["./**/*"]
4
+ }
@@ -0,0 +1,8 @@
1
+ import { test, expect } from '@playwright/test'
2
+
3
+ // See here how to get started:
4
+ // https://playwright.dev/docs/intro
5
+ test('visits the app root url', async ({ page }) => {
6
+ await page.goto('/')
7
+ await expect(page.locator('h1')).toHaveText('You did it!')
8
+ })
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,38 @@
1
+ import { globalIgnores } from 'eslint/config'
2
+ import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
3
+ import pluginVue from 'eslint-plugin-vue'
4
+ import pluginPlaywright from 'eslint-plugin-playwright'
5
+ import pluginVitest from '@vitest/eslint-plugin'
6
+ import pluginOxlint from 'eslint-plugin-oxlint'
7
+ import skipFormatting from 'eslint-config-prettier/flat'
8
+
9
+ // To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
10
+ // import { configureVueProject } from '@vue/eslint-config-typescript'
11
+ // configureVueProject({ scriptLangs: ['ts', 'tsx'] })
12
+ // More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
13
+
14
+ export default defineConfigWithVueTs(
15
+ {
16
+ name: 'app/files-to-lint',
17
+ files: ['**/*.{vue,ts,mts,tsx}'],
18
+ },
19
+
20
+ globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
21
+
22
+ ...pluginVue.configs['flat/essential'],
23
+ vueTsConfigs.recommended,
24
+
25
+ {
26
+ ...pluginPlaywright.configs['flat/recommended'],
27
+ files: ['e2e/**/*.{test,spec}.{js,ts,jsx,tsx}'],
28
+ },
29
+
30
+ {
31
+ ...pluginVitest.configs.recommended,
32
+ files: ['src/**/__tests__/*'],
33
+ },
34
+
35
+ ...pluginOxlint.buildFromOxlintConfigFile('.oxlintrc.json'),
36
+
37
+ skipFormatting,
38
+ )
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html lang="">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <link rel="icon" href="/favicon.ico">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Vite App</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/src/main.ts"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "vue",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "run-p type-check \"build-only {@}\" --",
9
+ "preview": "vite preview",
10
+ "test:unit": "vitest",
11
+ "test:e2e": "playwright test",
12
+ "build-only": "vite build",
13
+ "type-check": "vue-tsc --build",
14
+ "lint": "run-s lint:*",
15
+ "lint:oxlint": "oxlint . --fix",
16
+ "lint:eslint": "eslint . --fix --cache",
17
+ "format": "prettier --write --experimental-cli src/"
18
+ },
19
+ "dependencies": {
20
+ "pinia": "^3.0.4",
21
+ "vue": "^3.5.27",
22
+ "vue-router": "^5.0.1"
23
+ },
24
+ "devDependencies": {
25
+ "@playwright/test": "^1.58.1",
26
+ "@tsconfig/node24": "^24.0.4",
27
+ "@types/jsdom": "^27.0.0",
28
+ "@types/node": "^24.10.9",
29
+ "@vitejs/plugin-vue": "^6.0.3",
30
+ "@vitejs/plugin-vue-jsx": "^5.1.3",
31
+ "@vitest/eslint-plugin": "^1.6.6",
32
+ "@vue/eslint-config-typescript": "^14.6.0",
33
+ "@vue/test-utils": "^2.4.6",
34
+ "@vue/tsconfig": "^0.8.1",
35
+ "eslint": "^9.39.2",
36
+ "eslint-config-prettier": "^10.1.8",
37
+ "eslint-plugin-oxlint": "~1.42.0",
38
+ "eslint-plugin-playwright": "^2.5.1",
39
+ "eslint-plugin-vue": "~10.7.0",
40
+ "jiti": "^2.6.1",
41
+ "jsdom": "^27.4.0",
42
+ "npm-run-all2": "^8.0.4",
43
+ "oxlint": "~1.42.0",
44
+ "prettier": "3.8.1",
45
+ "typescript": "~5.9.3",
46
+ "vite": "^7.3.1",
47
+ "vite-plugin-vue-devtools": "^8.0.5",
48
+ "vitest": "^4.0.18",
49
+ "vue-tsc": "^3.2.4"
50
+ },
51
+ "engines": {
52
+ "node": "^20.19.0 || >=22.12.0"
53
+ }
54
+ }
@@ -0,0 +1,110 @@
1
+ import process from 'node:process'
2
+ import { defineConfig, devices } from '@playwright/test'
3
+
4
+ /**
5
+ * Read environment variables from file.
6
+ * https://github.com/motdotla/dotenv
7
+ */
8
+ // require('dotenv').config();
9
+
10
+ /**
11
+ * See https://playwright.dev/docs/test-configuration.
12
+ */
13
+ export default defineConfig({
14
+ testDir: './e2e',
15
+ /* Maximum time one test can run for. */
16
+ timeout: 30 * 1000,
17
+ expect: {
18
+ /**
19
+ * Maximum time expect() should wait for the condition to be met.
20
+ * For example in `await expect(locator).toHaveText();`
21
+ */
22
+ timeout: 5000,
23
+ },
24
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
25
+ forbidOnly: !!process.env.CI,
26
+ /* Retry on CI only */
27
+ retries: process.env.CI ? 2 : 0,
28
+ /* Opt out of parallel tests on CI. */
29
+ workers: process.env.CI ? 1 : undefined,
30
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
31
+ reporter: 'html',
32
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
33
+ use: {
34
+ /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
35
+ actionTimeout: 0,
36
+ /* Base URL to use in actions like `await page.goto('/')`. */
37
+ baseURL: process.env.CI ? 'http://localhost:4173' : 'http://localhost:5173',
38
+
39
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
40
+ trace: 'on-first-retry',
41
+
42
+ /* Only on CI systems run the tests headless */
43
+ headless: !!process.env.CI,
44
+ },
45
+
46
+ /* Configure projects for major browsers */
47
+ projects: [
48
+ {
49
+ name: 'chromium',
50
+ use: {
51
+ ...devices['Desktop Chrome'],
52
+ },
53
+ },
54
+ {
55
+ name: 'firefox',
56
+ use: {
57
+ ...devices['Desktop Firefox'],
58
+ },
59
+ },
60
+ {
61
+ name: 'webkit',
62
+ use: {
63
+ ...devices['Desktop Safari'],
64
+ },
65
+ },
66
+
67
+ /* Test against mobile viewports. */
68
+ // {
69
+ // name: 'Mobile Chrome',
70
+ // use: {
71
+ // ...devices['Pixel 5'],
72
+ // },
73
+ // },
74
+ // {
75
+ // name: 'Mobile Safari',
76
+ // use: {
77
+ // ...devices['iPhone 12'],
78
+ // },
79
+ // },
80
+
81
+ /* Test against branded browsers. */
82
+ // {
83
+ // name: 'Microsoft Edge',
84
+ // use: {
85
+ // channel: 'msedge',
86
+ // },
87
+ // },
88
+ // {
89
+ // name: 'Google Chrome',
90
+ // use: {
91
+ // channel: 'chrome',
92
+ // },
93
+ // },
94
+ ],
95
+
96
+ /* Folder for test artifacts such as screenshots, videos, traces, etc. */
97
+ // outputDir: 'test-results/',
98
+
99
+ /* Run your local dev server before starting the tests */
100
+ webServer: {
101
+ /**
102
+ * Use the dev server by default for faster feedback loop.
103
+ * Use the preview server on CI for more realistic testing.
104
+ * Playwright will re-use the local server if there is already a dev-server running.
105
+ */
106
+ command: process.env.CI ? 'npm run preview' : 'npm run dev',
107
+ port: process.env.CI ? 4173 : 5173,
108
+ reuseExistingServer: !process.env.CI,
109
+ },
110
+ })
@@ -0,0 +1,11 @@
1
+ <script setup lang="ts"></script>
2
+
3
+ <template>
4
+ <h1>You did it!</h1>
5
+ <p>
6
+ Visit <a href="https://vuejs.org/" target="_blank" rel="noopener">vuejs.org</a> to read the
7
+ documentation
8
+ </p>
9
+ </template>
10
+
11
+ <style scoped></style>
@@ -0,0 +1,11 @@
1
+ import { describe, it, expect } from 'vitest'
2
+
3
+ import { mount } from '@vue/test-utils'
4
+ import App from '../App.vue'
5
+
6
+ describe('App', () => {
7
+ it('mounts renders properly', () => {
8
+ const wrapper = mount(App)
9
+ expect(wrapper.text()).toContain('You did it!')
10
+ })
11
+ })
@@ -0,0 +1,12 @@
1
+ import { createApp } from 'vue'
2
+ import { createPinia } from 'pinia'
3
+
4
+ import App from './App.vue'
5
+ import router from './router'
6
+
7
+ const app = createApp(App)
8
+
9
+ app.use(createPinia())
10
+ app.use(router)
11
+
12
+ app.mount('#app')
@@ -0,0 +1,8 @@
1
+ import { createRouter, createWebHistory } from 'vue-router'
2
+
3
+ const router = createRouter({
4
+ history: createWebHistory(import.meta.env.BASE_URL),
5
+ routes: [],
6
+ })
7
+
8
+ export default router
@@ -0,0 +1,12 @@
1
+ import { ref, computed } from 'vue'
2
+ import { defineStore } from 'pinia'
3
+
4
+ export const useCounterStore = defineStore('counter', () => {
5
+ const count = ref(0)
6
+ const doubleCount = computed(() => count.value * 2)
7
+ function increment() {
8
+ count.value++
9
+ }
10
+
11
+ return { count, doubleCount, increment }
12
+ })