create-m5kdev 0.33.0 → 0.35.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.
- package/dist/src/catalog.cjs +21 -1
- package/dist/src/catalog.cjs.map +1 -1
- package/dist/src/catalog.d.cts +3 -1
- package/dist/src/create.cjs +6 -2
- package/dist/src/create.cjs.map +1 -1
- package/dist/src/doctor.cjs.map +1 -1
- package/dist/src/fs.cjs +10 -5
- package/dist/src/fs.cjs.map +1 -1
- package/dist/src/prompts.cjs +32 -0
- package/dist/src/prompts.cjs.map +1 -1
- package/dist/src/prompts.d.cts +7 -1
- package/dist/src/template.cjs +23 -4
- package/dist/src/template.cjs.map +1 -1
- package/dist/src/template.d.cts +17 -3
- package/dist/src/types.d.cts +11 -4
- package/package.json +4 -1
- package/templates/minimal-app/.cursor/rules/frontend-data-guide.mdc +1 -0
- package/templates/minimal-app/.cursor/rules/module-dto-guide.mdc +1 -1
- package/templates/minimal-app/.cursor/rules/module-grants-guide.mdc +3 -3
- package/templates/minimal-app/.cursor/rules/module-module-guide.mdc +3 -2
- package/templates/minimal-app/.cursor/rules/module-service-guide.mdc +1 -1
- package/templates/minimal-app/.cursor/rules/server-event-emit.mdc +41 -0
- package/templates/minimal-app/.dockerignore.tpl +31 -0
- package/templates/minimal-app/.gitignore.tpl +1 -0
- package/templates/minimal-app/AGENTS.md.tpl +14 -10
- package/templates/minimal-app/CLAUDE.md.tpl +133 -131
- package/templates/minimal-app/README.md.tpl +51 -19
- package/templates/minimal-app/apps/e2e/package.json.tpl +23 -23
- package/templates/minimal-app/apps/e2e/playwright.config.ts.tpl +51 -39
- package/templates/minimal-app/apps/e2e/playwright.expo.config.ts.tpl +38 -38
- package/templates/minimal-app/apps/e2e/servers.ts.tpl +136 -112
- package/templates/minimal-app/apps/e2e/tests/conversation.spec.ts.tpl +22 -0
- package/templates/minimal-app/apps/e2e/tests/email.spec.ts.tpl +47 -47
- package/templates/minimal-app/apps/e2e/tests/expo-helpers.ts.tpl +73 -73
- package/templates/minimal-app/apps/e2e/tests/expo-standard.spec.ts.tpl +517 -517
- package/templates/minimal-app/apps/e2e/tests/expo-waitlist.spec.ts.tpl +94 -94
- package/templates/minimal-app/apps/e2e/tests/files.spec.ts.tpl +35 -0
- package/templates/minimal-app/apps/e2e/tests/helpers.ts.tpl +289 -289
- package/templates/minimal-app/apps/e2e/tests/standard.spec.ts.tpl +516 -516
- package/templates/minimal-app/apps/e2e/tests/waitlist.spec.ts.tpl +107 -107
- package/templates/minimal-app/apps/e2e/tests/workflow.spec.ts.tpl +26 -0
- package/templates/minimal-app/apps/e2e/tsconfig.json.tpl +10 -10
- package/templates/minimal-app/apps/email/package.json.tpl +34 -34
- package/templates/minimal-app/apps/email/src/emails/trialEndingEmail.tsx.tpl +40 -0
- package/templates/minimal-app/apps/email/src/index.ts.tpl +7 -0
- package/templates/minimal-app/apps/email/src/resources.ts.tpl +72 -65
- package/templates/minimal-app/apps/expo/app.json.tpl +36 -36
- package/templates/minimal-app/apps/expo/eslint.config.js.tpl +8 -8
- package/templates/minimal-app/apps/expo/metro.config.js.tpl +16 -16
- package/templates/minimal-app/apps/expo/src/app/_layout.tsx.tpl +31 -31
- package/templates/minimal-app/apps/expo/src/app/claim-account.tsx.tpl +151 -151
- package/templates/minimal-app/apps/expo/src/app/forgot-password.tsx.tpl +53 -53
- package/templates/minimal-app/apps/expo/src/app/index.tsx.tpl +8 -8
- package/templates/minimal-app/apps/expo/src/app/login.tsx.tpl +71 -71
- package/templates/minimal-app/apps/expo/src/app/logout.tsx.tpl +19 -19
- package/templates/minimal-app/apps/expo/src/app/organization/accept-invitation.tsx.tpl +85 -85
- package/templates/minimal-app/apps/expo/src/app/posts.tsx.tpl +40 -40
- package/templates/minimal-app/apps/expo/src/app/reset-password.tsx.tpl +75 -75
- package/templates/minimal-app/apps/expo/src/app/signup.tsx.tpl +193 -193
- package/templates/minimal-app/apps/expo/src/components/AuthScaffold.tsx.tpl +34 -34
- package/templates/minimal-app/apps/expo/src/components/FormTextField.tsx.tpl +37 -37
- package/templates/minimal-app/apps/expo/src/components/LoadingScreen.tsx.tpl +10 -10
- package/templates/minimal-app/apps/expo/src/components/StatusText.tsx.tpl +22 -22
- package/templates/minimal-app/apps/expo/src/config.ts.tpl +9 -9
- package/templates/minimal-app/apps/expo/src/global.css.tpl +11 -11
- package/templates/minimal-app/apps/expo/src/lib/trpc.ts.tpl +20 -20
- package/templates/minimal-app/apps/expo/tsconfig.json.tpl +11 -11
- package/templates/minimal-app/apps/expo/uniwind-env.d.ts.tpl +1 -1
- package/templates/minimal-app/apps/landing/.env.production.example.tpl +7 -0
- package/templates/minimal-app/apps/landing/Dockerfile.tpl +39 -0
- package/templates/minimal-app/apps/landing/fly.toml.tpl +20 -0
- package/templates/minimal-app/apps/landing/index.html.tpl +35 -0
- package/templates/minimal-app/apps/landing/package.json.tpl +34 -0
- package/templates/minimal-app/apps/landing/src/App.tsx.tpl +12 -0
- package/templates/minimal-app/apps/landing/src/LandingPage.tsx.tpl +37 -0
- package/templates/minimal-app/apps/landing/src/index.css.tpl +24 -0
- package/templates/minimal-app/apps/landing/src/main.tsx.tpl +16 -0
- package/templates/minimal-app/apps/landing/src/vite-env.d.ts.tpl +11 -0
- package/templates/minimal-app/apps/landing/tsconfig.json.tpl +12 -0
- package/templates/minimal-app/apps/landing/vite.config.ts.tpl +25 -0
- package/templates/minimal-app/apps/server/AGENTS.md.tpl +55 -46
- package/templates/minimal-app/apps/server/{drizzle/seed.e2e.ts.tpl → db.e2e.ts.tpl} +57 -40
- package/templates/minimal-app/apps/server/{drizzle/seed.ts.tpl → db.ts.tpl} +27 -20
- package/templates/minimal-app/apps/server/drizzle/0003_silly_the_liberteens.sql +27 -0
- package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +56 -166
- package/templates/minimal-app/apps/server/drizzle/meta/0001_snapshot.json.tpl +1796 -1910
- package/templates/minimal-app/apps/server/drizzle/meta/0002_snapshot.json.tpl +1818 -1932
- package/templates/minimal-app/apps/server/drizzle/meta/0003_snapshot.json.tpl +2137 -0
- package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +8 -1
- package/templates/minimal-app/apps/server/drizzle.config.ts.tpl +3 -33
- package/templates/minimal-app/apps/server/package.json.tpl +5 -3
- package/templates/minimal-app/apps/server/src/app.ts.tpl +36 -0
- package/templates/minimal-app/apps/server/src/modules/conversation/conversation.mastra.ts.tpl +26 -0
- package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.module.ts.tpl +51 -0
- package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.service.ts.tpl +51 -0
- package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.trpc.ts.tpl +16 -0
- package/templates/minimal-app/apps/server/src/modules/posts/posts.db.ts.tpl +28 -28
- package/templates/minimal-app/apps/server/src/modules/posts/posts.grants.ts.tpl +28 -28
- package/templates/minimal-app/apps/server/src/modules/posts/posts.module.ts.tpl +57 -54
- package/templates/minimal-app/apps/server/src/modules/posts/posts.repository.ts.tpl +67 -67
- package/templates/minimal-app/apps/server/src/modules/posts/posts.service.ts.tpl +116 -116
- package/templates/minimal-app/apps/server/src/modules/posts/posts.trpc.ts.tpl +37 -37
- package/templates/minimal-app/apps/server/src/modules/test-harness/test-harness.module.ts.tpl +279 -279
- package/templates/minimal-app/apps/server/src/schema.ts.tpl +31 -20
- package/templates/minimal-app/apps/server/src/types.ts.tpl +12 -12
- package/templates/minimal-app/apps/server/tsconfig.json.tpl +1 -1
- package/templates/minimal-app/apps/server/tsup.config.ts.tpl +23 -9
- package/templates/minimal-app/apps/shared/.env.example.tpl +5 -0
- package/templates/minimal-app/apps/shared/.env.production.example.tpl +23 -0
- package/templates/minimal-app/apps/shared/.env.tpl +5 -0
- package/templates/minimal-app/apps/shared/Dockerfile.tpl +48 -0
- package/templates/minimal-app/apps/shared/fly.toml.tpl +24 -0
- package/templates/minimal-app/apps/shared/package.json.tpl +26 -24
- package/templates/minimal-app/apps/shared/src/modules/app/app.constants.ts.tpl +28 -28
- package/templates/minimal-app/apps/shared/src/modules/conversation/conversation.constants.ts.tpl +1 -0
- package/templates/minimal-app/apps/shared/src/modules/demo-workflow/demo-workflow.constants.ts.tpl +1 -0
- package/templates/minimal-app/apps/shared/src/modules/notification/notification.schema.ts.tpl +3 -3
- package/templates/minimal-app/apps/shared/src/modules/posts/posts.constants.ts.tpl +5 -5
- package/templates/minimal-app/apps/shared/src/modules/posts/posts.schema.ts.tpl +25 -25
- package/templates/minimal-app/apps/webapp/package.json.tpl +53 -53
- package/templates/minimal-app/apps/webapp/public/push-sw.js.tpl +34 -34
- package/templates/minimal-app/apps/webapp/src/App.tsx.tpl +49 -44
- package/templates/minimal-app/apps/webapp/src/Layout.tsx.tpl +96 -65
- package/templates/minimal-app/apps/webapp/src/Router.tsx.tpl +114 -93
- package/templates/minimal-app/apps/webapp/src/StarterServerEventProvider.tsx.tpl +38 -0
- package/templates/minimal-app/apps/webapp/src/index.css.tpl +15 -15
- package/templates/minimal-app/apps/webapp/src/main.tsx.tpl +26 -26
- package/templates/minimal-app/apps/webapp/src/modules/conversation/ConversationRoute.tsx.tpl +36 -0
- package/templates/minimal-app/apps/webapp/src/modules/files/FilesRoute.tsx.tpl +88 -0
- package/templates/minimal-app/apps/webapp/src/modules/files/hooks/useFilesRoute.ts.tpl +50 -0
- package/templates/minimal-app/apps/webapp/src/modules/workflows/WorkflowsRoute.tsx.tpl +97 -0
- package/templates/minimal-app/apps/webapp/src/modules/workflows/hooks/useWorkflowsRoute.ts.tpl +64 -0
- package/templates/minimal-app/apps/webapp/src/utils/i18n.ts.tpl +13 -13
- package/templates/minimal-app/apps/webapp/src/utils/trpc.ts.tpl +10 -10
- package/templates/minimal-app/apps/webapp/src/vite-env.d.ts.tpl +1 -1
- package/templates/minimal-app/apps/webapp/translations/en/app.json.tpl +7 -7
- package/templates/minimal-app/apps/webapp/translations/en/{{APP_SLUG}}.json.tpl +193 -135
- package/templates/minimal-app/apps/webapp/tsconfig.json.tpl +17 -17
- package/templates/minimal-app/apps/webapp/vite.config.ts.tpl +31 -31
- package/templates/minimal-app/biome.json.tpl +17 -12
- package/templates/minimal-app/package.json.tpl +7 -2
- package/templates/minimal-app/pnpm-workspace.yaml.tpl +14 -7
- package/templates/minimal-app/template.manifest.json +93 -2
- package/templates/minimal-app/apps/server/drizzle/db.ts.tpl +0 -26
- package/templates/minimal-app/apps/server/drizzle/guard.ts.tpl +0 -36
- package/templates/minimal-app/apps/server/drizzle/reset.ts.tpl +0 -40
- package/templates/minimal-app/apps/server/drizzle/sync.ts.tpl +0 -16
|
@@ -1,39 +1,51 @@
|
|
|
1
|
-
import { defineConfig, devices } from "@playwright/test";
|
|
2
|
-
import { BASE_URLS, serverWebServer, webappWebServer } from "./servers";
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
testDir: "./tests",
|
|
6
|
-
fullyParallel: false,
|
|
7
|
-
workers: 1,
|
|
8
|
-
forbidOnly: Boolean(process.env.CI),
|
|
9
|
-
retries: process.env.CI ? 2 : 0,
|
|
10
|
-
reporter: [["list"], ["html", { open: "never" }]],
|
|
11
|
-
use: {
|
|
12
|
-
trace: "on-first-retry",
|
|
13
|
-
},
|
|
14
|
-
webServer: [
|
|
15
|
-
serverWebServer("standard"),
|
|
16
|
-
webappWebServer("standard"),
|
|
17
|
-
serverWebServer("waitlist"),
|
|
18
|
-
webappWebServer("waitlist"),
|
|
19
|
-
],
|
|
20
|
-
projects: [
|
|
21
|
-
{
|
|
22
|
-
name: "standard",
|
|
23
|
-
// exact filenames — a broad regex would also match the expo-*.spec files
|
|
24
|
-
testMatch: [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
1
|
+
import { defineConfig, devices } from "@playwright/test";
|
|
2
|
+
import { BASE_URLS, serverWebServer, webappWebServer } from "./servers";
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
testDir: "./tests",
|
|
6
|
+
fullyParallel: false,
|
|
7
|
+
workers: 1,
|
|
8
|
+
forbidOnly: Boolean(process.env.CI),
|
|
9
|
+
retries: process.env.CI ? 2 : 0,
|
|
10
|
+
reporter: [["list"], ["html", { open: "never" }]],
|
|
11
|
+
use: {
|
|
12
|
+
trace: "on-first-retry",
|
|
13
|
+
},
|
|
14
|
+
webServer: [
|
|
15
|
+
serverWebServer("standard"),
|
|
16
|
+
webappWebServer("standard"),
|
|
17
|
+
serverWebServer("waitlist"),
|
|
18
|
+
webappWebServer("waitlist"),
|
|
19
|
+
],
|
|
20
|
+
projects: [
|
|
21
|
+
{
|
|
22
|
+
name: "standard",
|
|
23
|
+
// exact filenames — a broad regex would also match the expo-*.spec files
|
|
24
|
+
testMatch: [
|
|
25
|
+
"standard.spec.ts",
|
|
26
|
+
"email.spec.ts",
|
|
27
|
+
// m5k:ai:start
|
|
28
|
+
"conversation.spec.ts",
|
|
29
|
+
// m5k:ai:end
|
|
30
|
+
// m5k:files:start
|
|
31
|
+
"files.spec.ts",
|
|
32
|
+
// m5k:files:end
|
|
33
|
+
// m5k:workflows:start
|
|
34
|
+
"workflow.spec.ts",
|
|
35
|
+
// m5k:workflows:end
|
|
36
|
+
],
|
|
37
|
+
use: {
|
|
38
|
+
...devices["Desktop Chrome"],
|
|
39
|
+
baseURL: BASE_URLS.standard,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "waitlist",
|
|
44
|
+
testMatch: ["waitlist.spec.ts", "email.spec.ts"],
|
|
45
|
+
use: {
|
|
46
|
+
...devices["Desktop Chrome"],
|
|
47
|
+
baseURL: BASE_URLS.waitlist,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
});
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import { defineConfig, devices } from "@playwright/test";
|
|
2
|
-
import { EXPO_BASE_URLS, expoWebServer, serverWebServer } from "./servers";
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
testDir: "./tests",
|
|
6
|
-
fullyParallel: false,
|
|
7
|
-
workers: 1,
|
|
8
|
-
forbidOnly: Boolean(process.env.CI),
|
|
9
|
-
retries: process.env.CI ? 2 : 0,
|
|
10
|
-
reporter: [["list"], ["html", { open: "never" }]],
|
|
11
|
-
use: {
|
|
12
|
-
trace: "on-first-retry",
|
|
13
|
-
},
|
|
14
|
-
webServer: [
|
|
15
|
-
serverWebServer("standard", { expo: true }),
|
|
16
|
-
expoWebServer("standard"),
|
|
17
|
-
serverWebServer("waitlist", { expo: true }),
|
|
18
|
-
expoWebServer("waitlist"),
|
|
19
|
-
],
|
|
20
|
-
projects: [
|
|
21
|
-
{
|
|
22
|
-
name: "expo-standard",
|
|
23
|
-
testMatch: /.*expo-standard\.spec\.ts/,
|
|
24
|
-
use: {
|
|
25
|
-
...devices["Desktop Chrome"],
|
|
26
|
-
baseURL: EXPO_BASE_URLS.standard,
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: "expo-waitlist",
|
|
31
|
-
testMatch: /.*expo-waitlist\.spec\.ts/,
|
|
32
|
-
use: {
|
|
33
|
-
...devices["Desktop Chrome"],
|
|
34
|
-
baseURL: EXPO_BASE_URLS.waitlist,
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
});
|
|
1
|
+
import { defineConfig, devices } from "@playwright/test";
|
|
2
|
+
import { EXPO_BASE_URLS, expoWebServer, serverWebServer } from "./servers";
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
testDir: "./tests",
|
|
6
|
+
fullyParallel: false,
|
|
7
|
+
workers: 1,
|
|
8
|
+
forbidOnly: Boolean(process.env.CI),
|
|
9
|
+
retries: process.env.CI ? 2 : 0,
|
|
10
|
+
reporter: [["list"], ["html", { open: "never" }]],
|
|
11
|
+
use: {
|
|
12
|
+
trace: "on-first-retry",
|
|
13
|
+
},
|
|
14
|
+
webServer: [
|
|
15
|
+
serverWebServer("standard", { expo: true }),
|
|
16
|
+
expoWebServer("standard"),
|
|
17
|
+
serverWebServer("waitlist", { expo: true }),
|
|
18
|
+
expoWebServer("waitlist"),
|
|
19
|
+
],
|
|
20
|
+
projects: [
|
|
21
|
+
{
|
|
22
|
+
name: "expo-standard",
|
|
23
|
+
testMatch: /.*expo-standard\.spec\.ts/,
|
|
24
|
+
use: {
|
|
25
|
+
...devices["Desktop Chrome"],
|
|
26
|
+
baseURL: EXPO_BASE_URLS.standard,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "expo-waitlist",
|
|
31
|
+
testMatch: /.*expo-waitlist\.spec\.ts/,
|
|
32
|
+
use: {
|
|
33
|
+
...devices["Desktop Chrome"],
|
|
34
|
+
baseURL: EXPO_BASE_URLS.waitlist,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
});
|
|
@@ -1,112 +1,136 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared webServer definitions for the Playwright configs. Each profile runs
|
|
3
|
-
* an isolated stack: own port pair, own SQLite file, own email output dir, and
|
|
4
|
-
* own Redis DB index (queue names are identical across profiles — sharing a
|
|
5
|
-
* Redis DB would let one profile's workers consume the other's jobs).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export type E2EProfile = "standard" | "waitlist";
|
|
9
|
-
|
|
10
|
-
const SERVER_FILTER = "pnpm --filter {{PACKAGE_SCOPE}}/server exec";
|
|
11
|
-
const WEBAPP_FILTER = "pnpm --filter {{PACKAGE_SCOPE}}/webapp exec";
|
|
12
|
-
|
|
13
|
-
const SIGNOZ_OTLP_ENDPOINT =
|
|
14
|
-
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ??
|
|
15
|
-
"https://signoz-ingester-production-9090.up.railway.app";
|
|
16
|
-
|
|
17
|
-
const PORTS: Record<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Shared webServer definitions for the Playwright configs. Each profile runs
|
|
3
|
+
* an isolated stack: own port pair, own SQLite file, own email output dir, and
|
|
4
|
+
* own Redis DB index (queue names are identical across profiles — sharing a
|
|
5
|
+
* Redis DB would let one profile's workers consume the other's jobs).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type E2EProfile = "standard" | "waitlist";
|
|
9
|
+
|
|
10
|
+
const SERVER_FILTER = "pnpm --filter {{PACKAGE_SCOPE}}/server exec";
|
|
11
|
+
const WEBAPP_FILTER = "pnpm --filter {{PACKAGE_SCOPE}}/webapp exec";
|
|
12
|
+
|
|
13
|
+
const SIGNOZ_OTLP_ENDPOINT =
|
|
14
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ??
|
|
15
|
+
"https://signoz-ingester-production-9090.up.railway.app";
|
|
16
|
+
|
|
17
|
+
const PORTS: Record<
|
|
18
|
+
E2EProfile,
|
|
19
|
+
{
|
|
20
|
+
web: number;
|
|
21
|
+
server: number;
|
|
22
|
+
expoWeb: number;
|
|
23
|
+
expoServer: number;
|
|
24
|
+
redisDb: number;
|
|
25
|
+
expoRedisDb: number;
|
|
26
|
+
}
|
|
27
|
+
> = {
|
|
28
|
+
standard: {
|
|
29
|
+
web: 15173,
|
|
30
|
+
server: 18180,
|
|
31
|
+
expoWeb: 15183,
|
|
32
|
+
expoServer: 18182,
|
|
33
|
+
redisDb: 1,
|
|
34
|
+
expoRedisDb: 3,
|
|
35
|
+
},
|
|
36
|
+
waitlist: {
|
|
37
|
+
web: 15174,
|
|
38
|
+
server: 18181,
|
|
39
|
+
expoWeb: 15184,
|
|
40
|
+
expoServer: 18183,
|
|
41
|
+
redisDb: 2,
|
|
42
|
+
expoRedisDb: 4,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function serverEnv(profile: E2EProfile, expo: boolean): Record<string, string> {
|
|
47
|
+
const ports = PORTS[profile];
|
|
48
|
+
const serverPort = expo ? ports.expoServer : ports.server;
|
|
49
|
+
const webPort = expo ? ports.expoWeb : ports.web;
|
|
50
|
+
const suffix = expo ? `${profile}-expo` : profile;
|
|
51
|
+
return {
|
|
52
|
+
PORT: String(serverPort),
|
|
53
|
+
VITE_SERVER_URL: `http://127.0.0.1:${serverPort}`,
|
|
54
|
+
VITE_APP_URL: `http://127.0.0.1:${webPort}`,
|
|
55
|
+
DATABASE_URL: `file:./.e2e/${suffix}.db`,
|
|
56
|
+
EMAIL_OUTPUT_DIRECTORY: `.e2e/emails-${suffix}`,
|
|
57
|
+
PROVISIONED_EMAIL_DOMAIN: "provisioned.auth-e2e.local",
|
|
58
|
+
REDIS_URL: `redis://127.0.0.1:6379/${expo ? PORTS[profile].expoRedisDb : PORTS[profile].redisDb}`,
|
|
59
|
+
VITE_ENABLE_WAITLIST: profile === "waitlist" ? "true" : "false",
|
|
60
|
+
AUTH_E2E_PROFILE: profile,
|
|
61
|
+
BETTER_AUTH_SECRET: "auth-e2e-local-secret-auth-e2e-local-secret",
|
|
62
|
+
SYSTEM_NOTIFICATION_EMAIL: "ops@auth-e2e.local",
|
|
63
|
+
NODE_ENV: "development",
|
|
64
|
+
// hermetic: a real key in the invoking shell would flip email mode to
|
|
65
|
+
// "send" and the stored-email assertions would find nothing
|
|
66
|
+
RESEND_API_KEY: "",
|
|
67
|
+
...(process.env.OTEL_SDK_DISABLED === "true"
|
|
68
|
+
? {}
|
|
69
|
+
: {
|
|
70
|
+
OTEL_EXPORTER_OTLP_ENDPOINT: SIGNOZ_OTLP_ENDPOINT,
|
|
71
|
+
OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
|
|
72
|
+
OTEL_SERVICE_NAME: `starter-server-e2e-${suffix}`,
|
|
73
|
+
OTEL_RESOURCE_ATTRIBUTES: `deployment.environment=e2e,e2e.profile=${profile},e2e.platform=${expo ? "expo" : "web"}`,
|
|
74
|
+
}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function serverWebServer(profile: E2EProfile, { expo = false } = {}) {
|
|
79
|
+
const env = serverEnv(profile, expo);
|
|
80
|
+
return {
|
|
81
|
+
command: [
|
|
82
|
+
`${SERVER_FILTER} tsx db.ts reset`,
|
|
83
|
+
`${SERVER_FILTER} drizzle-kit migrate`,
|
|
84
|
+
`${SERVER_FILTER} tsx db.e2e.ts seed`,
|
|
85
|
+
`${SERVER_FILTER} tsx src/index.ts`,
|
|
86
|
+
].join(" && "),
|
|
87
|
+
url: `${env.VITE_SERVER_URL}/__emails`,
|
|
88
|
+
reuseExistingServer: !process.env.CI,
|
|
89
|
+
timeout: 120_000,
|
|
90
|
+
env,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function webappWebServer(profile: E2EProfile) {
|
|
95
|
+
const ports = PORTS[profile];
|
|
96
|
+
const env = serverEnv(profile, false);
|
|
97
|
+
return {
|
|
98
|
+
command: `${WEBAPP_FILTER} vite --host 127.0.0.1 --port ${ports.web}`,
|
|
99
|
+
url: `http://127.0.0.1:${ports.web}`,
|
|
100
|
+
reuseExistingServer: !process.env.CI,
|
|
101
|
+
timeout: 120_000,
|
|
102
|
+
env: {
|
|
103
|
+
VITE_SERVER_URL: env.VITE_SERVER_URL,
|
|
104
|
+
VITE_APP_URL: env.VITE_APP_URL,
|
|
105
|
+
VITE_ENABLE_WAITLIST: env.VITE_ENABLE_WAITLIST,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function expoWebServer(profile: E2EProfile) {
|
|
111
|
+
const ports = PORTS[profile];
|
|
112
|
+
const env = serverEnv(profile, true);
|
|
113
|
+
return {
|
|
114
|
+
command: `pnpm --filter {{PACKAGE_SCOPE}}/expo exec expo start --web --port ${ports.expoWeb} --host localhost`,
|
|
115
|
+
url: `http://127.0.0.1:${ports.expoWeb}`,
|
|
116
|
+
reuseExistingServer: !process.env.CI,
|
|
117
|
+
timeout: 180_000,
|
|
118
|
+
env: {
|
|
119
|
+
EXPO_PUBLIC_SERVER_URL: env.VITE_SERVER_URL,
|
|
120
|
+
EXPO_PUBLIC_APP_URL: `http://127.0.0.1:${ports.expoWeb}`,
|
|
121
|
+
EXPO_PUBLIC_ENABLE_WAITLIST: env.VITE_ENABLE_WAITLIST,
|
|
122
|
+
EXPO_NO_TELEMETRY: "1",
|
|
123
|
+
BROWSER: "none",
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const BASE_URLS: Record<E2EProfile, string> = {
|
|
129
|
+
standard: `http://127.0.0.1:${PORTS.standard.web}`,
|
|
130
|
+
waitlist: `http://127.0.0.1:${PORTS.waitlist.web}`,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export const EXPO_BASE_URLS: Record<E2EProfile, string> = {
|
|
134
|
+
standard: `http://127.0.0.1:${PORTS.standard.expoWeb}`,
|
|
135
|
+
waitlist: `http://127.0.0.1:${PORTS.waitlist.expoWeb}`,
|
|
136
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
|
+
import { clearEmails, login, signUp, verifyLatestEmail } from "./helpers";
|
|
3
|
+
|
|
4
|
+
const profile = "standard" as const;
|
|
5
|
+
|
|
6
|
+
test.beforeEach(async ({ request }) => {
|
|
7
|
+
await clearEmails(request, profile);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("open the agent conversation and see an empty transcript", async ({ page, request }) => {
|
|
11
|
+
const email = `conversation.${Date.now()}@auth-e2e.local`;
|
|
12
|
+
const password = "password1234";
|
|
13
|
+
|
|
14
|
+
await signUp(page, email, password);
|
|
15
|
+
await verifyLatestEmail(request, profile, email);
|
|
16
|
+
await login(page, email, password);
|
|
17
|
+
|
|
18
|
+
await page.goto("/conversation");
|
|
19
|
+
await expect(page.getByTestId("conversation-route")).toBeVisible();
|
|
20
|
+
await expect(page.getByText("No messages yet")).toBeVisible();
|
|
21
|
+
await expect(page.getByRole("button", { name: "Send" })).toBeVisible();
|
|
22
|
+
});
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import { expect, test } from "@playwright/test";
|
|
2
|
-
import { clearEmails, latestEmail, profileFrom, profiles } from "./helpers";
|
|
3
|
-
|
|
4
|
-
test("stored email inbox renders and exposes JSON helpers", async ({ page, request }, testInfo) => {
|
|
5
|
-
const profile = profileFrom(testInfo);
|
|
6
|
-
await clearEmails(request, profile);
|
|
7
|
-
|
|
8
|
-
await page.goto("/forgot-password");
|
|
9
|
-
await page.locator('[name="forgot-password-email"]').fill(profiles[profile].adminEmail);
|
|
10
|
-
const resetRequestPromise = page.waitForResponse(
|
|
11
|
-
(response) =>
|
|
12
|
-
response.url().includes("/api/auth/request-password-reset") &&
|
|
13
|
-
response.request().method() === "POST"
|
|
14
|
-
);
|
|
15
|
-
await page.getByRole("button", { name: /reset password/i }).click();
|
|
16
|
-
const resetRequest = await resetRequestPromise;
|
|
17
|
-
expect(
|
|
18
|
-
resetRequest.ok(),
|
|
19
|
-
`${resetRequest.status()} ${resetRequest.statusText()}: ${await resetRequest.text()}`
|
|
20
|
-
).toBe(true);
|
|
21
|
-
|
|
22
|
-
const email = await latestEmail(request, profile, {
|
|
23
|
-
to: profiles[profile].adminEmail,
|
|
24
|
-
templateId: "password-reset",
|
|
25
|
-
});
|
|
26
|
-
expect(email.subject).toContain("Reset");
|
|
27
|
-
expect(email.html).toContain("Reset your password");
|
|
28
|
-
|
|
29
|
-
await page.goto(`${profiles[profile].serverUrl}/__emails`);
|
|
30
|
-
await expect(page.getByRole("heading", { name: "Stored emails" })).toBeVisible();
|
|
31
|
-
await page.goto(`${profiles[profile].serverUrl}/__emails/${email.id}`);
|
|
32
|
-
await expect(page.getByRole("heading", { name: email.subject })).toBeVisible();
|
|
33
|
-
|
|
34
|
-
const rawResponse = await request.get(
|
|
35
|
-
`${profiles[profile].serverUrl}/__emails/${email.id}/raw.html`
|
|
36
|
-
);
|
|
37
|
-
expect(rawResponse.ok()).toBe(true);
|
|
38
|
-
expect(await rawResponse.text()).toContain("Reset your password");
|
|
39
|
-
|
|
40
|
-
const jsonResponse = await request.get(
|
|
41
|
-
`${profiles[profile].serverUrl}/__emails/${email.id}.json`
|
|
42
|
-
);
|
|
43
|
-
expect(jsonResponse.ok()).toBe(true);
|
|
44
|
-
const payload = await jsonResponse.json();
|
|
45
|
-
expect(payload.props).toEqual(expect.objectContaining({ url: expect.any(String) }));
|
|
46
|
-
expect(payload.html).toContain("Reset your password");
|
|
47
|
-
});
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
|
+
import { clearEmails, latestEmail, profileFrom, profiles } from "./helpers";
|
|
3
|
+
|
|
4
|
+
test("stored email inbox renders and exposes JSON helpers", async ({ page, request }, testInfo) => {
|
|
5
|
+
const profile = profileFrom(testInfo);
|
|
6
|
+
await clearEmails(request, profile);
|
|
7
|
+
|
|
8
|
+
await page.goto("/forgot-password");
|
|
9
|
+
await page.locator('[name="forgot-password-email"]').fill(profiles[profile].adminEmail);
|
|
10
|
+
const resetRequestPromise = page.waitForResponse(
|
|
11
|
+
(response) =>
|
|
12
|
+
response.url().includes("/api/auth/request-password-reset") &&
|
|
13
|
+
response.request().method() === "POST"
|
|
14
|
+
);
|
|
15
|
+
await page.getByRole("button", { name: /reset password/i }).click();
|
|
16
|
+
const resetRequest = await resetRequestPromise;
|
|
17
|
+
expect(
|
|
18
|
+
resetRequest.ok(),
|
|
19
|
+
`${resetRequest.status()} ${resetRequest.statusText()}: ${await resetRequest.text()}`
|
|
20
|
+
).toBe(true);
|
|
21
|
+
|
|
22
|
+
const email = await latestEmail(request, profile, {
|
|
23
|
+
to: profiles[profile].adminEmail,
|
|
24
|
+
templateId: "password-reset",
|
|
25
|
+
});
|
|
26
|
+
expect(email.subject).toContain("Reset");
|
|
27
|
+
expect(email.html).toContain("Reset your password");
|
|
28
|
+
|
|
29
|
+
await page.goto(`${profiles[profile].serverUrl}/__emails`);
|
|
30
|
+
await expect(page.getByRole("heading", { name: "Stored emails" })).toBeVisible();
|
|
31
|
+
await page.goto(`${profiles[profile].serverUrl}/__emails/${email.id}`);
|
|
32
|
+
await expect(page.getByRole("heading", { name: email.subject })).toBeVisible();
|
|
33
|
+
|
|
34
|
+
const rawResponse = await request.get(
|
|
35
|
+
`${profiles[profile].serverUrl}/__emails/${email.id}/raw.html`
|
|
36
|
+
);
|
|
37
|
+
expect(rawResponse.ok()).toBe(true);
|
|
38
|
+
expect(await rawResponse.text()).toContain("Reset your password");
|
|
39
|
+
|
|
40
|
+
const jsonResponse = await request.get(
|
|
41
|
+
`${profiles[profile].serverUrl}/__emails/${email.id}.json`
|
|
42
|
+
);
|
|
43
|
+
expect(jsonResponse.ok()).toBe(true);
|
|
44
|
+
const payload = await jsonResponse.json();
|
|
45
|
+
expect(payload.props).toEqual(expect.objectContaining({ url: expect.any(String) }));
|
|
46
|
+
expect(payload.html).toContain("Reset your password");
|
|
47
|
+
});
|