Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
# classifier walk.
- run: npm ci
- name: Test CI scope classifier
run: node --test scripts/ci-scope.spec.mjs scripts/cockpit-matrix.spec.mjs
run: node --test scripts/ci-scope.spec.mjs scripts/cockpit-matrix.spec.mjs scripts/cockpit-ports.spec.mjs
- name: Detect changed CI surfaces
id: scope
run: |
Expand Down
47 changes: 37 additions & 10 deletions apps/cockpit/cockpit-e2e-wiring.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs';
import { dirname, join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { capabilities } from './scripts/capability-registry';
// @ts-expect-error — .mjs ES module without .d.ts; the e2e tsconfig uses
// allowJs:true but this top-level test file doesn't go through that config.
import { portsFor } from '../../cockpit/ports.mjs';

interface E2eWiring {
angularPort: number;
Expand Down Expand Up @@ -84,12 +87,23 @@ function activeCockpitE2eWiring(): E2eWiring[] {
const projectRoot = dirname(projectJsonPath);
const globalSetupPath = join(projectRoot, 'e2e/global-setup-impl.ts');
const globalSetup = readFileSync(globalSetupPath, 'utf8');
const proxyPath = join(projectRoot, 'proxy.conf.json');
const proxy = JSON.parse(readFileSync(proxyPath, 'utf8')) as Record<string, { target?: string }>;
const proxyPort = Number(proxy['/api']?.target?.match(/:(\d+)$/)?.[1]);
const langgraphCwd = parseStringProperty(globalSetup, 'langgraphCwd');
const langgraphPort = parseNumberProperty(globalSetup, 'langgraphPort') ?? proxyPort;
const angularPort = parseNumberProperty(globalSetup, 'angularPort');

// Post-port-registry migration: ports are imported from
// cockpit/ports.mjs rather than living as literals in
// global-setup-impl.ts. Look them up by project name.
let langgraphPort: number | undefined;
let angularPort: number | undefined;
try {
const ports = portsFor(project.name) as { angular: number; langgraph: number };
langgraphPort = ports.langgraph;
angularPort = ports.angular;
} catch {
// Cap not in registry (e.g. cockpit-ag-ui-streaming-angular).
// Fall back to parsing literals if global-setup still has them.
langgraphPort = parseNumberProperty(globalSetup, 'langgraphPort');
angularPort = parseNumberProperty(globalSetup, 'angularPort');
}

if (!project.name || !langgraphCwd || !langgraphPort || !angularPort) {
throw new Error(`Unable to parse e2e wiring for ${relative(repoRoot, projectJsonPath)}`);
Expand Down Expand Up @@ -153,16 +167,29 @@ describe('cockpit e2e wiring', () => {
errors.push(`${wiring.project}: registry pythonDir ${capability.pythonDir} != global setup langgraphCwd ${wiring.langgraphCwd}`);
}

const proxyPath = join(wiring.projectRoot, 'proxy.conf.json');
if (!existsSync(proxyPath)) {
errors.push(`${wiring.project}: missing proxy.conf.json`);
} else {
const proxy = JSON.parse(readFileSync(proxyPath, 'utf8')) as Record<string, { target?: string }>;
// Post-port-registry: proxy.conf.mjs templates the target from
// cockpit/ports.mjs at runtime. Drift is impossible because the
// value comes from the same registry the test already checks via
// wiring.langgraphPort. So we just assert the .mjs file exists +
// imports portsFor with this cap's name.
const proxyMjs = join(wiring.projectRoot, 'proxy.conf.mjs');
const proxyJson = join(wiring.projectRoot, 'proxy.conf.json');
if (existsSync(proxyMjs)) {
const text = readFileSync(proxyMjs, 'utf8');
if (!text.includes(`portsFor('${wiring.project}')`)) {
errors.push(`${wiring.project}: proxy.conf.mjs does not call portsFor('${wiring.project}')`);
}
} else if (existsSync(proxyJson)) {
// Legacy (allowed for ag-ui exception only); not expected for any
// cap reaching this code path.
const proxy = JSON.parse(readFileSync(proxyJson, 'utf8')) as Record<string, { target?: string }>;
const target = proxy['/api']?.target;
const expectedTarget = `http://localhost:${wiring.langgraphPort}`;
if (target !== expectedTarget) {
errors.push(`${wiring.project}: proxy target ${target} != ${expectedTarget}`);
}
} else {
errors.push(`${wiring.project}: missing proxy.conf (no .mjs or .json)`);
}

const scriptsDir = join(wiring.projectRoot, 'e2e/scripts');
Expand Down
7 changes: 5 additions & 2 deletions cockpit/chat/a2ui/angular/e2e/global-setup-impl.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// SPDX-License-Identifier: MIT
import { resolve } from 'node:path';
import { portsFor } from '../../../../../cockpit/ports.mjs';
import { createGlobalSetup } from '@threadplane-internal/e2e-harness';

const ports = portsFor('cockpit-chat-a2ui-angular');

export default createGlobalSetup({
langgraphCwd: 'cockpit/chat/a2ui/python',
langgraphPort: 5511,
langgraphPort: ports.langgraph,
angularProject: 'cockpit-chat-a2ui-angular',
angularPort: 4511,
angularPort: ports.angular,
fixturesDir: resolve(__dirname, 'fixtures'),
});
6 changes: 5 additions & 1 deletion cockpit/chat/a2ui/angular/e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// SPDX-License-Identifier: MIT
import { defineConfig, devices } from '@playwright/test';
import { portsFor } from '../../../../../cockpit/ports.mjs';

const { angular: angularPort } = portsFor('cockpit-chat-a2ui-angular');


export default defineConfig({
testDir: '.',
Expand All @@ -9,7 +13,7 @@ export default defineConfig({
retries: process.env.CI ? 2 : 0,
reporter: process.env.CI ? [['list'], ['html', { open: 'never' }]] : 'list',
use: {
baseURL: 'http://localhost:4511',
baseURL: `http://localhost:${angularPort}`,
trace: 'retain-on-failure',
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
Expand Down
3 changes: 2 additions & 1 deletion cockpit/chat/a2ui/angular/e2e/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"@threadplane-internal/e2e-harness/global-teardown": [
"libs/e2e-harness/src/global-teardown.ts"
]
}
},
"allowJs": true
},
"include": [
"**/*.ts"
Expand Down
2 changes: 1 addition & 1 deletion cockpit/chat/a2ui/angular/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
},
"defaultConfiguration": "development",
"options": {
"proxyConfig": "cockpit/chat/a2ui/angular/proxy.conf.json"
"proxyConfig": "cockpit/chat/a2ui/angular/proxy.conf.mjs"
}
},
"smoke": {
Expand Down
9 changes: 0 additions & 9 deletions cockpit/chat/a2ui/angular/proxy.conf.json

This file was deleted.

14 changes: 14 additions & 0 deletions cockpit/chat/a2ui/angular/proxy.conf.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
import { portsFor } from '../../../../cockpit/ports.mjs';

const { langgraph } = portsFor('cockpit-chat-a2ui-angular');

export default {
"/api": {
target: `http://localhost:${langgraph}`,
secure: false,
changeOrigin: true,
pathRewrite: {"^/api":""},
ws: true,
},
};
2 changes: 1 addition & 1 deletion cockpit/chat/debug/angular/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
},
"defaultConfiguration": "development",
"options": {
"proxyConfig": "cockpit/chat/debug/angular/proxy.conf.json"
"proxyConfig": "cockpit/chat/debug/angular/proxy.conf.mjs"
}
},
"smoke": {
Expand Down
9 changes: 0 additions & 9 deletions cockpit/chat/debug/angular/proxy.conf.json

This file was deleted.

14 changes: 14 additions & 0 deletions cockpit/chat/debug/angular/proxy.conf.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
import { portsFor } from '../../../../cockpit/ports.mjs';

const { langgraph } = portsFor('cockpit-chat-debug-angular');

export default {
"/api": {
target: `http://localhost:${langgraph}`,
secure: false,
changeOrigin: true,
pathRewrite: {"^/api":""},
ws: true,
},
};
7 changes: 5 additions & 2 deletions cockpit/chat/generative-ui/angular/e2e/global-setup-impl.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// SPDX-License-Identifier: MIT
import { resolve } from 'node:path';
import { portsFor } from '../../../../../cockpit/ports.mjs';
import { createGlobalSetup } from '@threadplane-internal/e2e-harness';

const ports = portsFor('cockpit-chat-generative-ui-angular');

export default createGlobalSetup({
langgraphCwd: 'cockpit/chat/generative-ui/python',
langgraphPort: 5508,
langgraphPort: ports.langgraph,
angularProject: 'cockpit-chat-generative-ui-angular',
angularPort: 4508,
angularPort: ports.angular,
fixturesDir: resolve(__dirname, 'fixtures'),
});
6 changes: 5 additions & 1 deletion cockpit/chat/generative-ui/angular/e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// SPDX-License-Identifier: MIT
import { defineConfig, devices } from '@playwright/test';
import { portsFor } from '../../../../../cockpit/ports.mjs';

const { angular: angularPort } = portsFor('cockpit-chat-generative-ui-angular');


export default defineConfig({
testDir: '.',
Expand All @@ -9,7 +13,7 @@ export default defineConfig({
retries: process.env.CI ? 2 : 0,
reporter: process.env.CI ? [['list'], ['html', { open: 'never' }]] : 'list',
use: {
baseURL: 'http://localhost:4508',
baseURL: `http://localhost:${angularPort}`,
trace: 'retain-on-failure',
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
Expand Down
3 changes: 2 additions & 1 deletion cockpit/chat/generative-ui/angular/e2e/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"@threadplane-internal/e2e-harness/global-teardown": [
"libs/e2e-harness/src/global-teardown.ts"
]
}
},
"allowJs": true
},
"include": [
"**/*.ts"
Expand Down
2 changes: 1 addition & 1 deletion cockpit/chat/generative-ui/angular/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
},
"defaultConfiguration": "development",
"options": {
"proxyConfig": "cockpit/chat/generative-ui/angular/proxy.conf.json"
"proxyConfig": "cockpit/chat/generative-ui/angular/proxy.conf.mjs"
}
},
"test": {
Expand Down
9 changes: 0 additions & 9 deletions cockpit/chat/generative-ui/angular/proxy.conf.json

This file was deleted.

14 changes: 14 additions & 0 deletions cockpit/chat/generative-ui/angular/proxy.conf.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
import { portsFor } from '../../../../cockpit/ports.mjs';

const { langgraph } = portsFor('cockpit-chat-generative-ui-angular');

export default {
"/api": {
target: `http://localhost:${langgraph}`,
secure: false,
changeOrigin: true,
pathRewrite: {"^/api":""},
ws: true,
},
};
7 changes: 5 additions & 2 deletions cockpit/chat/input/angular/e2e/global-setup-impl.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// SPDX-License-Identifier: MIT
import { resolve } from 'node:path';
import { portsFor } from '../../../../../cockpit/ports.mjs';
import { createGlobalSetup } from '@threadplane-internal/e2e-harness';

const ports = portsFor('cockpit-chat-input-angular');

export default createGlobalSetup({
langgraphCwd: 'cockpit/chat/input/python',
langgraphPort: 5502,
langgraphPort: ports.langgraph,
angularProject: 'cockpit-chat-input-angular',
angularPort: 4502,
angularPort: ports.angular,
fixturesDir: resolve(__dirname, 'fixtures'),
});
6 changes: 5 additions & 1 deletion cockpit/chat/input/angular/e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// SPDX-License-Identifier: MIT
import { defineConfig, devices } from '@playwright/test';
import { portsFor } from '../../../../../cockpit/ports.mjs';

const { angular: angularPort } = portsFor('cockpit-chat-input-angular');


export default defineConfig({
testDir: '.',
Expand All @@ -9,7 +13,7 @@ export default defineConfig({
retries: process.env.CI ? 2 : 0,
reporter: process.env.CI ? [['list'], ['html', { open: 'never' }]] : 'list',
use: {
baseURL: 'http://localhost:4502',
baseURL: `http://localhost:${angularPort}`,
trace: 'retain-on-failure',
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
Expand Down
3 changes: 2 additions & 1 deletion cockpit/chat/input/angular/e2e/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"@threadplane-internal/e2e-harness/global-teardown": [
"libs/e2e-harness/src/global-teardown.ts"
]
}
},
"allowJs": true
},
"include": [
"**/*.ts"
Expand Down
2 changes: 1 addition & 1 deletion cockpit/chat/input/angular/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
},
"defaultConfiguration": "development",
"options": {
"proxyConfig": "cockpit/chat/input/angular/proxy.conf.json"
"proxyConfig": "cockpit/chat/input/angular/proxy.conf.mjs"
}
},
"smoke": {
Expand Down
9 changes: 0 additions & 9 deletions cockpit/chat/input/angular/proxy.conf.json

This file was deleted.

14 changes: 14 additions & 0 deletions cockpit/chat/input/angular/proxy.conf.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
import { portsFor } from '../../../../cockpit/ports.mjs';

const { langgraph } = portsFor('cockpit-chat-input-angular');

export default {
"/api": {
target: `http://localhost:${langgraph}`,
secure: false,
changeOrigin: true,
pathRewrite: {"^/api":""},
ws: true,
},
};
7 changes: 5 additions & 2 deletions cockpit/chat/interrupts/angular/e2e/global-setup-impl.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// SPDX-License-Identifier: MIT
import { resolve } from 'node:path';
import { portsFor } from '../../../../../cockpit/ports.mjs';
import { createGlobalSetup } from '@threadplane-internal/e2e-harness';

const ports = portsFor('cockpit-chat-interrupts-angular');

export default createGlobalSetup({
// Per-cap cleanup PR: each chat cap runs its OWN standalone backend
// (cockpit/chat/<name>/python) on `<angular_port> + 1000`. The
// proxy.conf.json target matches.
langgraphCwd: 'cockpit/chat/interrupts/python',
langgraphPort: 5503,
langgraphPort: ports.langgraph,
angularProject: 'cockpit-chat-interrupts-angular',
angularPort: 4503,
angularPort: ports.angular,
fixturesDir: resolve(__dirname, 'fixtures'),
});
6 changes: 5 additions & 1 deletion cockpit/chat/interrupts/angular/e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// SPDX-License-Identifier: MIT
import { defineConfig, devices } from '@playwright/test';
import { portsFor } from '../../../../../cockpit/ports.mjs';

const { angular: angularPort } = portsFor('cockpit-chat-interrupts-angular');


export default defineConfig({
testDir: '.',
Expand All @@ -9,7 +13,7 @@ export default defineConfig({
retries: process.env.CI ? 2 : 0,
reporter: process.env.CI ? [['list'], ['html', { open: 'never' }]] : 'list',
use: {
baseURL: 'http://localhost:4503',
baseURL: `http://localhost:${angularPort}`,
trace: 'retain-on-failure',
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
Expand Down
Loading
Loading