mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
add meteor reset E2E test coverage
This commit is contained in:
@@ -1865,6 +1865,15 @@ main.registerCommand({
|
||||
"MONGO_URL will NOT be reset.");
|
||||
}
|
||||
|
||||
// Always clean the default .meteor/local directory to prevent regressions.
|
||||
// When METEOR_LOCAL_DIR is set, also clean the custom local directory.
|
||||
const defaultLocalRelative = files.pathJoin('.meteor', 'local');
|
||||
const customLocalRelative = process.env.METEOR_LOCAL_DIR || null;
|
||||
const localDirs = [defaultLocalRelative];
|
||||
if (customLocalRelative && customLocalRelative !== defaultLocalRelative) {
|
||||
localDirs.push(customLocalRelative);
|
||||
}
|
||||
|
||||
const resetMeteorNpmCachePromise = options['skip-cache'] ? Promise.resolve() : files.rm_recursive_async(
|
||||
files.pathJoin(options.appDir, "node_modules", ".cache", "meteor")
|
||||
);
|
||||
@@ -1879,19 +1888,23 @@ main.registerCommand({
|
||||
// XXX detect the case where Meteor is running the app, but
|
||||
// MONGO_URL was set, so we don't see a Mongo process
|
||||
var findMongoPort = require('../runners/run-mongo.js').findMongoPort;
|
||||
var isRunning = !! await findMongoPort(files.pathJoin(options.appDir, ".meteor", "local", "db"));
|
||||
if (isRunning) {
|
||||
Console.error("reset: Meteor is running.");
|
||||
Console.error();
|
||||
Console.error(
|
||||
"This command does not work while Meteor is running your application.",
|
||||
"Exit the running Meteor development server.");
|
||||
return 1;
|
||||
// Check all local dirs for a running Mongo instance
|
||||
for (const localRelative of localDirs) {
|
||||
const localDir = files.pathResolve(options.appDir, localRelative);
|
||||
var isRunning = !! await findMongoPort(files.pathJoin(localDir, "db"));
|
||||
if (isRunning) {
|
||||
Console.error("reset: Meteor is running.");
|
||||
Console.error();
|
||||
Console.error(
|
||||
"This command does not work while Meteor is running your application.",
|
||||
"Exit the running Meteor development server.");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
files.rm_recursive_async(
|
||||
files.pathJoin(options.appDir, ".meteor", "local")
|
||||
...localDirs.map((rel) =>
|
||||
files.rm_recursive_async(files.pathResolve(options.appDir, rel))
|
||||
),
|
||||
resetMeteorNpmCachePromise,
|
||||
...resetRspackPromises,
|
||||
@@ -1901,11 +1914,19 @@ main.registerCommand({
|
||||
return;
|
||||
}
|
||||
|
||||
var allExceptDb = files.getPathsInDir(files.pathJoin('.meteor', 'local'), {
|
||||
cwd: options.appDir,
|
||||
maxDepth: 1,
|
||||
}).filter(function (path) {
|
||||
return !path.includes('.meteor/local/db');
|
||||
// Collect all paths inside each local dir except db
|
||||
var allExceptDb = localDirs.flatMap((rel) => {
|
||||
try {
|
||||
return files.getPathsInDir(rel, {
|
||||
cwd: options.appDir,
|
||||
maxDepth: 1,
|
||||
}).filter(function (p) {
|
||||
return !p.includes('/db');
|
||||
});
|
||||
} catch (e) {
|
||||
// Directory may not exist (e.g. default dir when only custom is used)
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
var allRemovePromises = [
|
||||
|
||||
@@ -145,6 +145,43 @@ export async function assertFileExist(tempDir, filePath, options = {}) {
|
||||
await checkFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to assert that a path does NOT exist
|
||||
* Retries until the path is gone or the timeout is exceeded
|
||||
* @param {string} basePath - Base directory path
|
||||
* @param {string} relPath - Relative path from basePath to check
|
||||
* @param {Object} options - Additional options
|
||||
* @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<void>}
|
||||
*/
|
||||
export async function assertPathNotExist(basePath, relPath, options = {}) {
|
||||
const { timeout = 5000, checkInterval = 100 } = options;
|
||||
const fullPath = path.join(basePath, relPath);
|
||||
const startTime = Date.now();
|
||||
|
||||
const check = async () => {
|
||||
const exists = await fs.pathExists(fullPath);
|
||||
if (exists && Date.now() - startTime < timeout) {
|
||||
await new Promise(r => setTimeout(r, checkInterval));
|
||||
return check();
|
||||
}
|
||||
if (exists) {
|
||||
const stat = await fs.stat(fullPath);
|
||||
const isDir = stat.isDirectory();
|
||||
let contents = '';
|
||||
if (isDir) {
|
||||
const entries = await fs.readdir(fullPath);
|
||||
contents = ` (contains: ${entries.join(', ')})`;
|
||||
}
|
||||
console.error(`assertPathNotExist FAILED: ${relPath} still exists at ${fullPath} [${isDir ? 'dir' : 'file'}, ${stat.size} bytes]${contents}`);
|
||||
}
|
||||
expect(exists).toBe(false);
|
||||
};
|
||||
|
||||
await check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to evaluate JavaScript code in the browser console and assert the result
|
||||
* @param {string} code - JavaScript code to evaluate in the browser console
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
assertFileExist,
|
||||
assertMeteorApp,
|
||||
assertMeteorReactApp,
|
||||
assertPathNotExist,
|
||||
assertRspackScriptTag
|
||||
} from "./assertions";
|
||||
import fs from "fs-extra";
|
||||
@@ -776,6 +777,57 @@ export function testMeteorRspackBundler(options) {
|
||||
await cleanupTempDir(buildOutputDir);
|
||||
}
|
||||
});
|
||||
|
||||
test(`"meteor reset" / should clear all caches and build artifacts`, async () => {
|
||||
// Derive METEOR_LOCAL_DIR-aware paths for assertions
|
||||
const resetEnv = { ...env, ...(env.meteorReset || {}) };
|
||||
const meteorLocalDirEnv = resetEnv.METEOR_LOCAL_DIR;
|
||||
const meteorLocalDirName = meteorLocalDirEnv
|
||||
? path.basename(meteorLocalDirEnv.replace(/\\/g, '/'))
|
||||
: '';
|
||||
const localDirSuffix = meteorLocalDirName ? `-${meteorLocalDirName}` : '';
|
||||
|
||||
// Verify build artifacts exist from previous tests
|
||||
await assertFileExist(appDir, buildDir);
|
||||
await assertFileExist(appDir, 'node_modules/.cache/rspack');
|
||||
|
||||
// Run meteor reset
|
||||
await runMeteorCommand("reset", [], appDir, {
|
||||
checkExitCode: true,
|
||||
env: resetEnv,
|
||||
});
|
||||
|
||||
// Verify Rspack build artifacts removed (always check defaults)
|
||||
await assertPathNotExist(appDir, buildDir);
|
||||
await assertPathNotExist(appDir, 'node_modules/.cache/rspack');
|
||||
await assertPathNotExist(appDir, '_build');
|
||||
await assertPathNotExist(appDir, 'public/build-assets');
|
||||
await assertPathNotExist(appDir, 'public/build-chunks');
|
||||
|
||||
// When METEOR_LOCAL_DIR is set, also verify suffixed paths are cleaned
|
||||
if (localDirSuffix) {
|
||||
await assertPathNotExist(appDir, `_build${localDirSuffix}`);
|
||||
await assertPathNotExist(appDir, `public/build-assets${localDirSuffix}`);
|
||||
await assertPathNotExist(appDir, `public/build-chunks${localDirSuffix}`);
|
||||
}
|
||||
|
||||
// Verify default .meteor/local caches are always cleaned
|
||||
await assertPathNotExist(appDir, '.meteor/local/build');
|
||||
await assertPathNotExist(appDir, '.meteor/local/bundler-cache');
|
||||
await assertPathNotExist(appDir, '.meteor/local/plugin-cache');
|
||||
|
||||
// When METEOR_LOCAL_DIR is set, also verify custom local dir is cleaned
|
||||
if (meteorLocalDirEnv && meteorLocalDirEnv !== '.meteor/local') {
|
||||
await assertPathNotExist(appDir, `${meteorLocalDirEnv}/build`);
|
||||
await assertPathNotExist(appDir, `${meteorLocalDirEnv}/bundler-cache`);
|
||||
await assertPathNotExist(appDir, `${meteorLocalDirEnv}/plugin-cache`);
|
||||
}
|
||||
|
||||
// Run custom assertions if provided
|
||||
if (customAssertions && customAssertions.afterReset) {
|
||||
await customAssertions.afterReset({ tempDir, appDir });
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1047,5 +1099,56 @@ export function testMeteorSkeleton(options) {
|
||||
await cleanupTempDir(buildOutputDir);
|
||||
}
|
||||
});
|
||||
|
||||
test(`"meteor reset" / should clear all caches and build artifacts`, async () => {
|
||||
// Derive METEOR_LOCAL_DIR-aware paths for assertions
|
||||
const resetEnv = { ...env, ...(env.meteorReset || {}) };
|
||||
const meteorLocalDirEnv = resetEnv.METEOR_LOCAL_DIR;
|
||||
const meteorLocalDirName = meteorLocalDirEnv
|
||||
? path.basename(meteorLocalDirEnv.replace(/\\/g, '/'))
|
||||
: '';
|
||||
const localDirSuffix = meteorLocalDirName ? `-${meteorLocalDirName}` : '';
|
||||
|
||||
// Verify build artifacts exist from previous tests
|
||||
await assertFileExist(tempDir, '_build');
|
||||
await assertFileExist(tempDir, 'node_modules/.cache/rspack');
|
||||
|
||||
// Run meteor reset
|
||||
await runMeteorCommand('reset', [], tempDir, {
|
||||
checkExitCode: true,
|
||||
env: resetEnv,
|
||||
});
|
||||
|
||||
// Verify Rspack build artifacts removed (always check defaults)
|
||||
await assertPathNotExist(tempDir, '_build');
|
||||
await assertPathNotExist(tempDir, 'node_modules/.cache/rspack');
|
||||
await assertPathNotExist(tempDir, 'node_modules/.cache/meteor');
|
||||
await assertPathNotExist(tempDir, 'public/build-assets');
|
||||
await assertPathNotExist(tempDir, 'public/build-chunks');
|
||||
|
||||
// When METEOR_LOCAL_DIR is set, also verify suffixed paths are cleaned
|
||||
if (localDirSuffix) {
|
||||
await assertPathNotExist(tempDir, `_build${localDirSuffix}`);
|
||||
await assertPathNotExist(tempDir, `public/build-assets${localDirSuffix}`);
|
||||
await assertPathNotExist(tempDir, `public/build-chunks${localDirSuffix}`);
|
||||
}
|
||||
|
||||
// Verify default .meteor/local caches are always cleaned
|
||||
await assertPathNotExist(tempDir, '.meteor/local/build');
|
||||
await assertPathNotExist(tempDir, '.meteor/local/bundler-cache');
|
||||
await assertPathNotExist(tempDir, '.meteor/local/plugin-cache');
|
||||
|
||||
// When METEOR_LOCAL_DIR is set, also verify custom local dir is cleaned
|
||||
if (meteorLocalDirEnv && meteorLocalDirEnv !== '.meteor/local') {
|
||||
await assertPathNotExist(tempDir, `${meteorLocalDirEnv}/build`);
|
||||
await assertPathNotExist(tempDir, `${meteorLocalDirEnv}/bundler-cache`);
|
||||
await assertPathNotExist(tempDir, `${meteorLocalDirEnv}/plugin-cache`);
|
||||
}
|
||||
|
||||
// Run custom assertions if provided
|
||||
if (customAssertions.afterReset) {
|
||||
await customAssertions.afterReset({ tempDir });
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
// Helper functions for Rspack integration
|
||||
const files = require('../fs/files');
|
||||
const path = require('path');
|
||||
const { getMeteorConfig } = require("./meteor-config");
|
||||
|
||||
const config = getMeteorConfig();
|
||||
|
||||
// Derive the METEOR_LOCAL_DIR suffix the same way packages/rspack/lib/constants.js does,
|
||||
// so reset cleans the correct directories when running multiple instances.
|
||||
const meteorLocalDirName = process.env.METEOR_LOCAL_DIR
|
||||
? path.basename(process.env.METEOR_LOCAL_DIR.replace(/\\/g, '/'))
|
||||
: '';
|
||||
const localDirSuffix = meteorLocalDirName ? `-${meteorLocalDirName}` : '';
|
||||
|
||||
// Get the build context from environment variable or use default "_build"
|
||||
const rspackBuildContext = config?.buildContext || process.env.RSPACK_BUILD_CONTEXT || "_build";
|
||||
const rspackBuildContext = config?.buildContext || process.env.RSPACK_BUILD_CONTEXT || `_build${localDirSuffix}`;
|
||||
|
||||
// Get the assets context from environment variable or use default "build-assets"
|
||||
const rspackAssetsContext = config?.assetsContext || process.env.RSPACK_ASSETS_CONTEXT || "build-assets";
|
||||
const rspackAssetsContext = config?.assetsContext || process.env.RSPACK_ASSETS_CONTEXT || `build-assets${localDirSuffix}`;
|
||||
|
||||
// Get the bundles context from environment variable or use default "build-chunks"
|
||||
const rspackChunksContext = config?.chunksContext || process.env.RSPACK_CHUNKS_CONTEXT || "build-chunks";
|
||||
const rspackChunksContext = config?.chunksContext || process.env.RSPACK_CHUNKS_CONTEXT || `build-chunks${localDirSuffix}`;
|
||||
|
||||
// Cache the regex pattern for performance
|
||||
const rspackFilePattern = new RegExp(`^${rspackBuildContext}\\/.*\\/[^\\/]*-rspack\\.js$`);
|
||||
@@ -35,16 +43,31 @@ exports.getRspackResourcesContexts = function() {
|
||||
];
|
||||
};
|
||||
|
||||
// Function to get the rspack app contexts
|
||||
// Function to get the rspack app contexts for cleanup.
|
||||
// Always includes the default paths (_build, build-assets, build-chunks) to
|
||||
// prevent regressions, plus suffixed paths when METEOR_LOCAL_DIR is set.
|
||||
exports.getRspackAppContexts = function(appDir) {
|
||||
const rspackResourcesContexts = exports.getRspackResourcesContexts();
|
||||
return [
|
||||
const contexts = [
|
||||
files.pathJoin(appDir, "node_modules", ".cache", "rspack"),
|
||||
files.pathJoin(appDir, rspackBuildContext),
|
||||
...rspackResourcesContexts.reduce((arr, context) => [
|
||||
...arr,
|
||||
files.pathJoin(appDir, `public/${context}`),
|
||||
files.pathJoin(appDir, `public/${context}`)
|
||||
], [])
|
||||
];
|
||||
|
||||
// Always include defaults
|
||||
const defaults = ['_build', 'build-assets', 'build-chunks'];
|
||||
for (const name of defaults) {
|
||||
contexts.push(files.pathJoin(appDir, name));
|
||||
contexts.push(files.pathJoin(appDir, `public/${name}`));
|
||||
contexts.push(files.pathJoin(appDir, `private/${name}`));
|
||||
}
|
||||
|
||||
// When METEOR_LOCAL_DIR is set, also include suffixed paths
|
||||
if (localDirSuffix) {
|
||||
const suffixed = defaults.map(name => `${name}${localDirSuffix}`);
|
||||
for (const name of suffixed) {
|
||||
contexts.push(files.pathJoin(appDir, name));
|
||||
contexts.push(files.pathJoin(appDir, `public/${name}`));
|
||||
contexts.push(files.pathJoin(appDir, `private/${name}`));
|
||||
}
|
||||
}
|
||||
|
||||
return contexts;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user