Compare commits

...

4 Commits

Author SHA1 Message Date
Nicholas Cunningham 2b4cc57874 feat(testing): update react-rsbuild test to use dynamic port allocation
- Update agents config to run e2e-release without parallel enabled
2025-07-31 10:17:01 -06:00
Nicholas Cunningham c18eb69493 feat(testing): implement dynamic port allocation 2025-07-31 09:04:04 -06:00
Nicholas Cunningham 46135510e8 feat(testing): test parallel execution for webpack e2e tests
- Increase maxWorkers from 1 to 2 in webpack e2e config
- Test parallel e2e execution to identify potential conflicts
- Builds on existing parallel-continuous infrastructure
2025-07-31 09:03:45 -06:00
Jason Jean 60838b283e chore(repo): try parallel 2025-07-31 09:03:45 -06:00
8 changed files with 101 additions and 14 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
launch-templates:
linux-medium:
resource-class: 'docker_linux_amd64/medium+'
linux-large:
resource-class: 'docker_linux_amd64/large'
image: 'us-east1-docker.pkg.dev/nxcloudoperations/nx-cloud/nx-agents-base-images:ubuntu22.04-node20.19-v1'
env:
GIT_AUTHOR_EMAIL: test@test.com
+16 -6
View File
@@ -1,13 +1,23 @@
distribute-on:
default: auto linux-medium, 1 linux-extra-large
default: auto linux-large, 1 linux-extra-large
assignment-rules:
# e2e-release tests must run sequentially due to shared git state and publishing operations
- projects:
- e2e-release
targets:
- e2e-ci**
run-on:
- agent: linux-large
parallelism: 1
# All other e2e tests can run in parallel
- targets:
- e2e-ci**
run-on:
- agent: linux-medium
parallelism: 1
- agent: linux-large
parallelism: 2
- agent: linux-extra-large
parallelism: 1
parallelism: 3
# These projects should not need to be isolated.
- projects:
@@ -29,13 +39,13 @@ assignment-rules:
- targets:
- lint
run-on:
- agent: linux-medium
- agent: linux-large
parallelism: 6
- targets:
- '*'
run-on:
- agent: linux-medium
- agent: linux-large
parallelism: 3
- agent: linux-extra-large
parallelism: 3
@@ -1,5 +1,6 @@
import {
cleanupProject,
getAvailablePort,
killProcessAndPorts,
newProject,
runCommandUntil,
@@ -310,12 +311,12 @@ describe('Independent Deployability', () => {
it('should support host and remote with library type var', async () => {
const shell = uniq('shell');
const remote = uniq('remote');
const shellPort = await getAvailablePort();
runCLI(
`generate @nx/react:host ${shell} --remotes=${remote} --bundler=webpack --e2eTestRunner=cypress --no-interactive --skipFormat`
);
const shellPort = readPort(shell);
const remotePort = readPort(remote);
// update host and remote to use library type var
@@ -6,6 +6,7 @@ import {
runE2ETests,
uniq,
updateFile,
getAvailablePort,
} from '@nx/e2e-utils';
import { readPort, runCLI } from './utils';
import { stripIndents } from 'nx/src/utils/strip-indents';
@@ -24,9 +25,10 @@ describe('React Rspack Module Federation Misc', () => {
it('should generate host and remote apps in webpack, convert to rspack and use playwright for e2es', async () => {
const shell = uniq('shell');
const remote1 = uniq('remote1');
const shellPort = await getAvailablePort();
runCLI(
`generate @nx/react:host ${shell} --remotes=${remote1} --bundler=webpack --e2eTestRunner=playwright --style=css --no-interactive --skipFormat`
`generate @nx/react:host ${shell} --remotes=${remote1} --bundler=webpack --devServerPort=${shellPort} --e2eTestRunner=playwright --style=css --no-interactive --skipFormat`
);
runCLI(
@@ -77,9 +79,10 @@ describe('React Rspack Module Federation Misc', () => {
const shell = uniq('shell');
const remote1 = uniq('remote1');
const remote2 = uniq('remote2');
const shellPort = await getAvailablePort();
runCLI(
`generate @nx/react:host apps/${shell} --name=${shell} --remotes=${remote1} --bundler=webpack --e2eTestRunner=cypress --style=css --no-interactive --skipFormat`
`generate @nx/react:host apps/${shell} --name=${shell} --remotes=${remote1} --bundler=webpack --devServerPort=${shellPort} --e2eTestRunner=cypress --style=css --no-interactive --skipFormat`
);
runCLI(
@@ -137,8 +140,10 @@ describe('React Rspack Module Federation Misc', () => {
const shell = uniq('shell');
const remote1 = uniq('remote1');
const remote2 = uniq('remote2');
const shellPort = await getAvailablePort();
runCLI(
`generate @nx/react:host apps/${shell} --name=${shell} --remotes=${remote1} --bundler=rspack --e2eTestRunner=cypress --style=css --no-interactive --skipFormat`
`generate @nx/react:host apps/${shell} --name=${shell} --remotes=${remote1} --bundler=rspack --devServerPort=${shellPort} --e2eTestRunner=cypress --style=css --no-interactive --skipFormat`
);
runCLI(
+3 -1
View File
@@ -1,6 +1,7 @@
import {
checkFilesExist,
cleanupProject,
getAvailablePort,
newProject,
runCLI,
runCLIAsync,
@@ -87,9 +88,10 @@ describe('Build React applications and libraries with Rsbuild', () => {
it('should support bundling with Rsbuild and Jest', async () => {
const rsbuildApp = uniq('rsbuildapp');
const port = await getAvailablePort();
runCLI(
`generate @nx/react:app apps/${rsbuildApp} --bundler=rsbuild --unitTestRunner=jest --no-interactive --linter=eslint`
`generate @nx/react:app apps/${rsbuildApp} --port=${port} --bundler=rsbuild --unitTestRunner=jest --no-interactive --linter=eslint`
);
const appTestResults = await runCLIAsync(`test ${rsbuildApp}`);
+2 -1
View File
@@ -1,6 +1,7 @@
import {
checkFilesExist,
cleanupProject,
getAvailablePort,
killPorts,
newProject,
readFile,
@@ -65,7 +66,7 @@ describe('Build React applications and libraries with Vite', () => {
it('should generate app with custom port', async () => {
const viteApp = uniq('viteapp');
const customPort = 8081;
const customPort = await getAvailablePort();
runCLI(
`generate @nx/react:app apps/${viteApp} --bundler=vite --port=${customPort} --unitTestRunner=vitest --no-interactive --linter=eslint --e2eTestRunner=playwright`
+1
View File
@@ -11,3 +11,4 @@ export * from './log-utils';
export * from './test-utils';
export * from './process-utils';
export * from './strip-indents';
export * from './port-utils';
+67
View File
@@ -0,0 +1,67 @@
import * as net from 'net';
/**
* Finds an available port by creating a temporary server on port 0
* and letting the OS assign a free port.
*/
export async function getAvailablePort(): Promise<number> {
return new Promise((resolve, reject) => {
const server = net.createServer();
server.unref();
server.on('error', reject);
server.listen(0, () => {
const addressInfo = server.address();
if (!addressInfo || typeof addressInfo === 'string') {
reject(new Error('Failed to get server address'));
return;
}
const port = addressInfo.port;
server.close(() => {
resolve(port);
});
});
});
}
/**
* Finds multiple available ports for running multiple services
*
* @param count - Number of ports needed
* @returns Promise<number[]> - Array of available port numbers
*/
export async function getAvailablePorts(count: number): Promise<number[]> {
const ports: number[] = [];
for (let i = 0; i < count; i++) {
const port = await getAvailablePort();
ports.push(port);
}
return ports;
}
/**
* Checks if a specific port is available
*
* @param port - Port number to check
* @returns Promise<boolean> - True if port is available, false otherwise
*/
export async function isPortAvailable(port: number): Promise<boolean> {
return new Promise((resolve) => {
const server = net.createServer();
server.once('error', (err: NodeJS.ErrnoException) => {
if (err.code === 'EADDRINUSE') {
resolve(false);
} else {
resolve(false);
}
});
server.once('listening', () => {
server.close();
resolve(true);
});
server.listen(port);
});
}