@abgov/nx-adsp 13.11.0 → 13.12.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/README.md CHANGED
@@ -265,7 +265,7 @@ have a tenant yet? That picker also offers a **+ Create a new tenant** choice
265
265
  `tenant-service-admin`, and (unless `tenant-service-admin`) that doesn't already own a tenant (one
266
266
  per admin email). Picking it prompts for a name and waits for the new realm to finish provisioning
267
267
  before continuing the login as that tenant. Requires `@abgov/adsp-cli` ^1.4.0+ (this plugin pins
268
- ^1.5.2 or later).
268
+ ^1.6.0 or later).
269
269
 
270
270
  ## Agent consultation
271
271
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abgov/nx-adsp",
3
- "version": "13.11.0",
3
+ "version": "13.12.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "src/index.js",
6
6
  "description": "Government of Alberta - Nx plugin for ADSP apps.",
@@ -11,6 +11,10 @@ const keycloak_admin_1 = require("../../utils/keycloak-admin");
11
11
  const plugin_version_1 = require("../../utils/plugin-version");
12
12
  const quality_1 = require("../../utils/quality");
13
13
  const init_1 = require("../init/init");
14
+ // Must match environment.ts.__tmpl__'s own `PORT: num({ default: ... })` —
15
+ // there's no --port option to source this from yet, so it's a second literal
16
+ // kept in sync by convention rather than a single shared value.
17
+ const DEFAULT_PORT = 3333;
14
18
  function normalizeOptions(host, options) {
15
19
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
16
20
  var _a;
@@ -39,7 +43,16 @@ function default_1(host, options) {
39
43
  throw new Error("The 'express-service' generator requires the '@nx/express' plugin. Install it and re-run:\n npm i -D @nx/express");
40
44
  });
41
45
  const normalizedOptions = yield normalizeOptions(host, options);
