create-m5kdev 0.25.2 → 0.26.1
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/create.cjs +18 -1
- package/dist/src/create.cjs.map +1 -1
- package/dist/src/fs.cjs +65 -5
- package/dist/src/fs.cjs.map +1 -1
- package/dist/src/fs.d.cts +13 -2
- package/dist/src/index.cjs +0 -0
- package/dist/src/paths.cjs +2 -2
- package/dist/src/prompts.cjs +22 -0
- package/dist/src/prompts.cjs.map +1 -1
- package/dist/src/runCli.cjs +13 -2
- package/dist/src/runCli.cjs.map +1 -1
- package/dist/src/types.d.cts +9 -1
- package/package.json +8 -6
- package/templates/minimal-app/.cursor/rules/frontend-data-guide.mdc +46 -3
- package/templates/minimal-app/.cursor/rules/module-db-guide.mdc +15 -1
- package/templates/minimal-app/.cursor/rules/module-dto-guide.mdc +60 -0
- package/templates/minimal-app/.cursor/rules/module-repository-guide.mdc +98 -193
- package/templates/minimal-app/.cursor/rules/module-schema-guide.mdc +53 -173
- package/templates/minimal-app/.cursor/rules/module-service-guide.mdc +133 -240
- package/templates/minimal-app/.cursor/rules/module-trpc-guide.mdc +71 -113
- package/templates/minimal-app/README.md.tpl +3 -3
- package/templates/minimal-app/apps/.gitignore.tpl +13 -0
- package/templates/minimal-app/apps/e2e/package.json.tpl +23 -0
- package/templates/minimal-app/apps/e2e/playwright.config.ts.tpl +39 -0
- package/templates/minimal-app/apps/e2e/playwright.expo.config.ts.tpl +38 -0
- package/templates/minimal-app/apps/e2e/servers.ts.tpl +100 -0
- package/templates/minimal-app/apps/e2e/tests/email.spec.ts.tpl +47 -0
- package/templates/minimal-app/apps/e2e/tests/expo-helpers.ts.tpl +73 -0
- package/templates/minimal-app/apps/e2e/tests/expo-standard.spec.ts.tpl +517 -0
- package/templates/minimal-app/apps/e2e/tests/expo-waitlist.spec.ts.tpl +94 -0
- package/templates/minimal-app/apps/e2e/tests/helpers.ts.tpl +289 -0
- package/templates/minimal-app/apps/e2e/tests/standard.spec.ts.tpl +516 -0
- package/templates/minimal-app/apps/e2e/tests/waitlist.spec.ts.tpl +107 -0
- package/templates/minimal-app/apps/e2e/tsconfig.json.tpl +10 -0
- package/templates/minimal-app/apps/email/package.json.tpl +34 -34
- package/templates/minimal-app/apps/email/src/emails/accountDeletionEmail.tsx.tpl +35 -34
- package/templates/minimal-app/apps/email/src/emails/organizationInviteEmail.tsx.tpl +34 -33
- package/templates/minimal-app/apps/email/src/emails/passwordResetEmail.tsx.tpl +35 -34
- package/templates/minimal-app/apps/email/src/emails/verificationEmail.tsx.tpl +37 -36
- package/templates/minimal-app/apps/email/src/emails/waitlistEmails.tsx.tpl +87 -0
- package/templates/minimal-app/apps/email/src/index.ts.tpl +72 -32
- package/templates/minimal-app/apps/email/src/resources.ts.tpl +65 -36
- package/templates/minimal-app/apps/email/tsconfig.json.tpl +16 -11
- package/templates/minimal-app/apps/expo/.gitignore.tpl +44 -0
- package/templates/minimal-app/apps/expo/.prettierignore +13 -0
- package/templates/minimal-app/apps/expo/.prettierrc.json.tpl +9 -0
- package/templates/minimal-app/apps/expo/CHANGELOG.md.tpl +156 -0
- package/templates/minimal-app/apps/expo/README.md.tpl +44 -0
- package/templates/minimal-app/apps/expo/app.json.tpl +36 -0
- package/templates/minimal-app/apps/expo/assets/images/adaptive-icon.png +0 -0
- package/templates/minimal-app/apps/expo/assets/images/favicon.png +0 -0
- package/templates/minimal-app/apps/expo/assets/images/icon.png +0 -0
- package/templates/minimal-app/apps/expo/assets/images/splash-icon.png +0 -0
- package/templates/minimal-app/apps/expo/eslint.config.js.tpl +8 -0
- package/templates/minimal-app/apps/expo/expo-env.d.ts.tpl +3 -0
- package/templates/minimal-app/apps/expo/metro.config.js.tpl +16 -0
- package/templates/minimal-app/apps/expo/package.json.tpl +59 -0
- package/templates/minimal-app/apps/expo/src/app/_layout.tsx.tpl +31 -0
- package/templates/minimal-app/apps/expo/src/app/claim-account.tsx.tpl +151 -0
- package/templates/minimal-app/apps/expo/src/app/forgot-password.tsx.tpl +53 -0
- package/templates/minimal-app/apps/expo/src/app/index.tsx.tpl +8 -0
- package/templates/minimal-app/apps/expo/src/app/login.tsx.tpl +71 -0
- package/templates/minimal-app/apps/expo/src/app/logout.tsx.tpl +19 -0
- package/templates/minimal-app/apps/expo/src/app/organization/accept-invitation.tsx.tpl +85 -0
- package/templates/minimal-app/apps/expo/src/app/posts.tsx.tpl +40 -0
- package/templates/minimal-app/apps/expo/src/app/reset-password.tsx.tpl +75 -0
- package/templates/minimal-app/apps/expo/src/app/signup.tsx.tpl +193 -0
- package/templates/minimal-app/apps/expo/src/components/AuthScaffold.tsx.tpl +34 -0
- package/templates/minimal-app/apps/expo/src/components/FormTextField.tsx.tpl +37 -0
- package/templates/minimal-app/apps/expo/src/components/LoadingScreen.tsx.tpl +10 -0
- package/templates/minimal-app/apps/expo/src/components/StatusText.tsx.tpl +22 -0
- package/templates/minimal-app/apps/expo/src/config.ts.tpl +9 -0
- package/templates/minimal-app/apps/expo/src/global.css.tpl +11 -0
- package/templates/minimal-app/apps/expo/src/lib/trpc.ts.tpl +20 -0
- package/templates/minimal-app/apps/expo/tsconfig.json.tpl +11 -0
- package/templates/minimal-app/apps/expo/uniwind-env.d.ts.tpl +1 -0
- package/templates/minimal-app/apps/expo/uniwind-types.d.ts.tpl +10 -0
- package/templates/minimal-app/apps/server/AGENTS.md.tpl +39 -39
- package/templates/minimal-app/apps/server/drizzle/0000_wealthy_absorbing_man.sql +268 -0
- package/templates/minimal-app/apps/server/drizzle/db.ts.tpl +26 -0
- package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +1890 -0
- package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +13 -0
- package/templates/minimal-app/apps/server/drizzle/reset.ts.tpl +33 -0
- package/templates/minimal-app/apps/server/drizzle/seed.e2e.ts.tpl +244 -0
- package/templates/minimal-app/apps/server/drizzle/seed.ts.tpl +181 -180
- package/templates/minimal-app/apps/server/drizzle/sync.ts.tpl +14 -21
- package/templates/minimal-app/apps/server/drizzle.config.ts.tpl +33 -35
- package/templates/minimal-app/apps/server/package.json.tpl +56 -52
- package/templates/minimal-app/apps/server/src/app.ts.tpl +142 -126
- package/templates/minimal-app/apps/server/src/index.ts.tpl +58 -58
- package/templates/minimal-app/apps/server/src/lib/auth.ts.tpl +12 -12
- package/templates/minimal-app/apps/server/src/modules/posts/posts.db.ts.tpl +25 -25
- package/templates/minimal-app/apps/server/src/modules/posts/posts.dto.ts.tpl +23 -0
- package/templates/minimal-app/apps/server/src/modules/posts/posts.grants.ts.tpl +30 -40
- package/templates/minimal-app/apps/server/src/modules/posts/posts.module.ts.tpl +54 -54
- package/templates/minimal-app/apps/server/src/modules/posts/posts.repository.ts.tpl +67 -112
- package/templates/minimal-app/apps/server/src/modules/posts/posts.service.ts.tpl +104 -170
- package/templates/minimal-app/apps/server/src/modules/posts/posts.trpc.ts.tpl +37 -48
- package/templates/minimal-app/apps/server/src/modules/posts/posts.utils.ts.tpl +19 -0
- package/templates/minimal-app/apps/server/src/modules/test-harness/test-harness.module.ts.tpl +279 -0
- package/templates/minimal-app/apps/server/src/schema.ts.tpl +20 -0
- package/templates/minimal-app/apps/server/src/types.ts.tpl +1 -1
- package/templates/minimal-app/apps/server/tsconfig.json.tpl +20 -14
- package/templates/minimal-app/apps/server/tsup.config.ts.tpl +9 -9
- package/templates/minimal-app/apps/shared/package.json.tpl +24 -24
- package/templates/minimal-app/apps/shared/src/modules/app/app.constants.ts.tpl +28 -2
- 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 -76
- package/templates/minimal-app/apps/shared/tsconfig.json.tpl +16 -12
- package/templates/minimal-app/apps/webapp/index.html.tpl +22 -22
- package/templates/minimal-app/apps/webapp/package.json.tpl +53 -52
- package/templates/minimal-app/apps/webapp/public/{push-sw.js → push-sw.js.tpl} +34 -34
- package/templates/minimal-app/apps/webapp/src/App.tsx.tpl +44 -13
- package/templates/minimal-app/apps/webapp/src/Layout.tsx.tpl +18 -12
- package/templates/minimal-app/apps/webapp/src/Router.tsx.tpl +33 -40
- package/templates/minimal-app/apps/webapp/src/index.css.tpl +15 -75
- package/templates/minimal-app/apps/webapp/src/main.tsx.tpl +26 -26
- package/templates/minimal-app/apps/webapp/src/modules/posts/PostsRoute.tsx.tpl +278 -307
- package/templates/minimal-app/apps/webapp/src/modules/posts/components/PostCard.tsx.tpl +96 -138
- package/templates/minimal-app/apps/webapp/src/modules/posts/components/PostEditorModal.tsx.tpl +118 -124
- package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostActions.ts.tpl +108 -116
- package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostsList.ts.tpl +15 -9
- package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostsRoute.ts.tpl +13 -13
- package/templates/minimal-app/apps/webapp/src/modules/posts/posts.utils.ts.tpl +16 -16
- 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 -6
- package/templates/minimal-app/apps/webapp/src/vite-env.d.ts.tpl +1 -1
- package/templates/minimal-app/apps/webapp/translations/{app.json.tpl → en/app.json.tpl} +7 -7
- package/templates/minimal-app/apps/webapp/translations/en/{blog-app.json.tpl → {{APP_SLUG}}.json.tpl} +135 -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/template.manifest.json +21 -0
- package/templates/minimal-app/apps/server/drizzle/generate-schema.ts.tpl +0 -31
- package/templates/minimal-app/apps/server/src/db.ts.tpl +0 -7
- package/templates/minimal-app/apps/server/src/generated/schema.ts.tpl +0 -13
- package/templates/minimal-app/apps/server/src/utils/trpc.ts.tpl +0 -8
- package/templates/minimal-app/apps/shared/src/modules/app/locale.constants.ts.tpl +0 -7
- package/templates/minimal-app/apps/shared/src/modules/app/roles.constants.ts.tpl +0 -17
- package/templates/minimal-app/apps/webapp/src/Providers.tsx.tpl +0 -36
- package/templates/minimal-app/apps/webapp/src/modules/notification/PushNotificationsPanel.tsx.tpl +0 -89
- package/templates/minimal-app/apps/webapp/src/navigation/navigation.config.tsx.tpl +0 -85
- /package/templates/minimal-app/apps/webapp/public/{logo.svg → logo.svg.tpl} +0 -0
|
@@ -0,0 +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
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
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 PORTS: Record<E2EProfile, { web: number; server: number; expoWeb: number; expoServer: number; redisDb: number; expoRedisDb: number }> = {
|
|
14
|
+
standard: { web: 15173, server: 18180, expoWeb: 15183, expoServer: 18182, redisDb: 1, expoRedisDb: 3 },
|
|
15
|
+
waitlist: { web: 15174, server: 18181, expoWeb: 15184, expoServer: 18183, redisDb: 2, expoRedisDb: 4 },
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function serverEnv(profile: E2EProfile, expo: boolean): Record<string, string> {
|
|
19
|
+
const ports = PORTS[profile];
|
|
20
|
+
const serverPort = expo ? ports.expoServer : ports.server;
|
|
21
|
+
const webPort = expo ? ports.expoWeb : ports.web;
|
|
22
|
+
const suffix = expo ? `${profile}-expo` : profile;
|
|
23
|
+
return {
|
|
24
|
+
PORT: String(serverPort),
|
|
25
|
+
VITE_SERVER_URL: `http://127.0.0.1:${serverPort}`,
|
|
26
|
+
VITE_APP_URL: `http://127.0.0.1:${webPort}`,
|
|
27
|
+
DATABASE_URL: `file:./.e2e/${suffix}.db`,
|
|
28
|
+
EMAIL_OUTPUT_DIRECTORY: `.e2e/emails-${suffix}`,
|
|
29
|
+
PROVISIONED_EMAIL_DOMAIN: "provisioned.auth-e2e.local",
|
|
30
|
+
REDIS_URL: `redis://127.0.0.1:6379/${expo ? PORTS[profile].expoRedisDb : PORTS[profile].redisDb}`,
|
|
31
|
+
VITE_ENABLE_WAITLIST: profile === "waitlist" ? "true" : "false",
|
|
32
|
+
AUTH_E2E_PROFILE: profile,
|
|
33
|
+
BETTER_AUTH_SECRET: "auth-e2e-local-secret-auth-e2e-local-secret",
|
|
34
|
+
SYSTEM_NOTIFICATION_EMAIL: "ops@auth-e2e.local",
|
|
35
|
+
NODE_ENV: "development",
|
|
36
|
+
// hermetic: a real key in the invoking shell would flip email mode to
|
|
37
|
+
// "send" and the stored-email assertions would find nothing
|
|
38
|
+
RESEND_API_KEY: "",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function serverWebServer(profile: E2EProfile, { expo = false } = {}) {
|
|
43
|
+
const env = serverEnv(profile, expo);
|
|
44
|
+
return {
|
|
45
|
+
command: [
|
|
46
|
+
`${SERVER_FILTER} tsx drizzle/reset.ts`,
|
|
47
|
+
`${SERVER_FILTER} drizzle-kit migrate`,
|
|
48
|
+
`${SERVER_FILTER} tsx drizzle/seed.e2e.ts`,
|
|
49
|
+
`${SERVER_FILTER} tsx src/index.ts`,
|
|
50
|
+
].join(" && "),
|
|
51
|
+
url: `${env.VITE_SERVER_URL}/__emails`,
|
|
52
|
+
reuseExistingServer: !process.env.CI,
|
|
53
|
+
timeout: 120_000,
|
|
54
|
+
env,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function webappWebServer(profile: E2EProfile) {
|
|
59
|
+
const ports = PORTS[profile];
|
|
60
|
+
const env = serverEnv(profile, false);
|
|
61
|
+
return {
|
|
62
|
+
command: `${WEBAPP_FILTER} vite --host 127.0.0.1 --port ${ports.web}`,
|
|
63
|
+
url: `http://127.0.0.1:${ports.web}`,
|
|
64
|
+
reuseExistingServer: !process.env.CI,
|
|
65
|
+
timeout: 120_000,
|
|
66
|
+
env: {
|
|
67
|
+
VITE_SERVER_URL: env.VITE_SERVER_URL,
|
|
68
|
+
VITE_APP_URL: env.VITE_APP_URL,
|
|
69
|
+
VITE_ENABLE_WAITLIST: env.VITE_ENABLE_WAITLIST,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function expoWebServer(profile: E2EProfile) {
|
|
75
|
+
const ports = PORTS[profile];
|
|
76
|
+
const env = serverEnv(profile, true);
|
|
77
|
+
return {
|
|
78
|
+
command: `pnpm --filter {{PACKAGE_SCOPE}}/expo exec expo start --web --port ${ports.expoWeb} --host localhost`,
|
|
79
|
+
url: `http://127.0.0.1:${ports.expoWeb}`,
|
|
80
|
+
reuseExistingServer: !process.env.CI,
|
|
81
|
+
timeout: 180_000,
|
|
82
|
+
env: {
|
|
83
|
+
EXPO_PUBLIC_SERVER_URL: env.VITE_SERVER_URL,
|
|
84
|
+
EXPO_PUBLIC_APP_URL: `http://127.0.0.1:${ports.expoWeb}`,
|
|
85
|
+
EXPO_PUBLIC_ENABLE_WAITLIST: env.VITE_ENABLE_WAITLIST,
|
|
86
|
+
EXPO_NO_TELEMETRY: "1",
|
|
87
|
+
BROWSER: "none",
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const BASE_URLS: Record<E2EProfile, string> = {
|
|
93
|
+
standard: `http://127.0.0.1:${PORTS.standard.web}`,
|
|
94
|
+
waitlist: `http://127.0.0.1:${PORTS.waitlist.web}`,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const EXPO_BASE_URLS: Record<E2EProfile, string> = {
|
|
98
|
+
standard: `http://127.0.0.1:${PORTS.standard.expoWeb}`,
|
|
99
|
+
waitlist: `http://127.0.0.1:${PORTS.waitlist.expoWeb}`,
|
|
100
|
+
};
|
|
@@ -0,0 +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
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { BackendTRPCRouter } from "@m5kdev/backend/types";
|
|
2
|
+
import { transformer } from "@m5kdev/commons/utils/trpc";
|
|
3
|
+
import type { Page } from "@playwright/test";
|
|
4
|
+
import { expect } from "@playwright/test";
|
|
5
|
+
import { createTRPCClient, httpBatchLink } from "@trpc/client";
|
|
6
|
+
import { type Profile, profiles } from "./helpers";
|
|
7
|
+
|
|
8
|
+
type FetchInit = NonNullable<Parameters<typeof fetch>[1]>;
|
|
9
|
+
|
|
10
|
+
async function cookieHeader(page: Page, profile: Profile) {
|
|
11
|
+
const cookies = await page.context().cookies(profiles[profile].serverUrl);
|
|
12
|
+
return cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join("; ");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function createExpoTrpcClient(page: Page, profile: Profile) {
|
|
16
|
+
const cookie = await cookieHeader(page, profile);
|
|
17
|
+
|
|
18
|
+
return createTRPCClient<BackendTRPCRouter>({
|
|
19
|
+
links: [
|
|
20
|
+
httpBatchLink({
|
|
21
|
+
url: `${profiles[profile].serverUrl}/trpc`,
|
|
22
|
+
fetch(url, options) {
|
|
23
|
+
const headers = new Headers(options?.headers);
|
|
24
|
+
if (cookie) {
|
|
25
|
+
headers.set("cookie", cookie);
|
|
26
|
+
}
|
|
27
|
+
return fetch(url, {
|
|
28
|
+
...options,
|
|
29
|
+
body: options?.body ? (options.body as FetchInit["body"]) : undefined,
|
|
30
|
+
headers,
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
transformer,
|
|
34
|
+
}),
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function expoSignUp(page: Page, email: string, password: string) {
|
|
40
|
+
await page.goto("/signup");
|
|
41
|
+
await page.getByTestId("signup-email").fill(email);
|
|
42
|
+
await page.getByTestId("signup-password").fill(password);
|
|
43
|
+
await page.getByTestId("signup-submit").click();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function expoLogin(page: Page, email: string, password: string) {
|
|
47
|
+
await page.goto("/login");
|
|
48
|
+
await page.getByTestId("login-email").fill(email);
|
|
49
|
+
await page.getByTestId("login-password").fill(password);
|
|
50
|
+
await page.getByTestId("login-submit").click();
|
|
51
|
+
await expect(page.getByTestId("session-email")).toContainText(email);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function expoLogout(page: Page) {
|
|
55
|
+
await page.goto("/logout");
|
|
56
|
+
await expect(page).toHaveURL(/\/login/);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function expectExpoLoginRejected(page: Page, email: string, password: string) {
|
|
60
|
+
await page.goto("/login");
|
|
61
|
+
await page.getByTestId("login-email").fill(email);
|
|
62
|
+
await page.getByTestId("login-password").fill(password);
|
|
63
|
+
const responsePromise = page.waitForResponse(
|
|
64
|
+
(response) =>
|
|
65
|
+
response.url().includes("/api/auth/sign-in/email") && response.request().method() === "POST"
|
|
66
|
+
);
|
|
67
|
+
await page.getByTestId("login-submit").click();
|
|
68
|
+
const response = await responsePromise;
|
|
69
|
+
expect(
|
|
70
|
+
response.ok(),
|
|
71
|
+
`${response.status()} ${response.statusText()}: ${await response.text()}`
|
|
72
|
+
).toBe(false);
|
|
73
|
+
}
|