@vitest/browser 4.1.9 → 4.1.10

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.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ResolvedConfig, BrowserServerFactory, BrowserCommand, BrowserProviderOption } from 'vitest/node';
1
+ import { TestProject, ResolvedConfig, BrowserServerFactory, BrowserCommand, BrowserProviderOption } from 'vitest/node';
2
2
  import { ScreenshotMatcherOptions, NonStandardScreenshotComparators, ScreenshotComparatorRegistry } from '@vitest/browser/context';
3
3
 
4
4
  interface BaseMetadata {
@@ -61,6 +61,8 @@ declare function parseKeyDef(text: string): {
61
61
  repeat: number;
62
62
  }[];
63
63
  declare function resolveScreenshotPath(testPath: string, name: string, config: ResolvedConfig, customPath: string | undefined): string;
64
+ declare function assertBrowserFileAccess(project: TestProject, path: string): void;
65
+ declare function assertBrowserApiWrite(project: TestProject, path: string): void;
64
66
 
65
67
  //#endregion
66
68
  //#region src/locatorGenerators.d.ts
@@ -89,5 +91,5 @@ declare function defineBrowserProvider<T extends object = object>(options: Omit<
89
91
  options?: T;
90
92
  }): BrowserProviderOption;
91
93
 
92
- export { asLocator, createBrowserServer, defineBrowserCommand, defineBrowserProvider, parseKeyDef, resolveScreenshotPath };
94
+ export { asLocator, assertBrowserApiWrite, assertBrowserFileAccess, createBrowserServer, defineBrowserCommand, defineBrowserProvider, parseKeyDef, resolveScreenshotPath };
93
95
  export type { CustomComparatorsRegistry };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ManualMockedModule, RedirectedModule, AutomockedModule, AutospiedModule, MockerRegistry } from '@vitest/mocker';
2
2
  import { dynamicImportPlugin, ServerMockResolver, interceptorPlugin } from '@vitest/mocker/node';
3
3
  import c from 'tinyrainbow';
4
- import { isValidApiRequest, isFileServingAllowed, distDir, rolldownVersion, resolveApiServerConfig, resolveFsAllow, isFileLoadingAllowed, createDebugger, createViteLogger, createViteServer } from 'vitest/node';
4
+ import { isFileLoadingAllowed, isValidApiRequest, isFileServingAllowed, distDir, rolldownVersion, resolveApiServerConfig, resolveFsAllow, createDebugger, createViteLogger, createViteServer } from 'vitest/node';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import fs, { readFileSync, createReadStream, promises, existsSync } from 'node:fs';
7
7
  import { createRequire } from 'node:module';
@@ -18,7 +18,7 @@ import { PNG } from 'pngjs';
18
18
  import { diff } from '@blazediff/core';
19
19
  import { WebSocketServer } from 'ws';
20
20
 
21
- var version = "4.1.9";
21
+ var version = "4.1.10";
22
22
 
23
23
  const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