42
- yield initExpress(host, Object.assign(Object.assign({}, options), { skipFormat: true, skipPackageJson: false, linter: eslint_1.Linter.EsLint, unitTestRunner: 'jest', js: false, directory: normalizedOptions.projectRoot }));
46
+ // @nx/express's own Schema type doesn't declare e2eTestRunner, even though
47
+ // its implementation forwards it straight through to @nx/node's own
48
+ // applicationGenerator (which does). Assigning to an inferred-type const
49
+ // first, rather than passing the object literal directly, sidesteps
50
+ // TypeScript's excess-property check on a type that's simply incomplete.
51
+ const initExpressOptions = Object.assign(Object.assign({}, options), { skipFormat: true, skipPackageJson: false, linter: eslint_1.Linter.EsLint, unitTestRunner: 'jest', e2eTestRunner: 'jest', js: false, directory: normalizedOptions.projectRoot });
52
+ yield initExpress(host, initExpressOptions);
53
+ // @nx/node:e2e-project's own generated output has two bugs of its own —
54
+ // see fixExpressServiceE2eProject's doc comment for how each was confirmed.
55
+ (0, quality_1.fixExpressServiceE2eProject)(host, `${normalizedOptions.projectRoot}-e2e`, DEFAULT_PORT);
43
56
  (0, devkit_1.addDependenciesToPackageJson)(host, Object.assign(Object.assign({ '@abgov/adsp-service-sdk': '^2.23.0', '@asteasolutions/zod-to-openapi': '^7.3.4', compression: '^1.8.1', cors: '^2.8.5', dotenv: '^16.4.7', envalid: '^8.0.0', helmet: '^8.0.0', passport: '^0.7.0', 'passport-anonymous': '^1.0.1', zod: '^3.0.0' }, (normalizedOptions.database === 'postgres'
44
57
  ? { 'drizzle-orm': '^0.44.0', pg: '^8.11.0' }
45
58
  : {})), (normalizedOptions.database === 'mongo' ? { mongoose: '^8.0.0' } : {})), Object.assign(Object.assign({ '@types/compression': '^1.7.5', '@types/cors': '^2.8.17', '@types/passport': '^1.0.16', '@types/passport-anonymous': '^1.0.3', supertest: '^7.0.0', '@types/supertest': '^6.0.0' }, (normalizedOptions.database === 'postgres'
@@ -1 +1 @@
1
- {"version":3,"file":"express-service.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/express-service/express-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"express-service.js","sourceRoot":"","sources":["../../../../../../packages/nx-adsp/src/generators/express-service/express-service.ts"],"names":[],"mappings":"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"}
@@ -99,6 +99,31 @@ describe('Express Service Generator', () => {
99
99
  expect(agents).toContain('search_sdk_reference');
100
100
  }, 60000);
101
101
 
102
+ it('scaffolds a Jest e2e project with the port default fixed to match environment.ts', async () => {
103
+ const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
104
+ await generator(host, options);
105
+
106
+ const e2eRoot = 'apps/test-e2e';
107
+ expect(host.exists(`${e2eRoot}/jest.config.cts`)).toBeTruthy();
108
+
109
+ // @nx/node:e2e-project's own template hardcodes a fallback port of 3000;
110
+ // nx-adsp's own environment.ts.__tmpl__ defaults PORT to 3333. Which of
111
+ // @nx/node's two jest.config.cts shapes (ts-jest vs. @swc/jest — see
112
+ // fixExpressServiceE2eProject's own tests for that variance) this
113
+ // workspace produces isn't deterministic here, but the port fix applies
114
+ // to both identically.
115
+ for (const file of ['global-setup.ts', 'global-teardown.ts']) {
116
+ const content = host.read(`${e2eRoot}/src/support/${file}`).toString();
117
+ expect(content).not.toContain(': 3000');
118
+ expect(content).toContain(': 3333');
119
+ }
120
+ const testSetup = host
121
+ .read(`${e2eRoot}/src/support/test-setup.ts`)
122
+ .toString();
123
+ expect(testSetup).not.toContain("?? '3000'");
124
+ expect(testSetup).toContain("?? '3333'");
125
+ }, 60000);
126
+
102
127
  it('merges .mcp.json without clobbering other servers or a customized entry', async () => {
103
128
  const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
104
129
  writeJson(host, '.mcp.json', {
@@ -167,6 +192,13 @@ describe('Express Service Generator', () => {
167
192
  const database = host.read('apps/test/src/database.ts').toString();
168
193
  expect(database).toContain('drizzle-orm/node-postgres');
169
194
  expect(database).toContain('closeDatabase');
195
+ expect(database).toContain('isDatabaseReady');
196
+
197
+ // Readiness (DB-checked) is wired up separately from liveness — a DB
198
+ // outage should hold traffic, not restart a pod that can't fix it.
199
+ const mainTs = host.read('apps/test/src/main.ts').toString();
200
+ expect(mainTs).toContain('isDatabaseReady');
201
+ expect(mainTs).toContain("app.get('/health/ready'");
170
202
 
171
203
  // webpack emits a second bundle (migrate.js) for the deploy init container.
172
204
  const webpackConfig = host.read('apps/test/webpack.config.js').toString();
@@ -208,6 +240,11 @@ describe('Express Service Generator', () => {
208
240
 
209
241
  const database = host.read('apps/test/src/database.ts').toString();
210
242
  expect(database).toContain('mongoose');
243
+ expect(database).toContain('isDatabaseReady');
244
+
245
+ const mainTs = host.read('apps/test/src/main.ts').toString();
246
+ expect(mainTs).toContain('isDatabaseReady');
247
+ expect(mainTs).toContain("app.get('/health/ready'");
211
248
 
212
249
  const config = readProjectConfiguration(host, 'test');
213
250
  expect(config.targets['dev-db']).toBeTruthy();
@@ -224,6 +261,12 @@ describe('Express Service Generator', () => {
224
261
  expect(host.exists('apps/test/src/database.ts')).toBeFalsy();
225
262
  expect(host.exists('apps/test/scripts/dev-db.sh')).toBeFalsy();
226
263
 
264
+ // No database to check readiness against — don't emit a route that
265
+ // would always report ready regardless of anything real.
266
+ const mainTs = host.read('apps/test/src/main.ts').toString();
267
+ expect(mainTs).not.toContain('/health/ready');
268
+ expect(mainTs).not.toContain('isDatabaseReady');
269
+
227
270
  const config = readProjectConfiguration(host, 'test');
228
271
  expect(config.targets['dev-db']).toBeFalsy();
229
272
  expect(
@@ -68,6 +68,17 @@ need project-scoped MCP enabled. The server runs via `npx` and needs no credenti
68
68
  | `src/database.ts` | Mongoose connection helpers — `connectDatabase()` and `disconnectDatabase()` |
69
69
  <% } %>
70
70
 
71
+ ## Health and readiness checks
72
+
73
+ `/health` and (<% if (database !== 'none') { %>already, since this service has a database<% } else { %>if this service later gains a database or another required dependency<% } %>) `/health/ready` are two different questions, mapped to the two different OpenShift probes for a reason — conflating them into one endpoint means an outage in a downstream dependency gets treated as "restart this pod," which does not fix the outage and just adds a restart loop on top of it:
74
+
75
+ - **`/health` — liveness: is the process itself up.** Stays dependency-free on purpose. Don't add a check here for anything this service depends on but doesn't own.
76
+ <% if (database !== 'none') { %>
77
+ - **`/health/ready` — readiness: can this pod actually serve requests right now.** Already checks the database (`isDatabaseReady()` in `src/database.ts`) — a real round-trip query, not just a pool-state flag. **Extend this handler, not `/health`, for any other resource this service can't function without** — another required downstream API, a message broker, a required ADSP capability beyond what `healthCheck()` already covers — following the same reasoning: a dependency being down should hold traffic (503, readiness), not trigger a pod restart (liveness) that can't fix a problem outside this pod.
78
+ <% } else { %>
79
+ - If this service later gains a database or another hard dependency, add a `/health/ready` route that checks it (see `@abgov/nx-adsp:express-service --database postgres|mongo` for the shipped pattern) and point the deployment's `readinessProbe` at it instead of `/health` — don't check dependencies from the liveness route.
80
+ <% } %>
81
+
71
82
  ## SDK capabilities
72
83
 
73
84
  `initializeService()` returns `capabilities`:
@@ -7,9 +7,9 @@ import helmet from 'helmet';
7
7
  import passport from 'passport';
8
8
  import { Strategy as AnonymousStrategy } from 'passport-anonymous';
9
9
  <% if (database === 'postgres') { %>
10
- import { closeDatabase } from './database';
10
+ import { closeDatabase, isDatabaseReady } from './database';
11
11
  <% } else if (database === 'mongo') { %>
12
- import { connectDatabase, disconnectDatabase } from './database';
12
+ import { connectDatabase, disconnectDatabase, isDatabaseReady } from './database';
13
13
  <% } %>
14
14
  import { environment } from './environment';
15
15
  import { exampleEventDefinition } from './events';
@@ -54,10 +54,34 @@ async function initializeApp() {
54
54
  passport.use('tenant', tenantStrategy);
55
55
  app.use(passport.initialize());
56
56
 
57
+ // Liveness — is the process itself up. Deliberately stays dependency-free
58
+ // (platform reachability, not this app's own resources) so an outage in a
59
+ // downstream dependency doesn't get treated as "restart this pod," which
60
+ // wouldn't fix the outage and would just cause a needless restart loop.
57
61
  app.get('/health', async (_req, res) => {
58
62
  const platform = await healthCheck();
59
63
  res.json({ ...platform });
60
64
  });
65
+ <% if (database !== 'none') { %>
66
+ // Readiness — can this pod actually serve requests right now. Checks the
67
+ // database because this service can't do anything useful without it; 503
68
+ // tells the platform to hold traffic rather than route it somewhere that
69
+ // will 500. Extend this handler (not /health above) for any other resource
70
+ // this service can't function without — another required downstream API,
71
+ // a message broker, etc. — following the same "hold traffic, don't
72
+ // restart" reasoning; see AGENTS.md's "Health and readiness checks".
73
+ app.get('/health/ready', async (_req, res) => {
74
+ try {
75
+ await isDatabaseReady();
76
+ res.json({ ready: true });
77
+ } catch (err) {
78
+ res.status(503).json({
79
+ ready: false,
80
+ error: err instanceof Error ? err.message : String(err),
81
+ });
82
+ }
83
+ });
84
+ <% } %>
61
85
 
62
86
  // Generated once from the routers' registry.registerPath() calls (see
63
87
  // routes/example.ts) — every router imported above has already registered
@@ -85,6 +109,9 @@ async function initializeApp() {
85
109
  _links: {
86
110
  self: { href: new URL(req.originalUrl, rootUrl).href },
87
111
  health: { href: new URL('/health', rootUrl).href },
112
+ <% if (database !== 'none') { %>
113
+ ready: { href: new URL('/health/ready', rootUrl).href },
114
+ <% } %>
88
115
  api: { href: new URL('/<%= projectName %>/v1', rootUrl).href },
89
116
  docs: { href: new URL('/swagger/docs/v1', rootUrl).href },
90
117
  },
@@ -9,3 +9,13 @@ export async function connectDatabase(): Promise<void> {
9
9
  export async function disconnectDatabase(): Promise<void> {
10
10
  await mongoose.disconnect();
11
11
  }
12
+
13
+ // A real ping, not just `readyState === 1` — the connection can report
14
+ // "connected" while actually stale. Used by /health/ready, not by `main.ts`
15
+ // directly.
16
+ export async function isDatabaseReady(): Promise<void> {
17
+ if (!mongoose.connection.db) {
18
+ throw new Error('Database connection not established.');
19
+ }
20
+ await mongoose.connection.db.admin().ping();
21
+ }
@@ -1,3 +1,4 @@
1
+ import { sql } from 'drizzle-orm';
1
2
  import { drizzle } from 'drizzle-orm/node-postgres';
2
3
  import { Pool } from 'pg';
3
4
 
@@ -12,3 +13,10 @@ const pool = new Pool({ connectionString: environment.DATABASE_URL });
12
13
  export const db = drizzle(pool, { schema });
13
14
 
14
15
  export const closeDatabase = (): Promise<void> => pool.end();
16
+
17
+ // A trivial round-trip query, not just a pool-state check — proves the
18
+ // database is actually reachable and answering, the same reason `pg_isready`
19
+ // exists. Used by /health/ready, not by `main.ts` directly.
20
+ export const isDatabaseReady = async (): Promise<void> => {
21
+ await db.execute(sql`SELECT 1`);
22
+ };
@@ -49,6 +49,60 @@ export declare function addSemgrepTarget(host: Tree, projectName: string): void;
49
49
  * Idempotent and a no-op if the config lacks a webServer or is already guarded.
50
50
  */
51
51
  export declare function guardPlaywrightWebServer(host: Tree, e2eProjectRoot: string): void;
52
+ /**
53
+ * Fixes two bugs that surface when e2e testing is added to express-service,
54
+ * confirmed by actually running the generated `e2e` target rather than just
55
+ * inspecting the templates. The two have different owners — kept together
56
+ * here only because both need fixing at the same call site, not because
57
+ * they share a cause:
58
+ *
59
+ * 1. UPSTREAM BUG, not nx-adsp's — lives entirely inside `@nx/node`'s own
60
+ * `e2e-project` generator (triggered here via `e2eTestRunner: 'jest'` on
61
+ * the express application generator). Nothing about nx-adsp's own code
62
+ * causes it, and it would reproduce in a plain `@nx/node`/`@nx/express`
63
+ * workspace with no nx-adsp involved at all. Kept as a local workaround
64
+ * anyway since nx-adsp doesn't control Nx's release cadence and a safe
65
+ * no-op against already-correct output (see below) costs nothing if
66
+ * upstream fixes it later — worth reporting to `nrwl/nx` regardless.
67
+ *
68
+ * `@nx/node:e2e-project` writes one of two shapes for `jest.config.cts`
69
+ * depending on whether `@swc/jest` is present in the workspace — not
70
+ * necessarily for this project itself; a Vue/React app elsewhere in a
71
+ * multi-app workspace pulling in `@swc/core` for its own build is enough
72
+ * to flip it. The `@swc/jest` shape is `export default {...}` with a
73
+ * top-level `import` — ESM syntax in a `.cts` file, which Node/ts-jest/SWC
74
+ * always treat as CommonJS regardless of the workspace's own
75
+ * `package.json` "type". Jest's own config loader (unlike its `transform`
76
+ * pipeline, which only applies to *test* files) can't reconcile the two,
77
+ * so the config fails to parse before a single test runs — every target
78
+ * that reads it breaks outright with `SyntaxError: Cannot use import
79
+ * statement outside a module`. Rewritten to `const {...} = require(...)`
80
+ * / `module.exports = {...}`, matching both the pattern the service's own
81
+ * `jest.config.cts` already uses correctly and the ts-jest shape
82
+ * `@nx/node` writes when `@swc/jest` isn't present — which needs no fix,
83
+ * so the two replaces below are safe no-ops against it.
84
+ *
85
+ * (`src/support/*.ts` also import at the top level, but — verified by
86
+ * running the target — those are plain `.ts` files that DO go through
87
+ * the config's `transform` pipeline, so they don't need this fix; only
88
+ * the config file itself, read before that pipeline exists, does.)
89
+ *
90
+ * 2. OUR OWN BUG, not Nx's — a convention mismatch entirely on nx-adsp's
91
+ * side, not a defect in the upstream generator. `@nx/node:e2e-project`'s
92
+ * fallback port of `3000` is a reasonable generic default with no
93
+ * knowledge of any particular caller; it's only wrong here because
94
+ * nx-adsp's own `environment.ts.__tmpl__` defaults `PORT` to `3333`
95
+ * instead (matching `@nx/express`'s own scaffolded `main.ts` before
96
+ * nx-adsp replaces it), and until this fix nothing reconciled the two.
97
+ * There's no upstream fix to wait for — reconciling nx-adsp's own port
98
+ * convention with whatever generates the e2e project is squarely
99
+ * nx-adsp's job. Confirmed via a real run:
100
+ * the express app logs `Listening at http://localhost:3333`, while
101
+ * `global-setup.ts` waits on `3000` and never sees it open — the target
102
+ * hangs, then fails with `Jest: Got error running globalSetup ...
103
+ * reason: [AggregateError]`, no port number anywhere in the message.
104
+ */
105
+ export declare function fixExpressServiceE2eProject(host: Tree, e2eProjectRoot: string, port: number): void;
52
106
  export declare function addVsCodeSettings(host: Tree): void;
53
107
  /**
54
108
  * Registers the @abgov/adsp-sdk-mcp-server MCP server in the workspace-root
@@ -4,6 +4,7 @@ exports.addEslintQualityRules = addEslintQualityRules;
4
4
  exports.addJestCoverageConfig = addJestCoverageConfig;
5
5
  exports.addSemgrepTarget = addSemgrepTarget;
6
6
  exports.guardPlaywrightWebServer = guardPlaywrightWebServer;
7
+ exports.fixExpressServiceE2eProject = fixExpressServiceE2eProject;
7
8
  exports.addVsCodeSettings = addVsCodeSettings;
8
9
  exports.addAdspMcpServer = addAdspMcpServer;
9
10
  const devkit_1 = require("@nx/devkit");
@@ -126,6 +127,86 @@ function guardPlaywrightWebServer(host, e2eProjectRoot) {
126
127
  return;
127
128
  }
128
129
  }
130
+ /**
131
+ * Fixes two bugs that surface when e2e testing is added to express-service,
132
+ * confirmed by actually running the generated `e2e` target rather than just
133
+ * inspecting the templates. The two have different owners — kept together
134
+ * here only because both need fixing at the same call site, not because
135
+ * they share a cause:
136
+ *
137
+ * 1. UPSTREAM BUG, not nx-adsp's — lives entirely inside `@nx/node`'s own
138
+ * `e2e-project` generator (triggered here via `e2eTestRunner: 'jest'` on
139
+ * the express application generator). Nothing about nx-adsp's own code
140
+ * causes it, and it would reproduce in a plain `@nx/node`/`@nx/express`
141
+ * workspace with no nx-adsp involved at all. Kept as a local workaround
142
+ * anyway since nx-adsp doesn't control Nx's release cadence and a safe
143
+ * no-op against already-correct output (see below) costs nothing if
144
+ * upstream fixes it later — worth reporting to `nrwl/nx` regardless.
145
+ *
146
+ * `@nx/node:e2e-project` writes one of two shapes for `jest.config.cts`
147
+ * depending on whether `@swc/jest` is present in the workspace — not
148
+ * necessarily for this project itself; a Vue/React app elsewhere in a
149
+ * multi-app workspace pulling in `@swc/core` for its own build is enough
150
+ * to flip it. The `@swc/jest` shape is `export default {...}` with a
151
+ * top-level `import` — ESM syntax in a `.cts` file, which Node/ts-jest/SWC
152
+ * always treat as CommonJS regardless of the workspace's own
153
+ * `package.json` "type". Jest's own config loader (unlike its `transform`
154
+ * pipeline, which only applies to *test* files) can't reconcile the two,
155
+ * so the config fails to parse before a single test runs — every target
156
+ * that reads it breaks outright with `SyntaxError: Cannot use import
157
+ * statement outside a module`. Rewritten to `const {...} = require(...)`
158
+ * / `module.exports = {...}`, matching both the pattern the service's own
159
+ * `jest.config.cts` already uses correctly and the ts-jest shape
160
+ * `@nx/node` writes when `@swc/jest` isn't present — which needs no fix,
161
+ * so the two replaces below are safe no-ops against it.
162
+ *
163
+ * (`src/support/*.ts` also import at the top level, but — verified by
164
+ * running the target — those are plain `.ts` files that DO go through
165
+ * the config's `transform` pipeline, so they don't need this fix; only
166
+ * the config file itself, read before that pipeline exists, does.)
167
+ *
168
+ * 2. OUR OWN BUG, not Nx's — a convention mismatch entirely on nx-adsp's
169
+ * side, not a defect in the upstream generator. `@nx/node:e2e-project`'s
170
+ * fallback port of `3000` is a reasonable generic default with no
171
+ * knowledge of any particular caller; it's only wrong here because
172
+ * nx-adsp's own `environment.ts.__tmpl__` defaults `PORT` to `3333`
173
+ * instead (matching `@nx/express`'s own scaffolded `main.ts` before
174
+ * nx-adsp replaces it), and until this fix nothing reconciled the two.
175
+ * There's no upstream fix to wait for — reconciling nx-adsp's own port
176
+ * convention with whatever generates the e2e project is squarely
177
+ * nx-adsp's job. Confirmed via a real run:
178
+ * the express app logs `Listening at http://localhost:3333`, while
179
+ * `global-setup.ts` waits on `3000` and never sees it open — the target
180
+ * hangs, then fails with `Jest: Got error running globalSetup ...
181
+ * reason: [AggregateError]`, no port number anywhere in the message.
182
+ */
183
+ function fixExpressServiceE2eProject(host, e2eProjectRoot, port) {
184
+ const jestConfigPath = `${e2eProjectRoot}/jest.config.cts`;
185
+ if (host.exists(jestConfigPath)) {
186
+ const cfg = host
187
+ .read(jestConfigPath)
188
+ .toString()
189
+ .replace(/^import \{([^}]+)\} from '([^']+)';$/m, "const {$1} = require('$2');")
190
+ .replace(/^export default \{/m, 'module.exports = {');
191
+ host.write(jestConfigPath, cfg);
192
+ }
193
+ for (const file of ['global-setup.ts', 'global-teardown.ts']) {
194
+ const path = `${e2eProjectRoot}/src/support/${file}`;
195
+ if (!host.exists(path))
196
+ continue;
197
+ host.write(path, host
198
+ .read(path)
199
+ .toString()
200
+ .replace(/process\.env\.PORT \? Number\(process\.env\.PORT\) : 3000/, `process.env.PORT ? Number(process.env.PORT) : ${port}`));
201
+ }
202
+ const testSetupPath = `${e2eProjectRoot}/src/support/test-setup.ts`;
203
+ if (host.exists(testSetupPath)) {
204
+ host.write(testSetupPath, host
205
+ .read(testSetupPath)
206
+ .toString()
207
+ .replace(/process\.env\.PORT \?\? '3000'/, `process.env.PORT ?? '${port}'`));
208
+ }
209
+ }
129
210
  function addVsCodeSettings(host) {
130
211
  const settingsPath = '.vscode/settings.json';
131
212
  const settings = {
@@ -1 +1 @@
1
- {"version":3,"file":"quality.js","sourceRoot":"","sources":["../../../../../packages/nx-adsp/src/utils/quality.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"quality.js","sourceRoot":"","sources":["../../../../../packages/nx-adsp/src/utils/quality.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { Tree } from '@nx/devkit';
2
2
  import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
3
- import { addJestCoverageConfig } from './quality';
3
+ import { addJestCoverageConfig, fixExpressServiceE2eProject } from './quality';
4
4
 
5
5
  function makeConfig(coverageDirectoryLine: string): string {
6
6
  return [
@@ -69,3 +69,153 @@ describe('addJestCoverageConfig', () => {
69
69
  expect(tree.exists('apps/none/jest.config.cts')).toBe(false);
70
70
  });
71
71
  });
72
+
73
+ describe('fixExpressServiceE2eProject', () => {
74
+ let tree: Tree;
75
+ beforeEach(() => {
76
+ tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
77
+ });
78
+
79
+ // Byte-for-byte what @nx/node:e2e-project actually writes when the
80
+ // workspace has @swc/jest available — confirmed via a real `nx e2e` run,
81
+ // which failed to parse with exactly this shape.
82
+ const SWC_JEST_CONFIG = `/* eslint-disable */
83
+ import { readFileSync } from 'fs';
84
+
85
+ // Reading the SWC compilation config for the spec files
86
+ const swcJestConfig = JSON.parse(
87
+ readFileSync(\`\${__dirname}/.spec.swcrc\`, 'utf-8'),
88
+ );
89
+
90
+ // Disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves
91
+ swcJestConfig.swcrc = false;
92
+
93
+ export default {
94
+ displayName: '@org/test-service-e2e',
95
+ preset: '../jest.preset.js',
96
+ globalSetup: '<rootDir>/src/support/global-setup.ts',
97
+ globalTeardown: '<rootDir>/src/support/global-teardown.ts',
98
+ setupFiles: ['<rootDir>/src/support/test-setup.ts'],
99
+ testEnvironment: 'node',
100
+ transform: {
101
+ '^.+\\\\.[tj]s$': ['@swc/jest', swcJestConfig],
102
+ },
103
+ moduleFileExtensions: ['ts', 'js', 'html'],
104
+ coverageDirectory: 'test-output/jest/coverage',
105
+ };
106
+ `;
107
+
108
+ // What @nx/node:e2e-project writes when the workspace uses ts-jest instead
109
+ // (this repo's own case) — already valid CommonJS, nothing to fix.
110
+ const TS_JEST_CONFIG = `module.exports = {
111
+ displayName: 'test-e2e',
112
+ preset: '../../jest.preset.js',
113
+ globalSetup: '<rootDir>/src/support/global-setup.ts',
114
+ globalTeardown: '<rootDir>/src/support/global-teardown.ts',
115
+ setupFiles: ['<rootDir>/src/support/test-setup.ts'],
116
+ testEnvironment: 'node',
117
+ transform: {
118
+ '^.+\\\\.[tj]s$': ['ts-jest', {
119
+ tsconfig: '<rootDir>/tsconfig.spec.json',
120
+ }],
121
+ },
122
+ moduleFileExtensions: ['ts', 'js', 'html'],
123
+ coverageDirectory: '../../coverage/test-e2e',
124
+ };
125
+ `;
126
+
127
+ const GLOBAL_SETUP = `import { waitForPortOpen } from '@nx/node/utils';
128
+
129
+ /* eslint-disable */
130
+ var __TEARDOWN_MESSAGE__: string;
131
+
132
+ module.exports = async function () {
133
+ console.log('\\nSetting up...\\n');
134
+
135
+ const host = process.env.HOST ?? 'localhost';
136
+ const port = process.env.PORT ? Number(process.env.PORT) : 3000;
137
+ await waitForPortOpen(port, { host });
138
+
139
+ globalThis.__TEARDOWN_MESSAGE__ = '\\nTearing down...\\n';
140
+ };
141
+ `;
142
+
143
+ const GLOBAL_TEARDOWN = `import { killPort } from '@nx/node/utils';
144
+ /* eslint-disable */
145
+
146
+ module.exports = async function () {
147
+ const port = process.env.PORT ? Number(process.env.PORT) : 3000;
148
+ await killPort(port);
149
+ console.log(globalThis.__TEARDOWN_MESSAGE__);
150
+ };
151
+ `;
152
+
153
+ const TEST_SETUP = `/* eslint-disable */
154
+ import axios from 'axios';
155
+
156
+ module.exports = async function () {
157
+ const host = process.env.HOST ?? 'localhost';
158
+ const port = process.env.PORT ?? '3000';
159
+ axios.defaults.baseURL = \`http://\${host}:\${port}\`;
160
+ };
161
+ `;
162
+
163
+ function writeE2eProject(root: string, jestConfig: string): void {
164
+ tree.write(`${root}/jest.config.cts`, jestConfig);
165
+ tree.write(`${root}/src/support/global-setup.ts`, GLOBAL_SETUP);
166
+ tree.write(`${root}/src/support/global-teardown.ts`, GLOBAL_TEARDOWN);
167
+ tree.write(`${root}/src/support/test-setup.ts`, TEST_SETUP);
168
+ }
169
+
170
+ it('rewrites the @swc/jest variant of jest.config.cts to CommonJS', () => {
171
+ writeE2eProject('apps/svc-e2e', SWC_JEST_CONFIG);
172
+
173
+ fixExpressServiceE2eProject(tree, 'apps/svc-e2e', 3333);
174
+
175
+ const out = tree.read('apps/svc-e2e/jest.config.cts').toString();
176
+ expect(out).not.toContain('export default');
177
+ expect(out).not.toMatch(/^import /m);
178
+ expect(out).toContain('module.exports = {');
179
+ expect(out).toContain("const { readFileSync } = require('fs');");
180
+ });
181
+
182
+ it('leaves the ts-jest variant of jest.config.cts untouched (already valid CommonJS)', () => {
183
+ writeE2eProject('apps/svc-e2e', TS_JEST_CONFIG);
184
+
185
+ fixExpressServiceE2eProject(tree, 'apps/svc-e2e', 3333);
186
+
187
+ expect(tree.read('apps/svc-e2e/jest.config.cts').toString()).toBe(
188
+ TS_JEST_CONFIG,
189
+ );
190
+ });
191
+
192
+ it('fixes the hardcoded port default in all three support files', () => {
193
+ writeE2eProject('apps/svc-e2e', TS_JEST_CONFIG);
194
+
195
+ fixExpressServiceE2eProject(tree, 'apps/svc-e2e', 3333);
196
+
197
+ const setup = tree
198
+ .read('apps/svc-e2e/src/support/global-setup.ts')
199
+ .toString();
200
+ expect(setup).toContain(
201
+ 'process.env.PORT ? Number(process.env.PORT) : 3333',
202
+ );
203
+ const teardown = tree
204
+ .read('apps/svc-e2e/src/support/global-teardown.ts')
205
+ .toString();
206
+ expect(teardown).toContain(
207
+ 'process.env.PORT ? Number(process.env.PORT) : 3333',
208
+ );
209
+ const testSetup = tree
210
+ .read('apps/svc-e2e/src/support/test-setup.ts')
211
+ .toString();
212
+ expect(testSetup).toContain("process.env.PORT ?? '3333'");
213
+ });
214
+
215
+ it('is a no-op when there is no e2e project at that root', () => {
216
+ expect(() =>
217
+ fixExpressServiceE2eProject(tree, 'apps/none-e2e', 3333),
218
+ ).not.toThrow();
219
+ expect(tree.exists('apps/none-e2e/jest.config.cts')).toBe(false);
220
+ });
221
+ });