diff --git a/tools/modern-tests/apps/react/package.json b/tools/modern-tests/apps/react/package.json index 8cad86c1af..bca1b3a374 100644 --- a/tools/modern-tests/apps/react/package.json +++ b/tools/modern-tests/apps/react/package.json @@ -14,6 +14,9 @@ "react": "^18.2.0", "react-dom": "^18.2.0" }, + "devDependencies": { + "playwright": "^1.54.2" + }, "meteor": { "mainModule": { "client": "client/main.jsx", diff --git a/tools/modern-tests/helpers.js b/tools/modern-tests/helpers.js index 9dbea3168b..7d4d03a49e 100644 --- a/tools/modern-tests/helpers.js +++ b/tools/modern-tests/helpers.js @@ -422,14 +422,81 @@ export async function appendFileContent(tempDir, filePath, options = {}) { } /** - * Helper function to wait for a specific console message from a Playwright page - * @param {Object} page - The Playwright page object - * @param {string|RegExp} pattern - String or RegExp pattern to wait for in console messages - * @param {Object} options - Options for waiting - * @param {number} options.timeout - Maximum time to wait in milliseconds (default: 30000) - * @param {number} options.checkInterval - Interval between checks in milliseconds (default: 100) - * @returns {Promise} - A promise that resolves with the matched console message + * Helper function to run Meteor tests with the meteortesting:mocha driver package + * @param {string} tempDir - Path to the directory containing the app + * @param {number} port - Port to run the tests on + * @param {Object} options - Additional options + * @param {string|RegExp} options.waitForOutput - Output pattern to wait for + * @param {Object} options.waitOptions - Options for waitForMeteorOutput + * @param {string[]} options.commandOptions - Additional command line options for the test command + * @param {boolean} options.checkTestResults - Whether to check test results and propagate failures to Jest + * @returns {Object} - The meteor process and output lines */ +export async function runMeteorTests(tempDir, port, options = {}) { + // Start Meteor tests + console.log(`Starting Meteor tests on port ${port}...`); + + // Determine if we need to capture output + const captureOutput = !!options.waitForOutput || !!options.checkTestResults; + + // Combine base options with any additional command options + const args = ['--port', port.toString(), '--driver-package', 'meteortesting:mocha']; + if (options.commandOptions && Array.isArray(options.commandOptions)) { + args.push(...options.commandOptions); + } + + // Run the meteor test command + const { meteorProcess, outputLines } = await runMeteorCommand( + 'test', + args, + tempDir, + { + env: { + ...process.env, + TEST_BROWSER_DRIVER: 'playwright' + } + }, + captureOutput + ); + + // If a specific output pattern is requested, wait for it + if (options.waitForOutput) { + await waitForMeteorOutput( + outputLines, + options.waitForOutput, + options + ); + } + + // Wait for server to be up + console.log(`Waiting for test server to be available on port ${port}...`); + await waitOn({ + resources: [`http-get://localhost:${port}`], + timeout: 60000 + }); + + // If we're checking test results, wait for the process to complete and check for failures + if (options.checkTestResults) { + console.log('Waiting for Meteor tests to complete...'); + + // Create a promise that resolves when the process exits + const processResult = await new Promise((resolve) => { + meteorProcess.on('exit', (code) => { + resolve({ code, outputLines }); + }); + }); + + // Check for test failures in the output + const hasFailures = processResult.code !== 0; + if (hasFailures) { + // Throw an error with the failure messages, which will cause the Jest test to fail + throw new Error(`Meteor tests failed:\n${failureMessages.join('\n')}`); + } + } + + return { meteorProcess, outputLines }; +} + export async function waitForPlaywrightConsole(page, pattern, options = {}) { const timeout = options.timeout || 30000; // Default 30 seconds timeout const checkInterval = options.checkInterval || 100; // Check every 100ms by default diff --git a/tools/modern-tests/react.test.js b/tools/modern-tests/react.test.js index c614ed5b88..936c0034e6 100644 --- a/tools/modern-tests/react.test.js +++ b/tools/modern-tests/react.test.js @@ -9,14 +9,14 @@ import { wait, appendFileContent, waitForMeteorOutput, - waitForPlaywrightConsole, + waitForPlaywrightConsole, runMeteorTests } from "./helpers"; import { assertMeteorReactApp, assertRspackScriptTag, assertFileExist } from './assertions'; import fs from 'fs-extra'; import path from 'path'; -describe('React App Bundling', () => { - describe('Meteor Creator', () => { +describe('React App Bundling /', () => { + describe('Meteor Creator /', () => { const PORT = 3100; beforeAll(async () => { @@ -66,7 +66,7 @@ describe('React App Bundling', () => { }); }); - describe('Meteor Bundler', () => { + describe('Meteor Bundler /', () => { const PORT = 3101; let meteorProcess; let tempDir; @@ -99,7 +99,7 @@ describe('React App Bundling', () => { }); }); - describe('Meteor+Rspack Bundler', () => { + describe('Meteor+Rspack Bundler /', () => { const PORT = 3102; let meteorProcess; let tempDir; @@ -129,15 +129,12 @@ describe('React App Bundling', () => { meteorProcess = result.meteorProcess; // Wait for a margin - await wait(500); + await wait(1000); // Assert that the config files exists await assertFileExist(tempDir, '.gitignore', { content: '_build' }); await assertFileExist(tempDir, 'rspack.config.js', { content: '@meteorjs/rspack' }); - // Wait for a margin - await wait(500); - // Kill the meteor process await killMeteorProcess(meteorProcess); @@ -248,5 +245,30 @@ describe('React App Bundling', () => { await killProcessByPort(PORT); await killProcessByPort('8080'); }); + + test(`"meteor test" / should run tests with Rspack`, async () => { + // Run the Meteor app and wait for "restarted at" output + const result = await runMeteorTests(tempDir, PORT, { + waitForOutput: "=> App running at:", + commandOptions: ['--once'], + checkTestResults: true, + }); + meteorProcess = result.meteorProcess; + + // Wait for a margin + await wait(500); + + // Assert that the app files exists + await assertFileExist(tempDir, '_build/test/test-entry.js'); + await assertFileExist(tempDir, '_build/test/test-rspack.js'); + await assertFileExist(tempDir, '_build/test/test-meteor.js'); + + // Note: We don't need to kill the meteor process here as it should have completed + // when using --once and checkTestResults: true + + // Ensure any process on the port is killed + await killProcessByPort(PORT); + await killProcessByPort('8080'); + }); }); });