fix(runner): recognize private-field property access in the wait guard

A private field named `#await` made the regex-literal check extract
the bare word `await`, so the scanner classified the `/` after it as
a regex start instead of division. That let the fake regex literal
mask a real `vi.waitFor(` or `expect.poll(` call on the same line.

Widen the word extraction to include a leading `#`, so a private
field's token (`#await`) never equals the bare keyword `await`.

Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Priya RamanandPaperclip committed 2026-09-04 02:16:24 +00:00
1 parent fdc16817d7
commit df0bea2da7
2 files changed
+23 -1

No files matched your search

@@ -228,6 +228,18 @@ test("rejects a vi.waitFor( that follows division after an await-named property,
]);
});
test("rejects a vi.waitFor( that follows division after a private #await field, inside a template literal interpolation", () => {
const source = [
"async function example() {",
" const message = `status: ${this.#await / divisor + (await vi.waitFor(() => expect(x).toBe(1)))}`;",
"}",
].join("\n");
assert.deepEqual(findBareRealProcessWaits(source), [
{ line: 2, pattern: "vi.waitFor(" },
]);
});
test("still flags a real bare vi.waitFor( on the line after a comment mentioning the pattern", () => {
const source = [
"async function example() {",
@@ -76,6 +76,14 @@ function isKeywordShapedPropertyName(source, wordStartIndex) {
return beforeWordIndex >= 0 && source[beforeWordIndex] === ".";
}
// A private-field access such as `this.#await` reads a `#` right before the
// word. The `#` is part of the field's own name, not the `await` keyword, so
// the token this check extracts is `#await` — a word that can never equal
// the bare keyword `await` no matter what sits before the `#`. This keeps
// the standalone-keyword check in `isRegexLiteralStart` correct without a
// separate lookup for the `.` that a private-field access also requires.
const WORD_WITH_OPTIONAL_PRIVATE_FIELD_HASH = /#?[A-Za-z_$][\w$]*$/;
function isRegexLiteralStart(source, index) {
let previousIndex = index - 1;
while (previousIndex >= 0 && /\s/.test(source[previousIndex])) {
@@ -84,7 +92,9 @@ function isRegexLiteralStart(source, index) {
if (previousIndex < 0) return true;
const previousCharacter = source[previousIndex];
if (!/[\w$)\]]/.test(previousCharacter)) return true;
const wordMatch = source.slice(0, previousIndex + 1).match(/[A-Za-z_$][\w$]*$/);
const wordMatch = source
.slice(0, previousIndex + 1)
.match(WORD_WITH_OPTIONAL_PRIVATE_FIELD_HASH);
const word = wordMatch?.[0] ?? "";
if (!REGEX_PRECEDING_KEYWORDS.has(word)) return false;
const wordStartIndex = previousIndex + 1 - word.length;