vela 0.21.0 → 0.23.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.
@@ -0,0 +1,21 @@
1
+ import type { TestProject } from 'vitest/node';
2
+ /** What a server-test run talks to; `inject('vela')` in a test or setup file. */
3
+ export interface TestServers {
4
+ /** The throwaway PocketBase, migrated and loaded with seeds and fixtures. */
5
+ pocketbaseUrl: string;
6
+ /** The app, served by Vite in test mode with its pages stubbed out. */
7
+ appUrl: string;
8
+ /** The superuser the harness created in the throwaway database. */
9
+ superuser: {
10
+ email: string;
11
+ password: string;
12
+ };
13
+ /** The run's data directory: PocketBase's, uploads, the CMS database. */
14
+ dataDir: string;
15
+ }
16
+ declare module 'vitest' {
17
+ interface ProvidedContext {
18
+ vela: TestServers;
19
+ }
20
+ }
21
+ export default function setup(project: TestProject): Promise<() => Promise<void>>;
@@ -0,0 +1,43 @@
1
+ // src/testing.ts
2
+ import { spawn } from "node:child_process";
3
+ import path from "node:path";
4
+ import process from "node:process";
5
+ import { fileURLToPath } from "node:url";
6
+ import dotenv from "dotenv";
7
+ async function setup(project) {
8
+ const root = project.config.root;
9
+ dotenv.config({ path: path.join(root, ".env"), quiet: true });
10
+ const bin = fileURLToPath(new URL("./bin.js", import.meta.url));
11
+ const child = spawn(process.execPath, [bin, "test:harness"], {
12
+ cwd: root,
13
+ // `test.env` from the config reaches the app too, not only the workers,
14
+ // so a suite's settings live in one place.
15
+ env: { ...process.env, ...project.config.env },
16
+ stdio: ["ignore", "inherit", "inherit", "ipc"]
17
+ });
18
+ const exited = new Promise((resolve) => {
19
+ child.once("exit", (code) => resolve(code));
20
+ });
21
+ const ready = await new Promise((resolve, reject) => {
22
+ child.on("message", (message) => {
23
+ if (message.type === "ready") resolve(message);
24
+ else reject(new Error(message.message));
25
+ });
26
+ void exited.then(
27
+ (code) => reject(new Error(`vela test:harness exited with code ${code} before its servers were up`))
28
+ );
29
+ });
30
+ Object.assign(process.env, ready.env);
31
+ project.provide("vela", ready.servers);
32
+ return async () => {
33
+ if (child.exitCode !== null) return;
34
+ child.send("stop");
35
+ const timer = setTimeout(() => child.kill(), 5e3);
36
+ await exited;
37
+ clearTimeout(timer);
38
+ };
39
+ }
40
+ export {
41
+ setup as default
42
+ };
43
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/testing.ts"],
4
+ "sourcesContent": ["/**\n * Vitest support for vela projects.\n *\n * The default export is a vitest global setup: it starts the servers the\n * server tests need (a throwaway PocketBase with the app's hooks, seeds and\n * fixtures, and the app under Vite in test mode) before the first test file\n * runs, and stops them after the last. A project names it in\n * `vitest.config.ts`:\n *\n * ```ts\n * test: { globalSetup: ['vela/testing'] }\n * ```\n *\n * The servers run in a child process (`vela test:harness`), so vitest, bare\n * or through `vela test:server`, in watch mode or from an editor, boots the\n * same thing. Tests reach the servers through `inject('vela')`, and through\n * the environment `vela test:server` always set: `POCKETBASE_URL`,\n * `POCKETBASE_SUPERUSER_EMAIL`, `POCKETBASE_SUPERUSER_PASSWORD`,\n * `VELA_DATA_DIR`, `VITE_TEST_URL` and `TEST=true`.\n */\nimport { spawn } from 'node:child_process';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { fileURLToPath } from 'node:url';\nimport dotenv from 'dotenv';\nimport type { TestProject } from 'vitest/node';\n\n/** What a server-test run talks to; `inject('vela')` in a test or setup file. */\nexport interface TestServers {\n\t/** The throwaway PocketBase, migrated and loaded with seeds and fixtures. */\n\tpocketbaseUrl: string;\n\t/** The app, served by Vite in test mode with its pages stubbed out. */\n\tappUrl: string;\n\t/** The superuser the harness created in the throwaway database. */\n\tsuperuser: { email: string; password: string };\n\t/** The run's data directory: PocketBase's, uploads, the CMS database. */\n\tdataDir: string;\n}\n\ndeclare module 'vitest' {\n\tinterface ProvidedContext {\n\t\tvela: TestServers;\n\t}\n}\n\ntype HarnessMessage =\n\t| { type: 'ready'; servers: TestServers; env: Record<string, string> }\n\t| { type: 'error'; message: string };\n\nexport default async function setup(project: TestProject): Promise<() => Promise<void>> {\n\tconst root = project.config.root;\n\t// The project's `.env`, as every vela command loads it: the app reads it\n\t// through Vite either way, and a test that signs a request with the same\n\t// secret must see the same value. Nothing already set is replaced, and the\n\t// harness's own variables are assigned over it below.\n\tdotenv.config({ path: path.join(root, '.env'), quiet: true });\n\n\t// This package's own bin, so the harness is the vela the project installed,\n\t// whatever is on the PATH.\n\tconst bin = fileURLToPath(new URL('./bin.js', import.meta.url));\n\tconst child = spawn(process.execPath, [bin, 'test:harness'], {\n\t\tcwd: root,\n\t\t// `test.env` from the config reaches the app too, not only the workers,\n\t\t// so a suite's settings live in one place.\n\t\tenv: { ...process.env, ...project.config.env },\n\t\tstdio: ['ignore', 'inherit', 'inherit', 'ipc']\n\t});\n\tconst exited = new Promise<number | null>((resolve) => {\n\t\tchild.once('exit', (code) => resolve(code));\n\t});\n\n\tconst ready = await new Promise<Extract<HarnessMessage, { type: 'ready' }>>((resolve, reject) => {\n\t\tchild.on('message', (message: HarnessMessage) => {\n\t\t\tif (message.type === 'ready') resolve(message);\n\t\t\telse reject(new Error(message.message));\n\t\t});\n\t\tvoid exited.then((code) =>\n\t\t\treject(new Error(`vela test:harness exited with code ${code} before its servers were up`))\n\t\t);\n\t});\n\n\t// Workers start after this returns and inherit the environment, so the\n\t// setup file and the tests see the same variables `vela test:server` set.\n\tObject.assign(process.env, ready.env);\n\tproject.provide('vela', ready.servers);\n\n\treturn async () => {\n\t\tif (child.exitCode !== null) return;\n\t\tchild.send('stop');\n\t\tconst timer = setTimeout(() => child.kill(), 5_000);\n\t\tawait exited;\n\t\tclearTimeout(timer);\n\t};\n}\n"],
5
+ "mappings": ";AAoBA,SAAS,aAAa;AACtB,OAAO,UAAU;AACjB,OAAO,aAAa;AACpB,SAAS,qBAAqB;AAC9B,OAAO,YAAY;AAyBnB,eAAO,MAA6B,SAAoD;AACvF,QAAM,OAAO,QAAQ,OAAO;AAK5B,SAAO,OAAO,EAAE,MAAM,KAAK,KAAK,MAAM,MAAM,GAAG,OAAO,KAAK,CAAC;AAI5D,QAAM,MAAM,cAAc,IAAI,IAAI,YAAY,YAAY,GAAG,CAAC;AAC9D,QAAM,QAAQ,MAAM,QAAQ,UAAU,CAAC,KAAK,cAAc,GAAG;AAAA,IAC5D,KAAK;AAAA;AAAA;AAAA,IAGL,KAAK,EAAE,GAAG,QAAQ,KAAK,GAAG,QAAQ,OAAO,IAAI;AAAA,IAC7C,OAAO,CAAC,UAAU,WAAW,WAAW,KAAK;AAAA,EAC9C,CAAC;AACD,QAAM,SAAS,IAAI,QAAuB,CAAC,YAAY;AACtD,UAAM,KAAK,QAAQ,CAAC,SAAS,QAAQ,IAAI,CAAC;AAAA,EAC3C,CAAC;AAED,QAAM,QAAQ,MAAM,IAAI,QAAoD,CAAC,SAAS,WAAW;AAChG,UAAM,GAAG,WAAW,CAAC,YAA4B;AAChD,UAAI,QAAQ,SAAS,QAAS,SAAQ,OAAO;AAAA,UACxC,QAAO,IAAI,MAAM,QAAQ,OAAO,CAAC;AAAA,IACvC,CAAC;AACD,SAAK,OAAO;AAAA,MAAK,CAAC,SACjB,OAAO,IAAI,MAAM,sCAAsC,IAAI,6BAA6B,CAAC;AAAA,IAC1F;AAAA,EACD,CAAC;AAID,SAAO,OAAO,QAAQ,KAAK,MAAM,GAAG;AACpC,UAAQ,QAAQ,QAAQ,MAAM,OAAO;AAErC,SAAO,YAAY;AAClB,QAAI,MAAM,aAAa,KAAM;AAC7B,UAAM,KAAK,MAAM;AACjB,UAAM,QAAQ,WAAW,MAAM,MAAM,KAAK,GAAG,GAAK;AAClD,UAAM;AACN,iBAAa,KAAK;AAAA,EACnB;AACD;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vela",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "type": "module",
5
5
  "description": "A CLI for creating and updating SvelteKit projects",
