diff --git a/.gitignore b/.gitignore
index 4742e13056..36de1287c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ node_modules
\#*\#
.\#*
.idea
+!.idea/icon.svg
*.iml
*.sublime-project
*.sublime-workspace
diff --git a/.idea/icon.svg b/.idea/icon.svg
new file mode 100755
index 0000000000..16ecae9b10
--- /dev/null
+++ b/.idea/icon.svg
@@ -0,0 +1,21 @@
+
\ No newline at end of file
diff --git a/packages/accounts-password/password_server.js b/packages/accounts-password/password_server.js
index cb7cb96112..4bb1de5149 100644
--- a/packages/accounts-password/password_server.js
+++ b/packages/accounts-password/password_server.js
@@ -507,6 +507,7 @@ Meteor.methods({forgotPassword: async options => {
const user = await Accounts.findUserByEmail(options.email, { fields: { emails: 1 } });
if (!user) {
+ if (Accounts._options.ambiguousErrorMessages) return;
Accounts._handleError("User not found");
}
diff --git a/packages/accounts-password/password_tests.js b/packages/accounts-password/password_tests.js
index 5df402d74d..f409516c59 100644
--- a/packages/accounts-password/password_tests.js
+++ b/packages/accounts-password/password_tests.js
@@ -1417,9 +1417,8 @@ if (Meteor.isServer) (() => {
);
Accounts._options.ambiguousErrorMessages = true;
- await test.throwsAsync(
- async () => await Meteor.callAsync('forgotPassword', wrongOptions),
- 'Something went wrong. Please check your credentials'
+ await test.doesNotThrowsAsync(
+ async () => await Meteor.callAsync("forgotPassword", wrongOptions)
);
Accounts._options.ambiguousErrorMessages = false;
diff --git a/packages/tinytest/README.md b/packages/tinytest/README.md
index d93c5f1222..514ae883a7 100644
--- a/packages/tinytest/README.md
+++ b/packages/tinytest/README.md
@@ -272,7 +272,13 @@ EXPERIMENTAL way to compare two strings that results in a nicer display in the t
### Assertions without optional fail messages
-`test.throws(func, expected);`
+`test.throws(func, expected[, message]);`
+
+`test.throwsAsync(func, expected[, message]);`
+
+`test.doesNotThrows(func[, failureMessage]);`
+
+`test.doesNotThrowsAsync(func[, failureMessage]);`
`expected` can be:
@@ -281,6 +287,8 @@ EXPERIMENTAL way to compare two strings that results in a nicer display in the t
- `regexp`: pass if the exception message passes the regexp.
- `function`: call the function as a predicate with the exception.
+`doesNotThrows` and `doesNotThrowsAsync` assert that the function does not throw. If the function throws, the assertion fails. The optional `failureMessage` is only used to annotate the failure.
+
Note: Node's `assert.throws` also accepts a constructor to test whether the error is of the expected class. But since JavaScript can't distinguish between constructors and plain functions and Node's `assert.throws` also accepts a predicate function, if the error fails the `instanceof` test with the constructor then the constructor is then treated as a predicate and called (!)
The upshot is, if you want to test whether an error is of a particular class, use a predicate function.
diff --git a/packages/tinytest/tinytest.js b/packages/tinytest/tinytest.js
index b66097aafb..f8c29bab74 100644
--- a/packages/tinytest/tinytest.js
+++ b/packages/tinytest/tinytest.js
@@ -244,6 +244,19 @@ export class TestCaseResults {
// The upshot is, if you want to test whether an error is of a
// particular class, use a predicate function.
//
+ /**
+ * Assert that `f` throws.
+ *
+ * `expected` can be:
+ * - undefined: accept any exception.
+ * - string: pass if the string is a substring of the exception message.
+ * - regexp: pass if the exception message passes the regexp.
+ * - function: call the function as a predicate with the exception.
+ *
+ * @param {Function} f
+ * @param {*} expected
+ * @param {String} message
+ */
throws(f, expected, message) {
let actual;
const predicate = this._guessPredicate(expected);
@@ -258,10 +271,34 @@ export class TestCaseResults {
}
/**
- * Same as throw, but accepts an async function as a parameter.
- * @param f
- * @param expected
- * @param message
+ * Assert that `f` does not throw.
+ * @param {Function} f
+ * @param {String} failureMessage
+ */
+ doesNotThrows(f, failureMessage) {
+ let actual;
+
+ try {
+ f();
+ } catch (exception) {
+ actual = exception;
+ }
+
+ if (!actual) {
+ this.ok();
+ } else {
+ this.fail({
+ type: "throws",
+ message: ("threw an error unexpectedly: " + actual.message) + (failureMessage ? ": " + failureMessage : ""),
+ });
+ }
+ }
+
+ /**
+ * Same as `throws`, but accepts an async function as a parameter.
+ * @param {Function} f
+ * @param {*} expected
+ * @param {String} message
* @returns {Promise}
*/
async throwsAsync(f, expected, message) {
@@ -276,6 +313,31 @@ export class TestCaseResults {
this._assertActual(actual, predicate, message);
}
+ /**
+ * Same as `doesNotThrows`, but accepts an async function as a parameter.
+ * @param {Function} f
+ * @param {String} failureMessage
+ * @returns {Promise}
+ */
+ async doesNotThrowsAsync(f, failureMessage) {
+ let actual;
+
+ try {
+ await f();
+ } catch (exception) {
+ actual = exception;
+ }
+
+ if (!actual) {
+ this.ok();
+ } else {
+ this.fail({
+ type: "throws",
+ message: ("threw an error unexpectedly: " + actual.message) + (failureMessage ? ": " + failureMessage : ""),
+ });
+ }
+ }
+
isTrue(v, msg) {
if (v)
this.ok();
diff --git a/tools/fs/safe-watcher.ts b/tools/fs/safe-watcher.ts
index 6e2bebc023..38e57d0bc6 100644
--- a/tools/fs/safe-watcher.ts
+++ b/tools/fs/safe-watcher.ts
@@ -312,9 +312,13 @@ async function ensureWatchRoot(dirPath: string): Promise {
if (/Events were dropped/.test(err.message)) {
return;
}
+ if (/RootResolveError/.test(err.message) || /failed to resolve root/.test(err.message)) {
+ console.warn(`Parcel watcher root resolve error on ${osDirPath}, ignoring: ${err.message}`);
+ ignoredWatchRoots.add(dirPath);
+ watchRoots.delete(dirPath);
+ return;
+ }
console.error(`Parcel watcher error on ${osDirPath}:`, err);
- // Only disable native watching for critical errors (like ENOSPC).
- // @ts-ignore
if (err.code === "ENOSPC" || err.errno === require("constants").ENOSPC) {
fallbackToPolling();
}
@@ -340,9 +344,11 @@ async function ensureWatchRoot(dirPath: string): Promise {
(e.code === "ENOTDIR" ||
/Not a directory/.test(e.message) ||
e.code === "EBADF" ||
- /Bad file descriptor/.test(e.message))
+ /Bad file descriptor/.test(e.message) ||
+ /RootResolveError/.test(e.message) ||
+ /failed to resolve root/.test(e.message))
) {
- console.warn(`Skipping watcher for ${osDirPath}: not a directory`);
+ console.warn(`Skipping watcher for ${osDirPath}: ${e.message || 'not watchable'}`);
ignoredWatchRoots.add(dirPath);
} else {
console.error(`Failed to start watcher for ${osDirPath}:`, e);
diff --git a/tools/static-assets/skel-react/client/main.jsx b/tools/static-assets/skel-react/client/main.jsx
index c7e0c1f05e..879c8859a8 100644
--- a/tools/static-assets/skel-react/client/main.jsx
+++ b/tools/static-assets/skel-react/client/main.jsx
@@ -1,9 +1,10 @@
-import { createRoot } from 'react-dom/client';
-import { Meteor } from 'meteor/meteor';
-import { App } from '/imports/ui/App';
+import { createRoot } from "react-dom/client";
+import { Meteor } from "meteor/meteor";
+import { App } from "/imports/ui/App";
+import "/imports/ui/styles.css";
Meteor.startup(() => {
- const container = document.getElementById('react-target');
+ const container = document.getElementById("react-target");
const root = createRoot(container);
root.render();
});
diff --git a/tools/static-assets/skel-react/imports/ui/App.jsx b/tools/static-assets/skel-react/imports/ui/App.jsx
index eeeee2161a..6b12ade912 100644
--- a/tools/static-assets/skel-react/imports/ui/App.jsx
+++ b/tools/static-assets/skel-react/imports/ui/App.jsx
@@ -1,10 +1,13 @@
-import { Hello } from './Hello.jsx';
-import { Info } from './Info.jsx';
+import { Counter } from "./Counter.jsx";
+import { Header } from "./Header.jsx";
+import { Info } from "./Info.jsx";
export const App = () => (
-