24
24
  function normalizeWindowsPath(input = "") {
@@ -674,6 +674,17 @@ async function getBrowserProvider(options, project) {
674
674
  function slash(path) {
675
675
  return path.replace(/\\/g, "/").replace(/\/+/g, "/");
676
676
  }
677
+ function assertBrowserFileAccess(project, path) {
678
+ const normalized = slash(path);
679
+ if (!isFileLoadingAllowed(project.vite.config, normalized) && !isFileLoadingAllowed(project.vitest.vite.config, normalized)) {
680
+ throw new Error(`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`);
681
+ }
682
+ }
683
+ function assertBrowserApiWrite(project, path) {
684
+ if (!project.config.browser.api.allowWrite || !project.vitest.config.api.allowWrite) {
685
+ throw new Error(`Cannot modify file "${path}". File writing is disabled because the server is exposed to the internet, see https://vitest.dev/config/browser/api.`);
686
+ }
687
+ }
677
688
 
678
689
  async function resolveOrchestrator(globalServer, url, res) {
679
690
  let sessionId = url.searchParams.get("sessionId");
@@ -1896,19 +1907,9 @@ _Mime_extensionToType = new WeakMap(), _Mime_typeToExtension = new WeakMap(), _M
1896
1907
 
1897
1908
  var mime = new Mime(types)._freeze();
1898
1909
 
1899
- function assertFileAccess(path, project) {
1900
- if (!isFileLoadingAllowed(project.vite.config, path) && !isFileLoadingAllowed(project.vitest.vite.config, path)) {
1901
- throw new Error(`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`);
1902
- }
1903
- }
1904
- function assertWrite(path, project) {
1905
- if (!project.config.browser.api.allowWrite || !project.vitest.config.api.allowWrite) {
1906
- throw new Error(`Cannot modify file "${path}". File writing is disabled because server is exposed to the internet, see https://vitest.dev/config/browser/api.`);
1907
- }
1908
- }
1909
1910
  const readFile = async ({ project }, path, options = {}) => {
1910
1911
  const filepath = resolve$1(project.config.root, path);
1911
- assertFileAccess(slash(filepath), project);
1912
+ assertBrowserFileAccess(project, filepath);
1912
1913
  // never return a Buffer
1913
1914
  if (typeof options === "object" && !options.encoding) {
1914
1915
  options.encoding = "utf-8";
@@ -1916,9 +1917,9 @@ const readFile = async ({ project }, path, options = {}) => {
1916
1917
  return promises.readFile(filepath, options);
1917
1918
  };
1918
1919
  const writeFile = async ({ project }, path, data, options) => {
1919
- assertWrite(path, project);
1920
+ assertBrowserApiWrite(project, path);
1920
1921
  const filepath = resolve$1(project.config.root, path);
1921
- assertFileAccess(slash(filepath), project);
1922
+ assertBrowserFileAccess(project, filepath);
1922
1923
  const dir = dirname$1(filepath);
1923
1924
  if (!fs.existsSync(dir)) {
1924
1925
  await promises.mkdir(dir, { recursive: true });
@@ -1926,14 +1927,14 @@ const writeFile = async ({ project }, path, data, options) => {
1926
1927
  await promises.writeFile(filepath, data, options);
1927
1928
  };
1928
1929
  const removeFile = async ({ project }, path) => {
1929
- assertWrite(path, project);
1930
+ assertBrowserApiWrite(project, path);
1930
1931
  const filepath = resolve$1(project.config.root, path);
1931
- assertFileAccess(slash(filepath), project);
1932
+ assertBrowserFileAccess(project, filepath);
1932
1933
  await promises.rm(filepath);
1933
1934
  };
1934
1935
  const _fileInfo = async ({ project }, path, encoding) => {
1935
1936
  const filepath = resolve$1(project.config.root, path);
1936
- assertFileAccess(slash(filepath), project);
1937
+ assertBrowserFileAccess(project, filepath);
1937
1938
  const content = await promises.readFile(filepath, encoding || "base64");
1938
1939
  return {
1939
1940
  content,
@@ -2249,7 +2250,7 @@ const screenshotMatcher = async (context, name, testName, options) => {
2249
2250
  comparator,
2250
2251
  comparatorOptions
2251
2252
  });
2252
- await performSideEffects(outcome, codec);
2253
+ await performSideEffects(outcome, codec, context.project);
2253
2254
  return buildOutput(outcome, timeout);
2254
2255
  };
2255
2256
  /**
@@ -2335,17 +2336,17 @@ async function determineOutcome({ comparator, comparatorOptions, paths, referenc
2335
2336
  *
2336
2337
  * Only `missing-reference`, `update-reference`, and `mismatch` write files. Successful matches produce no side effects.
2337
2338
  */
2338
- async function performSideEffects(outcome, codec) {
2339
+ async function performSideEffects(outcome, codec, project) {
2339
2340
  switch (outcome.type) {
2340
2341
  case "missing-reference":
2341
2342
  case "update-reference": {
2342
- await writeScreenshot(outcome.reference.path, await codec.encode(outcome.reference.image, {}));
2343
+ await writeScreenshot(outcome.reference.path, await codec.encode(outcome.reference.image, {}), project);
2343
2344
  break;
2344
2345
  }
2345
2346
  case "mismatch": {
2346
- await writeScreenshot(outcome.actual.path, await codec.encode(outcome.actual.image, {}));
2347
+ await writeScreenshot(outcome.actual.path, await codec.encode(outcome.actual.image, {}), project);
2347
2348
  if (outcome.diff) {
2348
- await writeScreenshot(outcome.diff.path, await codec.encode(outcome.diff.image, {}));
2349
+ await writeScreenshot(outcome.diff.path, await codec.encode(outcome.diff.image, {}), project);
2349
2350
  }
2350
2351
  break;
2351
2352
  }
@@ -2479,8 +2480,10 @@ async function getStableScreenshot({ codec, context, comparator, comparatorOptio
2479
2480
  };
2480
2481
  }
2481
2482
  /** Writes encoded images to disk, creating parent directories as needed. */
2482
- async function writeScreenshot(path, image) {
2483
+ async function writeScreenshot(path, image, project) {
2483
2484
  try {
2485
+ assertBrowserApiWrite(project, path);
2486
+ assertBrowserFileAccess(project, path);
2484
2487
  await mkdir(dirname(path), { recursive: true });
2485
2488
  await writeFile$1(path, image);
2486
2489
  } catch (cause) {
@@ -3114,6 +3117,16 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
3114
3117
  artifact.attachments = [];
3115
3118
  vitest.logger.error(`[vitest] Cannot record attachments ("${attachments}") because file writing is disabled, removing attachments from artifact "${artifact.type}". See https://vitest.dev/config/browser/api.`);
3116
3119
  }
3120
+ } else {
3121
+ // attachment files are copied into `attachmentsDir`, so confine
3122
+ // client-supplied paths to Vite's `server.fs` boundary
3123
+ const attachments = artifact.type === "internal:annotation" ? artifact.annotation.attachment ? [artifact.annotation.attachment] : [] : artifact.attachments ?? [];
3124
+ for (const attachment of attachments) {
3125
+ const path = attachment.path;
3126
+ if (path && !path.startsWith("http://") && !path.startsWith("https://")) {
3127
+ checkFileAccess(resolve(project.config.root, path));
3128
+ }
3129
+ }
3117
3130
  }
3118
3131
  return vitest._testRun.recordArtifact(id, artifact);
3119
3132
  },
@@ -7950,4 +7963,4 @@ function defineBrowserProvider(options) {
7950
7963
  };
7951
7964
  }
7952
7965
 
7953
- export { asLocator, createBrowserServer, defineBrowserCommand, defineBrowserProvider, parseKeyDef, resolveScreenshotPath };
7966
+ export { asLocator, assertBrowserApiWrite, assertBrowserFileAccess, createBrowserServer, defineBrowserCommand, defineBrowserProvider, parseKeyDef, resolveScreenshotPath };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/browser",
3
3
  "type": "module",
4
- "version": "4.1.9",
4
+ "version": "4.1.10",
5
5
  "description": "Browser running for Vitest",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -57,7 +57,7 @@
57
57
  "providers"
58
58
  ],
59
59
  "peerDependencies": {
60
- "vitest": "4.1.9"
60
+ "vitest": "4.1.10"
61
61
  },
62
62
  "dependencies": {
63
63
  "@blazediff/core": "1.9.1",
@@ -66,8 +66,8 @@
66
66
  "sirv": "^3.0.2",
67
67
  "tinyrainbow": "^3.1.0",
68
68
  "ws": "^8.19.0",
69
- "@vitest/utils": "4.1.9",
70
- "@vitest/mocker": "4.1.9"
69
+ "@vitest/mocker": "4.1.10",
70
+ "@vitest/utils": "4.1.10"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@opentelemetry/api": "^1.9.0",
@@ -79,8 +79,8 @@
79
79
  "ivya": "^1.8.0",
80
80
  "mime": "^4.1.0",
81
81
  "pathe": "^2.0.3",
82
- "vitest": "4.1.9",
83
- "@vitest/runner": "4.1.9"
82
+ "@vitest/runner": "4.1.10",
83
+ "vitest": "4.1.10"
84
84
  },
85
85
  "scripts": {
86
86
  "typecheck": "tsc -p ./src/client/tsconfig.json --noEmit",