6
6
  "license": "MIT",
@@ -16,12 +16,19 @@
16
16
  "vela": "./dist/bin.js",
17
17
  "velastack": "./dist/bin.js"
18
18
  },
19
+ "exports": {
20
+ "./testing": {
21
+ "types": "./dist/testing.d.ts",
22
+ "default": "./dist/testing.js"
23
+ },
24
+ "./package.json": "./package.json"
25
+ },
19
26
  "files": [
20
27
  "dist",
21
28
  "templates"
22
29
  ],
23
30
  "scripts": {
24
- "build": "node --experimental-strip-types scripts/build.ts",
31
+ "build": "node --experimental-strip-types scripts/build.ts && tsc -p tsconfig.testing.json",
25
32
  "dev": "node --experimental-strip-types scripts/build.ts --watch",
26
33
  "clean": "rm -rf dist",
27
34
  "lint": "npm run lint:ts && npm run lint:format && npm run lint:sh",
@@ -37,7 +44,7 @@
37
44
  "dependencies": {
38
45
  "@clack/prompts": "^1.7.0",
39
46
  "@faker-js/faker": "^10.6.0",
40
- "@velastack/patterns": "^0.10.0",
47
+ "@velastack/patterns": "^0.11.0",
41
48
  "@velastack/pocketbase-codegen": "^0.1.0",
42
49
  "@velastack/schema": "^0.3.0",
43
50
  "@velastack/schema-ui": "^0.3.1",
@@ -45,6 +52,7 @@
45
52
  "commander": "^15.0.0",
46
53
  "cross-spawn": "^7.0.6",
47
54
  "dedent": "^1.7.2",
55
+ "devalue": "^5.9.4",
48
56
  "dotenv": "^18.0.1",
49
57
  "fetch-cookie": "^3.2.0",
50
58
  "json-schema-faker": "^0.6.3",
@@ -53,7 +61,7 @@
53
61
  "picocolors": "^1.1.1",
54
62
  "pocketbase": "^0.28.0",
55
63
  "pocketbase-server": "^0.40.6-beta.5",
56
- "stripe": "^22.6.2",
64
+ "stripe": "^23.0.1",
57
65
  "sv": "~1.1.1",
58
66
  "svelte": "^5.56.10",
59
67
  "tar": "^7.5.22",
@@ -30,3 +30,6 @@ vite.config.ts.timestamp-*
30
30
 
31
31
  # Backups
32
32
  /backups
33
+
34
+ # Throwaway databases of server-test runs (vela/testing)
35
+ test-data/
@@ -16,6 +16,8 @@
16
16
  "check:watch": "svelte-kit sync && vela sync && svelte-check --tsconfig ./tsconfig.json --watch",
17
17
  "lint": "prettier --check .",
18
18
  "format": "prettier --write .",
19
+ "test": "vitest run",
20
+ "test:unit": "vitest run --project unit",
19
21
  "test:server": "vela test:server"
20
22
  },
21
23
  "devDependencies": {
@@ -1,10 +1,19 @@
1
- import { beforeEach, afterEach, beforeAll } from 'vitest';
1
+ import { beforeEach, afterEach, beforeAll, inject } from 'vitest';
2
2
  import supertest, { type Agent } from 'supertest';
3
3
  import PocketBase from 'pocketbase-sveltekit';
4
4
  import type { TestContext } from '@velastack/pocketbase/testing';
5
+ import type {} from 'vela/testing';
5
6
 
6
- const admin = new PocketBase(process.env.POCKETBASE_URL!) as App.Locals['admin'];
7
- const pb = new PocketBase(process.env.POCKETBASE_URL!) as App.Locals['pb'];
7
+ // The servers `vela/testing` started: a throwaway PocketBase and the app.
8
+ const servers = inject('vela');
9
+ if (!servers) {
10
+ throw new Error(
11
+ 'Server tests need the servers `vela/testing` starts. Add `globalSetup: [\'vela/testing\']` to the test config in vitest.config.ts.'
12
+ );
13
+ }
14
+
15
+ const admin = new PocketBase(servers.pocketbaseUrl) as App.Locals['admin'];
16
+ const pb = new PocketBase(servers.pocketbaseUrl) as App.Locals['pb'];
8
17
 
9
18
  const testUserPassword = 'password';
10
19
 
@@ -25,15 +34,12 @@ beforeAll(async () => {
25
34
  await admin
26
35
  // @ts-ignore
27
36
  .collection('_superusers')
28
- .authWithPassword(
29
- process.env.POCKETBASE_SUPERUSER_EMAIL!,
30
- process.env.POCKETBASE_SUPERUSER_PASSWORD!
31
- );
37
+ .authWithPassword(servers.superuser.email, servers.superuser.password);
32
38
  });
33
39
 
34
40
  beforeEach(async (context: TestContext) => {
35
- context.request = supertest(process.env.VITE_TEST_URL!);
36
- context.agent = supertest.agent(process.env.VITE_TEST_URL!) as TestContext['agent'];
41
+ context.request = supertest(servers.appUrl);
42
+ context.agent = supertest.agent(servers.appUrl) as TestContext['agent'];
37
43
  context.admin = admin;
38
44
  context.pb = pb;
39
45
  const email = `test-${Math.random().toString(36).slice(2)}@example.com`;
@@ -1,4 +1,4 @@
1
- import { defineConfig, mergeConfig } from 'vitest/config';
1
+ import { configDefaults, defineConfig, mergeConfig } from 'vitest/config';
2
2
  import viteConfig from './vite.config.ts';
3
3
 
4
4
  export default mergeConfig(
@@ -8,11 +8,48 @@ export default mergeConfig(
8
8
  expect: {
9
9
  requireAssertions: true
10
10
  },
11
- name: 'server',
12
11
  environment: 'node',
13
- include: ['src/**/*.{test,spec}.{js,ts}'],
14
- exclude: ['src/**/*.svelte.{test,spec}.{js,ts}'],
15
- setupFiles: ['test/setup.ts']
12
+ passWithNoTests: true,
13
+ // Server tests reach the app over HTTP and import nothing from it, so
14
+ // watch mode cannot see which tests an edit affects. Rerun them all.
15
+ forceRerunTriggers: [
16
+ '**/package.json/**',
17
+ '**/{vitest,vite}.config.*/**',
18
+ 'src/routes/**',
19
+ 'src/lib/server/**',
20
+ 'src/hooks.server.ts',
21
+ 'data/hooks/**'
22
+ ],
23
+ projects: [
24
+ {
25
+ extends: true,
26
+ test: {
27
+ // `server.test.ts` next to a route, `<name>.server.test.ts` elsewhere:
28
+ // tests that run against the app and its database.
29
+ name: 'server',
30
+ include: ['src/**/*server.{test,spec}.{js,ts}'],
31
+ // Starts a throwaway PocketBase and the app before the first test
32
+ // file, and stops them after the last. Every way of running vitest
33
+ // gets the same servers: `vela test:server`, `vitest`, `vitest --watch`,
34
+ // an editor's test runner.
35
+ globalSetup: ['vela/testing'],
36
+ setupFiles: ['test/setup.ts']
37
+ }
38
+ },
39
+ {
40
+ extends: true,
41
+ test: {
42
+ // Every other test: pure functions, no servers, no setup file.
43
+ name: 'unit',
44
+ include: ['src/**/*.{test,spec}.{js,ts}'],
45
+ exclude: [
46
+ ...configDefaults.exclude,
47
+ 'src/**/*server.{test,spec}.{js,ts}',
48
+ 'src/**/*.svelte.{test,spec}.{js,ts}'
49
+ ]
50
+ }
51
+ }
52
+ ]
16
53
  }
17
54
  })
18
55
  );