diff --git a/tools/modern-tests/assertions.js b/tools/modern-tests/assertions.js index 6080888a98..b4be00b2b3 100644 --- a/tools/modern-tests/assertions.js +++ b/tools/modern-tests/assertions.js @@ -2,6 +2,9 @@ * This file contains assertion helpers for testing Meteor applications. */ +import fs from 'fs-extra'; +import path from 'path'; + /** * Helper function to assert that a Meteor React app is running correctly * @param {number} port - Port where the app is running @@ -38,3 +41,75 @@ export async function assertRspackScriptTag(port, shoudlExist = true) { const hasRspackScript = scriptTags.some(src => src && src.includes('__rspack__')); expect(hasRspackScript).toBe(shoudlExist); } + +/** + * Helper function to assert that a single file exists and optionally contains specific content + * @param {string} tempDir - Path to the temporary directory + * @param {string} filePath - File path relative to tempDir + * @param {Object} options - Additional options + * @param {string} options.content - Content to check for + * @param {boolean} options.exactMatch - Whether the content should be an exact match (default: false) + * @param {number} options.timeout - Maximum time to wait in milliseconds (default: 5000) + * @param {number} options.checkInterval - Interval between checks in milliseconds (default: 100) + * @returns {Promise} + */ +export async function assertFileExist(tempDir, filePath, options = {}) { + const { content, exactMatch = false, timeout = 5000, checkInterval = 100 } = options; + const fullPath = path.join(tempDir, filePath); + + const startTime = Date.now(); + + // Function to check file existence and content + const checkFile = async () => { + // Check if file exists + const fileExists = await fs.pathExists(fullPath); + + if (!fileExists) { + // If file doesn't exist and we haven't exceeded the timeout, wait and retry + if (Date.now() - startTime < timeout) { + await new Promise(resolve => setTimeout(resolve, checkInterval)); + return checkFile(); + } + // If we've exceeded the timeout, fail the test + expect(fileExists).toBe(true); + return false; + } + + // If content check is requested + if (content) { + // Read the file content + const fileContent = await fs.readFile(fullPath, 'utf8'); + + if (exactMatch) { + // Check for exact match + if (fileContent !== content) { + // If content doesn't match and we haven't exceeded the timeout, wait and retry + if (Date.now() - startTime < timeout) { + await new Promise(resolve => setTimeout(resolve, checkInterval)); + return checkFile(); + } + // If we've exceeded the timeout, fail the test + expect(fileContent).toBe(content); + return false; + } + } else { + // Check if file includes the specified content + if (!fileContent.includes(content)) { + // If content doesn't include the specified content and we haven't exceeded the timeout, wait and retry + if (Date.now() - startTime < timeout) { + await new Promise(resolve => setTimeout(resolve, checkInterval)); + return checkFile(); + } + // If we've exceeded the timeout, fail the test + expect(fileContent).toContain(content); + return false; + } + } + } + + return true; + }; + + // Start checking + await checkFile(); +} diff --git a/tools/modern-tests/react.test.js b/tools/modern-tests/react.test.js index df16bde0f0..a73cfbbe7c 100644 --- a/tools/modern-tests/react.test.js +++ b/tools/modern-tests/react.test.js @@ -7,7 +7,7 @@ import { createMeteorApp, runMeteorCommand, wait } from "./helpers"; -import { assertMeteorReactApp, assertRspackScriptTag } from './assertions'; +import { assertMeteorReactApp, assertRspackScriptTag, assertFileExist } from './assertions'; import fs from 'fs-extra'; import path from 'path'; import waitOn from "wait-on"; @@ -16,6 +16,11 @@ describe('React App Bundling', () => { describe.skip('Meteor Creator', () => { const PORT = 3100; + beforeAll(async () => { + // Ensure any process on the port is killed + await killProcessByPort(PORT); + }); + test('"meteor create" should create a new Meteor app with --react example', async () => { // Create a new Meteor app with --react example const result = await createMeteorApp('react', 'react'); @@ -64,6 +69,9 @@ describe('React App Bundling', () => { let tempDir; beforeAll(async () => { + // Ensure any process on the port is killed + await killProcessByPort(PORT); + // Setup the Meteor app tempDir = (await setupMeteorApp('react'))?.tempDir; }); @@ -120,6 +128,10 @@ describe('React App Bundling', () => { // Wait for a margin await wait(500); + // Assert that the config files exists + await assertFileExist(tempDir, '.gitignore', { content: '_build' }); + await assertFileExist(tempDir, 'rspack.config.js', { content: '@meteorjs/rspack' }); + // Kill the meteor process await killMeteorProcess(meteorProcess); @@ -138,6 +150,14 @@ describe('React App Bundling', () => { // Wait for a margin await wait(500); + // Assert that the app files exists + await assertFileExist(tempDir, '_build/main-dev/client-entry.js'); + await assertFileExist(tempDir, '_build/main-dev/client-rspack.js'); + await assertFileExist(tempDir, '_build/main-dev/client-meteor.js'); + await assertFileExist(tempDir, '_build/main-dev/server-entry.js'); + await assertFileExist(tempDir, '_build/main-dev/server-rspack.js'); + await assertFileExist(tempDir, '_build/main-dev/server-meteor.js'); + // Assert that the Meteor React app is running correctly await assertMeteorReactApp(PORT); @@ -163,6 +183,16 @@ describe('React App Bundling', () => { // Wait for a margin await wait(500); + // Assert that the app files exists + await assertFileExist(tempDir, '_build/main-prod/client-entry.js'); + await assertFileExist(tempDir, '_build/main-prod/client-rspack.js'); + await assertFileExist(tempDir, '_build/main-prod/client-meteor.js'); + await assertFileExist(tempDir, '_build/main-prod/server-entry.js'); + await assertFileExist(tempDir, '_build/main-prod/server-rspack.js'); + await assertFileExist(tempDir, '_build/main-prod/server-meteor.js'); + + await assertFileExist(tempDir, 'server/main.js'); + // Assert that the Meteor React app is running correctly await assertMeteorReactApp(PORT);