@vitest/browser 4.1.0-beta.2 → 4.1.0-beta.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.
package/dist/index.js CHANGED
@@ -1,14 +1,13 @@
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, resolveApiServerConfig, resolveFsAllow, rolldownVersion, createDebugger, createViteLogger, createViteServer } from 'vitest/node';
4
+ import { isValidApiRequest, isFileServingAllowed, distDir, resolveApiServerConfig, resolveFsAllow, rolldownVersion, isFileLoadingAllowed, createDebugger, createViteLogger, createViteServer } from 'vitest/node';
5
5
  import { fileURLToPath } from 'node:url';
6
- import fs, { readFileSync, lstatSync, createReadStream, promises, existsSync } from 'node:fs';
6
+ import fs, { readFileSync, createReadStream, promises, existsSync } from 'node:fs';
7
7
  import { createRequire } from 'node:module';
8
8
  import { slash as slash$1, toArray, deepMerge } from '@vitest/utils/helpers';
9
9
  import MagicString from 'magic-string';
10
10
  import sirv from 'sirv';
11
- import { coverageConfigDefaults } from 'vitest/config';
12
11
  import crypto from 'node:crypto';
13
12
  import { readFile as readFile$1, mkdir, writeFile as writeFile$1 } from 'node:fs/promises';
