diff --git a/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll b/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll
index aa3aa81bffe..45a132e088a 100644
--- a/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll
+++ b/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll
@@ -56,9 +56,7 @@ predicate isGeneratedCodeFile(File f) { isGenerated(f.getATopLevel()) }
predicate isTestFile(File f) {
exists(Test t | t.getFile() = f)
or
- exists(string stemExt | stemExt = "test" or stemExt = "spec" |
- f = getTestFile(any(File orig), stemExt)
- )
+ f = getATestFile(_)
or
f.getAbsolutePath().regexpMatch(".*/__(mocks|tests)__/.*")
}
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Testing.qll b/javascript/ql/lib/semmle/javascript/frameworks/Testing.qll
index fb2d85523d4..262d90e8499 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/Testing.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/Testing.qll
@@ -40,7 +40,7 @@ class BDDTest extends Test, @call_expr {
/**
* Gets the test file for `f` with stem extension `stemExt`.
- * That is, a file named file named `..` in the
+ * That is, a file named `..` in the
* same directory as `f` which is named `.`.
*/
bindingset[stemExt]
@@ -48,6 +48,33 @@ File getTestFile(File f, string stemExt) {
result = f.getParentContainer().getFile(f.getStem() + "." + stemExt + "." + f.getExtension())
}
+/**
+ * Gets a test file for `f`.
+ * That is, a file named `..` in the
+ * same directory as `f`, where `f` is named `.` and
+ * `` is a well-known test file identifier, such as `test` or `spec`.
+ */
+File getATestFile(File f) {
+ result = f.getParentContainer().getFile(getATestFileName(f))
+}
+
+/**
+ * Gets a name of a test file for `f`.
+ * That is, `..` where
+ * `f` is named `.` and `` is
+ * a well-known test file identifier, such as `test` or `spec`.
+ */
+// Helper predicate factored out for performance.
+// This predicate is linear in the size of f, and forces
+// callers to join only once against f rather than two separate joins
+// when computing the stem and the extension.
+// This loses some flexibility because callers cannot specify
+// an arbitrary stemExt.
+pragma[nomagic]
+private string getATestFileName(File f) {
+ result = f.getStem() + "." + ["test", "spec"] + "." + f.getExtension()
+}
+
/**
* A Jest test, that is, an invocation of a global function named
* `test` where the first argument is a string and the second