Reproduces a bug in Vitest 4.1.6 where describe.skipIf(condition).for(cases) runs
the parametrized suites even when condition is true. Sibling chains
describe.skipIf(condition).each(cases) and plain describe.skipIf(condition)
both behave correctly.
npm install
npm testAll three suites are skipped — no failing tests.
The .for variant runs and fails:
× describe.skipIf(true).for — case 1 > should be skipped but runs
× describe.skipIf(true).for — case 2 > should be skipped but runs
× describe.skipIf(true).for — case 3 > should be skipped but runs
↓ describe.skipIf(true).each — case 1 > correctly skipped
↓ describe.skipIf(true).each — case 2 > correctly skipped
↓ describe.skipIf(true).each — case 3 > correctly skipped
↓ describe.skipIf(true) — plain > correctly skipped
In @vitest/runner createSuite(), .for calls the module-level closure-bound
suite(...) instead of resolving it from this, so the chained { skip: true }
context (set by .skipIf) is discarded:
// node_modules/.../@vitest/runner/dist/chunk-artifact.js — createSuite()
suiteFn.for = function(cases, ...args) {
// ...
return (name, optionsOrFn, fnOrOptions) => {
// ...
cases.forEach((item, idx) => {
suite(formatTitle(name_, toArray(item), idx), options, ...);
// ↑ closure-bound, ignores `this` / chained context
});
};
};
suiteFn.skipIf = (condition) => condition ? suite.skip : suite;Compare to suiteFn.each, which does it correctly:
suiteFn.each = function(cases, ...args) {
const context = getChainableContext(this);
const suite = context.withContext(); // ← chained suite
// ...
suite(formatTitle(...), ...); // ← respects skip/only/etc.
};A minimal patch would be to mirror .each’s pattern in .for:
suiteFn.for = function(cases, ...args) {
+ const context = getChainableContext(this);
+ const suite = context.withContext();
if (Array.isArray(cases) && args.length) {
cases = formatTemplateString(cases, args);
}
return (name, optionsOrFn, fnOrOptions) => {
const name_ = formatName(name);
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
cases.forEach((item, idx) => {
suite(formatTitle(name_, toArray(item), idx), options, handler ? () => handler(item) : undefined);
});
};
};The same bug shape likely affects describe.only.for, describe.skip.for,
describe.concurrent.for, describe.sequential.for, describe.shuffle.for,
describe.todo.for, and describe.runIf(...).for — none of those chained modes
will reach the inner .for either.