14
13
  import { parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
@@ -18,7 +17,7 @@ import { PNG } from 'pngjs';
18
17
  import pm from 'pixelmatch';
19
18
  import { WebSocketServer } from 'ws';
20
19
 
21
- var version = "4.1.0-beta.2";
20
+ var version = "4.1.0-beta.4";
22
21
 
23
22
  const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
24
23
  function normalizeWindowsPath(input = "") {
@@ -942,13 +941,10 @@ var BrowserPlugin = (parentServer, base = "/") => {
942
941
  res.write(content, "utf-8");
943
942
  res.end();
944
943
  });
945
- const coverageFolder = resolveCoverageFolder(parentServer.vitest);
946
- const coveragePath = coverageFolder ? coverageFolder[1] : undefined;
947
- if (coveragePath && base === coveragePath) {
948
- throw new Error(`The ui base path and the coverage path cannot be the same: ${base}, change coverage.reportsDirectory`);
949
- }
950
- if (coverageFolder) {
951
- server.middlewares.use(coveragePath, sirv(coverageFolder[0], {
944
+ // Serve coverage HTML at ./coverage if configured
945
+ const coverageHtmlDir = parentServer.vitest.config.coverage?.htmlDir;
946
+ if (coverageHtmlDir) {
947
+ server.middlewares.use("/__vitest_test__/coverage", sirv(coverageHtmlDir, {
952
948
  single: true,
953
949
  dev: true,
954
950
  setHeaders: (res) => {
@@ -962,46 +958,6 @@ var BrowserPlugin = (parentServer, base = "/") => {
962
958
  }
963
959
  }));
964
960
  }
965
- const uiEnabled = parentServer.config.browser.ui;
966
- if (uiEnabled) {
967
- // eslint-disable-next-line prefer-arrow-callback
968
- server.middlewares.use(`${base}__screenshot-error`, function vitestBrowserScreenshotError(req, res) {
969
- if (!req.url) {
970
- res.statusCode = 404;
971
- res.end();
972
- return;
973
- }
974
- const url = new URL(req.url, "http://localhost");
975
- const id = url.searchParams.get("id");
976
- if (!id) {
977
- res.statusCode = 404;
978
- res.end();
979
- return;
980
- }
981
- const task = parentServer.vitest.state.idMap.get(id);
982
- const file = task?.meta.failScreenshotPath;
983
- if (!file) {
984
- res.statusCode = 404;
985
- res.end();
986
- return;
987
- }
988
- let stat;
989
- try {
990
- stat = lstatSync(file);
991
- } catch {}
992
- if (!stat?.isFile()) {
993
- res.statusCode = 404;
994
- res.end();
995
- return;
996
- }
997
- const ext = extname(file);
998
- const buffer = readFileSync(file);
999
- res.setHeader("Cache-Control", "public,max-age=0,must-revalidate");
1000
- res.setHeader("Content-Length", buffer.length);
1001
- res.setHeader("Content-Type", ext === "jpeg" || ext === "jpg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png");
1002
- res.end(buffer);
1003
- });
1004
- }
1005
961
  server.middlewares.use((req, res, next) => {
1006
962
  // 9000 mega head move
1007
963
  // Vite always caches optimized dependencies, but users might mock
@@ -1412,26 +1368,6 @@ function getRequire() {
1412
1368
  }
1413
1369
  return _require;
1414
1370
  }
1415
- function resolveCoverageFolder(vitest) {
1416
- const options = vitest.config;
1417
- const coverageOptions = vitest._coverageOptions;
1418
- const htmlReporter = coverageOptions?.enabled ? toArray(options.coverage.reporter).find((reporter) => {
1419
- if (typeof reporter === "string") {
1420
- return reporter === "html";
1421
- }
1422
- return reporter[0] === "html";
1423
- }) : undefined;
1424
- if (!htmlReporter) {
1425
- return undefined;
1426
- }
1427
- // reportsDirectory not resolved yet
1428
- const root = resolve(options.root || process.cwd(), coverageOptions.reportsDirectory || coverageConfigDefaults.reportsDirectory);
1429
- const subdir = Array.isArray(htmlReporter) && htmlReporter.length > 1 && "subdir" in htmlReporter[1] ? htmlReporter[1].subdir : undefined;
1430
- if (!subdir || typeof subdir !== "string") {
1431
- return [root, `/${basename(root)}/`];
1432
- }
1433
- return [resolve(root, subdir), `/${basename(root)}/${subdir}/`];
1434
- }
1435
1371
  const postfixRE = /[?#].*$/;
1436
1372
  function cleanUrl(url) {
1437
1373
  return url.replace(postfixRE, "");
@@ -1930,13 +1866,18 @@ _Mime_extensionToType = new WeakMap(), _Mime_typeToExtension = new WeakMap(), _M
1930
1866
  var mime = new Mime(types)._freeze();
1931
1867
 
1932
1868
  function assertFileAccess(path, project) {
1933
- if (!isFileServingAllowed(path, project.vite) && !isFileServingAllowed(path, project.vitest.vite)) {
1869
+ if (!isFileLoadingAllowed(project.vite.config, path) && !isFileLoadingAllowed(project.vitest.vite.config, path)) {
1934
1870
  throw new Error(`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`);
1935
1871
  }
1936
1872
  }
1873
+ function assertWrite(path, project) {
1874
+ if (!project.config.browser.api.allowWrite || !project.vitest.config.api.allowWrite) {
1875
+ 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.`);
1876
+ }
1877
+ }
1937
1878
  const readFile = async ({ project }, path, options = {}) => {
1938
1879
  const filepath = resolve$1(project.config.root, path);
1939
- assertFileAccess(filepath, project);
1880
+ assertFileAccess(slash(filepath), project);
1940
1881
  // never return a Buffer
1941
1882
  if (typeof options === "object" && !options.encoding) {
1942
1883
  options.encoding = "utf-8";
@@ -1944,8 +1885,9 @@ const readFile = async ({ project }, path, options = {}) => {
1944
1885
  return promises.readFile(filepath, options);
1945
1886
  };
1946
1887
  const writeFile = async ({ project }, path, data, options) => {
1888
+ assertWrite(path, project);
1947
1889
  const filepath = resolve$1(project.config.root, path);
1948
- assertFileAccess(filepath, project);
1890
+ assertFileAccess(slash(filepath), project);
1949
1891
  const dir = dirname$1(filepath);
1950
1892
  if (!fs.existsSync(dir)) {
1951
1893
  await promises.mkdir(dir, { recursive: true });
@@ -1953,13 +1895,14 @@ const writeFile = async ({ project }, path, data, options) => {
1953
1895
  await promises.writeFile(filepath, data, options);
1954
1896
  };
1955
1897
  const removeFile = async ({ project }, path) => {
1898
+ assertWrite(path, project);
1956
1899
  const filepath = resolve$1(project.config.root, path);
1957
- assertFileAccess(filepath, project);
1900
+ assertFileAccess(slash(filepath), project);
1958
1901
  await promises.rm(filepath);
1959
1902
  };
1960
1903
  const _fileInfo = async ({ project }, path, encoding) => {
1961
1904
  const filepath = resolve$1(project.config.root, path);
1962
- assertFileAccess(filepath, project);
1905
+ assertFileAccess(slash(filepath), project);
1963
1906
  const content = await promises.readFile(filepath, encoding || "base64");
1964
1907
  return {
1965
1908
  content,
@@ -3067,10 +3010,13 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
3067
3010
  vitest.state.catchError(err, "RPC Error");
3068
3011
  }
3069
3012
  function checkFileAccess(path) {
3070
- if (!isFileServingAllowed(path, vite)) {
3013
+ if (!isFileLoadingAllowed(vite.config, path)) {
3071
3014
  throw new Error(`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`);
3072
3015
  }
3073
3016
  }
3017
+ function canWrite(project) {
3018
+ return project.config.browser.api.allowWrite && project.vitest.config.browser.api.allowWrite && project.config.api.allowWrite && project.vitest.config.api.allowWrite;
3019
+ }
3074
3020
  function setupClient(project, rpcId, ws) {
3075
3021
  const mockResolver = new ServerMockResolver(globalServer.vite, { moduleDirectories: project.config?.deps?.moduleDirectories });
3076
3022
  const mocker = project.browser?.provider.mocker;
@@ -3097,6 +3043,18 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
3097
3043
  }
3098
3044
  },
3099
3045
  async onTaskArtifactRecord(id, artifact) {
3046
+ if (!canWrite(project)) {
3047
+ if (artifact.type === "internal:annotation" && artifact.annotation.attachment) {
3048
+ artifact.annotation.attachment = undefined;
3049
+ vitest.logger.error(`[vitest] Cannot record annotation attachment because file writing is disabled. See https://vitest.dev/config/browser/api.`);
3050
+ }
3051
+ // remove attachments if cannot write
3052
+ if (artifact.attachments?.length) {
3053
+ const attachments = artifact.attachments.map((n) => n.path).filter((r) => !!r).join("\", \"");
3054
+ artifact.attachments = [];
3055
+ 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.`);
3056
+ }
3057
+ }
3100
3058
  return vitest._testRun.recordArtifact(id, artifact);
3101
3059
  },
3102
3060
  async onTaskUpdate(method, packs, events) {
@@ -3134,15 +3092,23 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
3134
3092
  },
3135
3093
  async saveSnapshotFile(id, content) {
3136
3094
  checkFileAccess(id);
3095
+ if (!canWrite(project)) {
3096
+ vitest.logger.error(`[vitest] Cannot save snapshot file "${id}". File writing is disabled because server is exposed to the internet, see https://vitest.dev/config/browser/api.`);
3097
+ return;
3098
+ }
3137
3099
  await promises.mkdir(dirname(id), { recursive: true });
3138
- return promises.writeFile(id, content, "utf-8");
3100
+ await promises.writeFile(id, content, "utf-8");
3139
3101
  },
3140
3102
  async removeSnapshotFile(id) {
3141
3103
  checkFileAccess(id);
3104
+ if (!canWrite(project)) {
3105
+ vitest.logger.error(`[vitest] Cannot remove snapshot file "${id}". File writing is disabled because server is exposed to the internet, see https://vitest.dev/config/browser/api.`);
3106
+ return;
3107
+ }
3142
3108
  if (!existsSync(id)) {
3143
3109
  throw new Error(`Snapshot file "${id}" does not exist.`);
3144
3110
  }
3145
- return promises.unlink(id);
3111
+ await promises.unlink(id);
3146
3112
  },
3147
3113
  getBrowserFileSourceMap(id) {
3148
3114
  const mod = globalServer.vite.moduleGraph.getModuleById(id);
package/dist/locators.js CHANGED
@@ -1 +1 @@
1
- export{L as Locator,n as convertElementToCssSelector,q as getByAltTextSelector,r as getByLabelSelector,t as getByPlaceholderSelector,u as getByRoleSelector,v as getByTestIdSelector,w as getByTextSelector,x as getByTitleSelector,o as getIframeScale,p as processTimeoutOptions,s as selectorEngine}from"./index-5ZLDAkrH.js";import"vitest/browser";import"vitest/internal/browser";
1
+ export{L as Locator,n as convertElementToCssSelector,o as getByAltTextSelector,q as getByLabelSelector,r as getByPlaceholderSelector,s as getByRoleSelector,t as getByTestIdSelector,u as getByTextSelector,v as getByTitleSelector,w as getIframeScale,p as processTimeoutOptions,x as selectorEngine}from"./index-Dlkb5vw8.js";import"vitest/browser";import"vitest/internal/browser";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/browser",
3
3
  "type": "module",
4
- "version": "4.1.0-beta.2",
4
+ "version": "4.1.0-beta.4",
5
5
  "description": "Browser running for Vitest",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -51,7 +51,7 @@
51
51
  "providers"
52
52
  ],
53
53
  "peerDependencies": {
54
- "vitest": "4.1.0-beta.2"
54
+ "vitest": "4.1.0-beta.4"
55
55
  },
56
56
  "dependencies": {
57
57
  "magic-string": "^0.30.21",
@@ -60,8 +60,8 @@
60
60
  "sirv": "^3.0.2",
61
61
  "tinyrainbow": "^3.0.3",
62
62
  "ws": "^8.19.0",
63
- "@vitest/mocker": "4.1.0-beta.2",
64
- "@vitest/utils": "4.1.0-beta.2"
63
+ "@vitest/mocker": "4.1.0-beta.4",
64
+ "@vitest/utils": "4.1.0-beta.4"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@opentelemetry/api": "^1.9.0",
@@ -70,11 +70,11 @@
70
70
  "@types/ws": "^8.18.1",
71
71
  "birpc": "^4.0.0",
72
72
  "flatted": "3.3.3",
73
- "ivya": "^1.7.0",
73
+ "ivya": "^1.7.1",
74
74
  "mime": "^4.1.0",
75
75
  "pathe": "^2.0.3",
76
- "@vitest/runner": "4.1.0-beta.2",
77
- "vitest": "4.1.0-beta.2"
76
+ "vitest": "4.1.0-beta.4",
77
+ "@vitest/runner": "4.1.0-beta.4"
78
78
  },
79
79
  "scripts": {
80
80
  "typecheck": "tsc -p ./src/client/tsconfig.json --noEmit",