vitest 0.8.3 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,21 +21,19 @@ var __spreadValues = (a, b) => {
21
21
  }
22
22
  return a;
23
23
  };
24
- function getObjectType(value) {
24
+ function getType(value) {
25
25
  return Object.prototype.toString.apply(value).slice(8, -1);
26
26
  }
27
- function mockPrototype(spyOn, proto) {
28
- if (!proto)
29
- return null;
30
- const newProto = {};
31
- const protoDescr = Object.getOwnPropertyDescriptors(proto);
32
- for (const d in protoDescr) {
33
- Object.defineProperty(newProto, d, protoDescr[d]);
34
- if (typeof protoDescr[d].value === "function")
35
- spyOn(newProto, d).mockImplementation(() => {
36
- });
37
- }
38
- return newProto;
27
+ function getAllProperties(obj) {
28
+ const allProps = /* @__PURE__ */ new Set();
29
+ let curr = obj;
30
+ do {
31
+ if (curr === Object.prototype)
32
+ break;
33
+ const props = Object.getOwnPropertyNames(curr);
34
+ props.forEach((prop) => allProps.add(prop));
35
+ } while (curr = Object.getPrototypeOf(curr));
36
+ return Array.from(allProps);
39
37
  }
40
38
  const _VitestMocker = class {
41
39
  constructor(options, moduleCache, request) {
@@ -127,26 +125,26 @@ const _VitestMocker = class {
127
125
  const fullPath = resolve(dir, "__mocks__", baseId);
128
126
  return existsSync(fullPath) ? fullPath.replace(this.root, "") : null;
129
127
  }
130
- mockObject(obj) {
131
- if (!_VitestMocker.spyModule)
132
- throw new Error("Internal Vitest error: Spy function is not defined.");
133
- const type = getObjectType(obj);
134
- if (Array.isArray(obj))
128
+ mockValue(value) {
129
+ if (!_VitestMocker.spyModule) {
130
+ throw new Error("Error: Spy module is not defined. This is likely an internal bug in Vitest. Please report it to https://github.com/vitest-dev/vitest/issues");
131
+ }
132
+ const type = getType(value);
133
+ if (Array.isArray(value))
135
134
  return [];
136
135
  else if (type !== "Object" && type !== "Module")
137
- return obj;
138
- const newObj = __spreadValues({}, obj);
139
- const proto = mockPrototype(_VitestMocker.spyModule.spyOn, Object.getPrototypeOf(obj));
140
- Object.setPrototypeOf(newObj, proto);
141
- for (const k in obj) {
142
- newObj[k] = this.mockObject(obj[k]);
143
- const type2 = getObjectType(obj[k]);
144
- if (type2.includes("Function") && !obj[k]._isMockFunction) {
145
- _VitestMocker.spyModule.spyOn(newObj, k).mockImplementation(() => {
146
- });
136
+ return value;
137
+ const newObj = {};
138
+ const proproperties = getAllProperties(value);
139
+ for (const k of proproperties) {
140
+ newObj[k] = this.mockValue(value[k]);
141
+ const type2 = getType(value[k]);
142
+ if (type2.includes("Function") && !value[k]._isMockFunction) {
143
+ _VitestMocker.spyModule.spyOn(newObj, k).mockImplementation(() => void 0);
147
144
  Object.defineProperty(newObj[k], "length", { value: 0 });
148
145
  }
149
146
  }
147
+ Object.setPrototypeOf(newObj, Object.getPrototypeOf(value));
150
148
  return newObj;
151
149
  }
152
150
  unmockPath(path) {
@@ -178,7 +176,7 @@ const _VitestMocker = class {
178
176
  await this.ensureSpy();
179
177
  const fsPath = this.getFsPath(path, external);
180
178
  const mod = await this.request(fsPath);
181
- return this.mockObject(mod);
179
+ return this.mockValue(mod);
182
180
  }
183
181
  if (typeof mock === "function")
184
182
  return this.callFunctionMock(path, mock);
@@ -201,7 +199,7 @@ const _VitestMocker = class {
201
199
  return cache.exports;
202
200
  const cacheKey = toFilePath(dep, this.root);
203
201
  const mod = ((_a = this.moduleCache.get(cacheKey)) == null ? void 0 : _a.exports) || await this.request(dep);
204
- const exports = this.mockObject(mod);
202
+ const exports = this.mockValue(mod);
205
203
  this.emit("mocked", cacheName, { exports });
206
204
  return exports;
207
205
  }
@@ -24,7 +24,7 @@ import MagicString from './chunk-magic-string.d5e0e473.js';
24
24
  import require$$0$3 from 'readline';
25
25
  import { p as prompts } from './vendor-index.ee829ed6.js';
26
26
 
27
- var version = "0.8.3";
27
+ var version = "0.8.4";
28
28
 
29
29
  function stripFinalNewline(input) {
30
30
  const LF = typeof input === 'string' ? '\n' : '\n'.charCodeAt();
@@ -11753,6 +11753,17 @@ class DefaultReporter extends BaseReporter {
11753
11753
  super(...arguments);
11754
11754
  this.rendererOptions = {};
11755
11755
  }
11756
+ async onTestRemoved(trigger) {
11757
+ await this.stopListRender();
11758
+ this.ctx.console.clear();
11759
+ this.ctx.log(c.yellow("Test removed...") + (trigger ? c.dim(` [ ${this.relative(trigger)} ]
11760
+ `) : ""));
11761
+ const files = this.ctx.state.getFiles(this.watchFilters);
11762
+ createListRenderer(files, this.rendererOptions).stop();
11763
+ this.ctx.log();
11764
+ await super.reportSummary(files);
11765
+ super.onWatcherStart();
11766
+ }
11756
11767
  onCollected() {
11757
11768
  if (this.isTTY) {
11758
11769
  this.rendererOptions.outputStream = this.ctx.outputStream;
@@ -12570,6 +12581,8 @@ function resolveConfig(options, viteConfig) {
12570
12581
  ])).filter(Boolean);
12571
12582
  if (!resolved.reporters.length)
12572
12583
  resolved.reporters.push("default");
12584
+ if (resolved.changed)
12585
+ resolved.passWithNoTests ?? (resolved.passWithNoTests = true);
12573
12586
  return resolved;
12574
12587
  }
12575
12588
 
@@ -12691,6 +12704,65 @@ function generateCodeFrame(source, indent = 0, start = 0, end, range = 2) {
12691
12704
  return res.join("\n");
12692
12705
  }
12693
12706
 
12707
+ class VitestGit {
12708
+ constructor(cwd) {
12709
+ this.cwd = cwd;
12710
+ }
12711
+ async resolveFilesWithGitCommand(args) {
12712
+ let result;
12713
+ try {
12714
+ result = await execa("git", args, { cwd: this.root });
12715
+ } catch (e) {
12716
+ e.message = e.stderr;
12717
+ throw e;
12718
+ }
12719
+ return result.stdout.split("\n").filter((s) => s !== "").map((changedPath) => resolve(this.root, changedPath));
12720
+ }
12721
+ async findChangedFiles(options) {
12722
+ const root = await this.getRoot(this.cwd);
12723
+ if (!root)
12724
+ return null;
12725
+ this.root = root;
12726
+ const changedSince = options.changedSince;
12727
+ if (typeof changedSince === "string") {
12728
+ const [committed, staged2, unstaged2] = await Promise.all([
12729
+ this.getFilesSince(changedSince),
12730
+ this.getStagedFiles(),
12731
+ this.getUnstagedFiles()
12732
+ ]);
12733
+ return [...committed, ...staged2, ...unstaged2];
12734
+ }
12735
+ const [staged, unstaged] = await Promise.all([
12736
+ this.getStagedFiles(),
12737
+ this.getUnstagedFiles()
12738
+ ]);
12739
+ return [...staged, ...unstaged];
12740
+ }
12741
+ getFilesSince(hash) {
12742
+ return this.resolveFilesWithGitCommand(["diff", "--name-only", `${hash}...HEAD`]);
12743
+ }
12744
+ getStagedFiles() {
12745
+ return this.resolveFilesWithGitCommand(["diff", "--cached", "--name-only"]);
12746
+ }
12747
+ getUnstagedFiles() {
12748
+ return this.resolveFilesWithGitCommand([
12749
+ "ls-files",
12750
+ "--other",
12751
+ "--modified",
12752
+ "--exclude-standard"
12753
+ ]);
12754
+ }
12755
+ async getRoot(cwd) {
12756
+ const options = ["rev-parse", "--show-cdup"];
12757
+ try {
12758
+ const result = await execa("git", options, { cwd });
12759
+ return resolve(cwd, result.stdout);
12760
+ } catch {
12761
+ return null;
12762
+ }
12763
+ }
12764
+ }
12765
+
12694
12766
  const WATCHER_DEBOUNCE = 100;
12695
12767
  const CLOSE_TIMEOUT = 1e3;
12696
12768
  class Vitest {
@@ -12799,6 +12871,17 @@ Run with \`--passWithNoTests\`to exit with code 0
12799
12871
  return deps;
12800
12872
  }
12801
12873
  async filterTestsBySource(tests) {
12874
+ if (this.config.changed && !this.config.related) {
12875
+ const vitestGit = new VitestGit(this.config.root);
12876
+ const related2 = await vitestGit.findChangedFiles({
12877
+ changedSince: this.config.changed
12878
+ });
12879
+ if (!related2) {
12880
+ this.error(c.red("Could not find Git root. Have you initialized git with `git init`?\n"));
12881
+ process.exit(1);
12882
+ }
12883
+ this.config.related = Array.from(new Set(related2));
12884
+ }
12802
12885
  const related = this.config.related;
12803
12886
  if (!related)
12804
12887
  return tests;
@@ -12903,6 +12986,7 @@ Run with \`--passWithNoTests\`to exit with code 0
12903
12986
  if (this.state.filesMap.has(id)) {
12904
12987
  this.state.filesMap.delete(id);
12905
12988
  this.changedTests.delete(id);
12989
+ this.report("onTestRemoved", id);
12906
12990
  }
12907
12991
  };
12908
12992
  const onAdd = async (id) => {
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { EventEmitter } from 'events';
2
2
  import { d as c } from './chunk-utils-base.8408f73a.js';
3
- import { v as version, s as startVitest, d as divider } from './chunk-vite-node-externalize.ecc58a1f.js';
3
+ import { v as version, s as startVitest, d as divider } from './chunk-vite-node-externalize.8c747551.js';
4
4
  import 'path';
5
5
  import 'tty';
6
6
  import 'local-pkg';
@@ -643,7 +643,7 @@ class CAC extends EventEmitter {
643
643
  const cac = (name = "") => new CAC(name);
644
644
 
645
645
  const cli = cac("vitest");
646
- cli.version(version).option("-r, --root <path>", "root path").option("-c, --config <path>", "path to config file").option("-u, --update", "update snapshot").option("-w, --watch", "watch mode").option("-t, --testNamePattern <pattern>", "run tests with full names matching the specified pattern").option("--dir <path>", "base directory to scan for the test files").option("--ui", "enable UI").option("--open", "open UI automatically (default: !process.env.CI))").option("--api [api]", "serve API, available options: --api.port <port>, --api.host [host] and --api.strictPort").option("--threads", "enabled threads (default: true)").option("--silent", "silent console output from tests").option("--isolate", "isolate environment for each test file (default: true)").option("--reporter <name>", "reporter").option("--outputTruncateLength <length>", "diff output length").option("--outputFile <filename>", "write test results to a file when the --reporter=json or --reporter=junit option is also specified").option("--coverage", "use c8 for coverage").option("--run", "do not watch").option("--mode <name>", "override Vite mode (default: test)").option("--globals", "inject apis globally").option("--global", "deprecated, use --globals").option("--dom", "mock browser api with happy-dom").option("--environment <env>", "runner environment (default: node)").option("--passWithNoTests", "pass when no tests found").option("--allowOnly", "Allow tests and suites that are marked as only (default: !process.env.CI)").help();
646
+ cli.version(version).option("-r, --root <path>", "root path").option("-c, --config <path>", "path to config file").option("-u, --update", "update snapshot").option("-w, --watch", "watch mode").option("-t, --testNamePattern <pattern>", "run tests with full names matching the specified pattern").option("--dir <path>", "base directory to scan for the test files").option("--ui", "enable UI").option("--open", "open UI automatically (default: !process.env.CI))").option("--api [api]", "serve API, available options: --api.port <port>, --api.host [host] and --api.strictPort").option("--threads", "enabled threads (default: true)").option("--silent", "silent console output from tests").option("--isolate", "isolate environment for each test file (default: true)").option("--reporter <name>", "reporter").option("--outputTruncateLength <length>", "diff output length").option("--outputFile <filename>", "write test results to a file when the --reporter=json or --reporter=junit option is also specified").option("--coverage", "use c8 for coverage").option("--run", "do not watch").option("--mode <name>", "override Vite mode (default: test)").option("--globals", "inject apis globally").option("--global", "deprecated, use --globals").option("--dom", "mock browser api with happy-dom").option("--environment <env>", "runner environment (default: node)").option("--passWithNoTests", "pass when no tests found").option("--allowOnly", "Allow tests and suites that are marked as only (default: !process.env.CI)").option("--changed [since]", "Run tests that are affected by the changed files (default: false)").help();
647
647
  cli.command("run [...filters]").action(run);
648
648
  cli.command("related [...filters]").action(runRelated);
649
649
  cli.command("watch [...filters]").action(start);
package/dist/index.d.ts CHANGED
@@ -397,6 +397,7 @@ declare const createListRenderer: (_tasks: Task[], options: ListRendererOptions)
397
397
  declare class DefaultReporter extends BaseReporter {
398
398
  renderer?: ReturnType<typeof createListRenderer>;
399
399
  rendererOptions: ListRendererOptions;
400
+ onTestRemoved(trigger?: string): Promise<void>;
400
401
  onCollected(): void;
401
402
  onFinished(files?: File[]): Promise<void>;
402
403
  onWatcherStart(): Promise<void>;
@@ -774,6 +775,7 @@ interface Reporter {
774
775
  onCollected?: (files?: File[]) => Awaitable<void>;
775
776
  onFinished?: (files?: File[]) => Awaitable<void>;
776
777
  onTaskUpdate?: (packs: TaskResultPack[]) => Awaitable<void>;
778
+ onTestRemoved?: (trigger?: string) => Awaitable<void>;
777
779
  onWatcherStart?: () => Awaitable<void>;
778
780
  onWatcherRerun?: (files: string[], trigger?: string) => Awaitable<void>;
779
781
  onServerRestart?: () => Awaitable<void>;
@@ -1102,6 +1104,12 @@ interface UserConfig extends InlineConfig {
1102
1104
  * @default 'test'
1103
1105
  */
1104
1106
  mode?: string;
1107
+ /**
1108
+ * Runs tests that are affected by the changes in the repository, or between specified branch or commit hash
1109
+ * Requires initialized git repository
1110
+ * @default false
1111
+ */
1112
+ changed?: boolean | string;
1105
1113
  }
1106
1114
  interface ResolvedConfig extends Omit<Required<UserConfig>, 'config' | 'filters' | 'coverage' | 'testNamePattern' | 'related' | 'api' | 'reporters'> {
1107
1115
  base?: string;
package/dist/node.d.ts CHANGED
@@ -245,6 +245,7 @@ declare const createListRenderer: (_tasks: Task[], options: ListRendererOptions)
245
245
  declare class DefaultReporter extends BaseReporter {
246
246
  renderer?: ReturnType<typeof createListRenderer>;
247
247
  rendererOptions: ListRendererOptions;
248
+ onTestRemoved(trigger?: string): Promise<void>;
248
249
  onCollected(): void;
249
250
  onFinished(files?: File[]): Promise<void>;
250
251
  onWatcherStart(): Promise<void>;
@@ -538,6 +539,7 @@ interface Reporter {
538
539
  onCollected?: (files?: File[]) => Awaitable<void>;
539
540
  onFinished?: (files?: File[]) => Awaitable<void>;
540
541
  onTaskUpdate?: (packs: TaskResultPack[]) => Awaitable<void>;
542
+ onTestRemoved?: (trigger?: string) => Awaitable<void>;
541
543
  onWatcherStart?: () => Awaitable<void>;
542
544
  onWatcherRerun?: (files: string[], trigger?: string) => Awaitable<void>;
543
545
  onServerRestart?: () => Awaitable<void>;
@@ -857,6 +859,12 @@ interface UserConfig extends InlineConfig {
857
859
  * @default 'test'
858
860
  */
859
861
  mode?: string;
862
+ /**
863
+ * Runs tests that are affected by the changes in the repository, or between specified branch or commit hash
864
+ * Requires initialized git repository
865
+ * @default false
866
+ */
867
+ changed?: boolean | string;
860
868
  }
861
869
  interface ResolvedConfig extends Omit<Required<UserConfig>, 'config' | 'filters' | 'coverage' | 'testNamePattern' | 'related' | 'api' | 'reporters'> {
862
870
  base?: string;
@@ -992,7 +1000,7 @@ declare class VitestMocker {
992
1000
  normalizePath(path: string): string;
993
1001
  getFsPath(path: string, external: string | null): string;
994
1002
  resolveMockPath(mockPath: string, external: string | null): string | null;
995
- mockObject(obj: any): any;
1003
+ mockValue(value: any): any;
996
1004
  unmockPath(path: string): void;
997
1005
  mockPath(path: string, external: string | null, factory?: () => any): void;
998
1006
  importActual<T>(id: string, importer: string): Promise<T>;
package/dist/node.js CHANGED
@@ -1,5 +1,5 @@
1
- export { V as VitestPlugin, c as createVitest, s as startVitest } from './chunk-vite-node-externalize.ecc58a1f.js';
2
- export { V as VitestRunner } from './chunk-runtime-mocker.def94aba.js';
1
+ export { V as VitestPlugin, c as createVitest, s as startVitest } from './chunk-vite-node-externalize.8c747551.js';
2
+ export { V as VitestRunner } from './chunk-runtime-mocker.d320e5e9.js';
3
3
  import 'buffer';
4
4
  import 'path';
5
5
  import 'child_process';
package/dist/worker.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { u as resolve } from './chunk-utils-base.8408f73a.js';
2
2
  import { c as createBirpc, M as ModuleCacheMap } from './chunk-vite-node-utils.3c7ce184.js';
3
3
  import { d as distDir } from './chunk-constants.6062c404.js';
4
- import { e as executeInViteNode } from './chunk-runtime-mocker.def94aba.js';
4
+ import { e as executeInViteNode } from './chunk-runtime-mocker.d320e5e9.js';
5
5
  import { r as rpc } from './chunk-runtime-rpc.e8aa1ebe.js';
6
6
  import { g as getWorkerState } from './chunk-utils-global.7bcfa03c.js';
7
7
  import 'path';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitest",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "description": "A blazing fast unit test framework powered by Vite",
5
5
  "keywords": [
6
6
  "vite",
@@ -93,7 +93,7 @@
93
93
  "@types/node": "^17.0.23",
94
94
  "@types/prompts": "^2.4.0",
95
95
  "@types/sinonjs__fake-timers": "^8.1.2",
96
- "@vitest/ui": "0.8.3",
96
+ "@vitest/ui": "0.8.4",
97
97
  "birpc": "^0.2.2",
98
98
  "c8": "^7.11.0",
99
99
  "cac": "^6.7.12",
@@ -120,7 +120,7 @@
120
120
  "source-map-js": "^1.0.2",
121
121
  "strip-ansi": "^7.0.1",
122
122
  "typescript": "^4.6.3",
123
- "vite-node": "0.8.3",
123
+ "vite-node": "0.8.4",
124
124
  "ws": "^8.5.0"
125
125
  },
126
126
  "engines": {