@vitest/browser 4.1.0-beta.3 → 4.1.0-beta.5

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
@@ -3,22 +3,22 @@ import { dynamicImportPlugin, ServerMockResolver, interceptorPlugin } from '@vit
3
3
  import c from 'tinyrainbow';
4
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';
14
+ import { extractSourcemapFromFile } from '@vitest/utils/source-map/node';
15
15
  import { resolve as resolve$1, basename as basename$1, dirname as dirname$1 } from 'node:path';
16
16
  import { platform } from 'node:os';
17
17
  import { PNG } from 'pngjs';
18
- import pm from 'pixelmatch';
18
+ import { diff } from '@blazediff/core';
19
19
  import { WebSocketServer } from 'ws';
20
20
 
21
- var version = "4.1.0-beta.3";
21
+ var version = "4.1.0-beta.5";
22
22
 
23
23
  const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
24
24
  function normalizeWindowsPath(input = "") {
@@ -942,13 +942,10 @@ var BrowserPlugin = (parentServer, base = "/") => {
942
942
  res.write(content, "utf-8");
943
943
  res.end();
944
944
  });
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], {
945
+ // Serve coverage HTML at ./coverage if configured
946
+ const coverageHtmlDir = parentServer.vitest.config.coverage?.htmlDir;
947
+ if (coverageHtmlDir) {
948
+ server.middlewares.use("/__vitest_test__/coverage", sirv(coverageHtmlDir, {
952
949
  single: true,
953
950
  dev: true,
954
951
  setHeaders: (res) => {
@@ -962,46 +959,6 @@ var BrowserPlugin = (parentServer, base = "/") => {
962
959
  }
963
960
  }));
964
961
  }
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
962
  server.middlewares.use((req, res, next) => {
1006
963
  // 9000 mega head move
1007
964
  // Vite always caches optimized dependencies, but users might mock
@@ -1412,26 +1369,6 @@ function getRequire() {
1412
1369
  }
1413
1370
  return _require;
1414
1371
  }
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
1372
  const postfixRE = /[?#].*$/;
1436
1373
  function cleanUrl(url) {
1437
1374
  return url.replace(postfixRE, "");
@@ -2063,7 +2000,7 @@ const pixelmatch = (reference, actual, { createDiff, ...options }) => {
2063
2000
  ...options
2064
2001
  };
2065
2002
  const diffBuffer = createDiff ? new Uint8Array(reference.data.length) : undefined;
2066
- const mismatchedPixels = pm(reference.data, actual.data, diffBuffer, reference.metadata.width, reference.metadata.height, optionsWithDefaults);
2003
+ const mismatchedPixels = diff(reference.data, actual.data, diffBuffer, reference.metadata.width, reference.metadata.height, optionsWithDefaults);
2067
2004
  const imageArea = reference.metadata.width * reference.metadata.height;
2068
2005
  let allowedMismatchedPixels = Math.min(optionsWithDefaults.allowedMismatchedPixels ?? Number.POSITIVE_INFINITY, (optionsWithDefaults.allowedMismatchedPixelRatio ?? Number.POSITIVE_INFINITY) * imageArea);
2069
2006
  if (allowedMismatchedPixels === Number.POSITIVE_INFINITY) {
@@ -2101,6 +2038,7 @@ const defaultOptions = {
2101
2038
  scale: "device"
2102
2039
  },
2103
2040
  timeout: 5e3,
2041
+ strict: true,
2104
2042
  resolveDiffPath: ({ arg, ext, root, attachmentsDir, browserName, platform, testFileDirectory, testFileName }) => resolve(root, attachmentsDir, testFileDirectory, testFileName, `${arg}-${browserName}-${platform}${ext}`),
2105
2043
  resolveScreenshotPath: ({ arg, ext, root, screenshotDirectory, testFileDirectory, testFileName, browserName }) => resolve(root, testFileDirectory, screenshotDirectory, testFileName, `${arg}-${browserName}-${platform}${ext}`)
2106
2044
  };
@@ -2638,18 +2576,14 @@ class ParentBrowserProject {
2638
2576
  return this.sourceMapCache.get(id);
2639
2577
  }
2640
2578
  const result = this.vite.moduleGraph.getModuleById(id)?.transformResult;
2641
- // this can happen for bundled dependencies in node_modules/.vite
2579
+ // handle non-inline source map such as pre-bundled deps in node_modules/.vite
2642
2580
  if (result && !result.map) {
2643
- const sourceMapUrl = this.retrieveSourceMapURL(result.code);
2644
- if (!sourceMapUrl) {
2645
- return null;
2646
- }
2647
- const filepathDir = dirname(id);
2648
- const sourceMapPath = resolve(filepathDir, sourceMapUrl);
2649
- const map = JSON.parse(readFileSync(sourceMapPath, "utf-8"));
2650
- this.sourceMapCache.set(id, map);
2651
- return map;
2581
+ const filePath = id.split("?")[0];
2582
+ const extracted = extractSourcemapFromFile(result.code, filePath);
2583
+ this.sourceMapCache.set(id, extracted?.map);
2584
+ return extracted?.map;
2652
2585
  }
2586
+ this.sourceMapCache.set(id, result?.map);
2653
2587
  return result?.map;
2654
2588
  },
2655
2589
  getUrlId: (id) => {
@@ -2785,20 +2719,6 @@ class ParentBrowserProject {
2785
2719
  testFile: decodedTestFile
2786
2720
  };
2787
2721
  }
2788
- retrieveSourceMapURL(source) {
2789
- const re = /\/\/[@#]\s*sourceMappingURL=([^\s'"]+)\s*$|\/\*[@#]\s*sourceMappingURL=[^\s*'"]+\s*\*\/\s*$/gm;
2790
- // Keep executing the search to find the *last* sourceMappingURL to avoid
2791
- // picking up sourceMappingURLs from comments, strings, etc.
2792
- let lastMatch, match;
2793
- // eslint-disable-next-line no-cond-assign
2794
- while (match = re.exec(source)) {
2795
- lastMatch = match;
2796
- }
2797
- if (!lastMatch) {
2798
- return null;
2799
- }
2800
- return lastMatch[1];
2801
- }
2802
2722
  }
2803
2723
 
2804
2724
  //#region src/messages.ts
@@ -3177,20 +3097,11 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
3177
3097
  getBrowserFileSourceMap(id) {
3178
3098
  const mod = globalServer.vite.moduleGraph.getModuleById(id);
3179
3099
  const result = mod?.transformResult;
3180
- // this can happen for bundled dependencies in node_modules/.vite
3100
+ // handle non-inline source map such as pre-bundled deps in node_modules/.vite
3181
3101
  if (result && !result.map) {
3182
- const sourceMapUrl = retrieveSourceMapURL(result.code);
3183
- if (!sourceMapUrl) {
3184
- return null;
3185
- }
3186
- const filepathDir = dirname(id);
3187
- const sourceMapPath = resolve(filepathDir, sourceMapUrl);
3188
- try {
3189
- const map = JSON.parse(readFileSync(sourceMapPath, "utf-8"));
3190
- return map;
3191
- } catch {
3192
- return null;
3193
- }
3102
+ const filePath = id.split("?")[0];
3103
+ const extracted = extractSourcemapFromFile(result.code, filePath);
3104
+ return extracted?.map;
3194
3105
  }
3195
3106
  return result?.map;
3196
3107
  },
@@ -3318,20 +3229,6 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
3318
3229
  };
3319
3230
  }
3320
3231
  }
3321
- function retrieveSourceMapURL(source) {
3322
- const re = /\/\/[@#]\s*sourceMappingURL=([^\s'"]+)\s*$|\/\*[@#]\s*sourceMappingURL=[^\s*'"]+\s*\*\/\s*$/gm;
3323
- // keep executing the search to find the *last* sourceMappingURL to avoid
3324
- // picking up sourceMappingURLs from comments, strings, etc.
3325
- let lastMatch, match;
3326
- // eslint-disable-next-line no-cond-assign
3327
- while (match = re.exec(source)) {
3328
- lastMatch = match;
3329
- }
3330
- if (!lastMatch) {
3331
- return null;
3332
- }
3333
- return lastMatch[1];
3334
- }
3335
3232
  // Serialization support utils.
3336
3233
  function cloneByOwnProperties(value) {
3337
3234
  // Clones the value's properties into a new Object. The simpler approach of
@@ -1,4 +1,4 @@
1
- import { UserEventClickOptions, UserEventWheelOptions, UserEventClearOptions, UserEventHoverOptions, UserEventFillOptions, UserEventUploadOptions, UserEventDragAndDropOptions, UserEventSelectOptions, LocatorScreenshotOptions, LocatorByRoleOptions, LocatorOptions } from 'vitest/browser';
1
+ import { UserEventClickOptions, UserEventWheelOptions, UserEventClearOptions, UserEventHoverOptions, UserEventFillOptions, UserEventUploadOptions, UserEventDragAndDropOptions, UserEventSelectOptions, LocatorScreenshotOptions, LocatorByRoleOptions, LocatorOptions, SelectorOptions } from 'vitest/browser';
2
2
 
3
3
  type ClauseCombinator = '' | '>' | '+' | '~' | '>=';
4
4
  type CSSFunctionArgument = CSSComplexSelector | number | string;
@@ -299,15 +299,18 @@ interface SelectorEngine {
299
299
  declare function convertElementToCssSelector(element: Element): string;
300
300
  declare function processTimeoutOptions<T extends {
301
301
  timeout?: number;
302
- }>(options_?: T): T | undefined;
302
+ }>(options_: T | undefined): T | undefined;
303
303
  declare function getIframeScale(): number;
304
304
 
305
+ declare function ensureAwaited<T>(promise: (error?: Error) => Promise<T>): Promise<T>;
306
+
305
307
  declare const selectorEngine: Ivya;
306
308
  declare abstract class Locator {
307
309
  abstract selector: string;
308
310
  private _parsedSelector;
309
311
  protected _container?: Element | undefined;
310
312
  protected _pwSelector?: string | undefined;
313
+ protected _errorSource?: Error;
311
314
  constructor();
312
315
  click(options?: UserEventClickOptions): Promise<void>;
313
316
  dblClick(options?: UserEventClickOptions): Promise<void>;
@@ -349,7 +352,13 @@ declare abstract class Locator {
349
352
  last(): Locator;
350
353
  toString(): string;
351
354
  toJSON(): string;
355
+ findElement(options_?: SelectorOptions): Promise<HTMLElement | SVGElement>;
352
356
  protected triggerCommand<T>(command: string, ...args: any[]): Promise<T>;
353
357
  }
358
+ declare function triggerCommandWithTrace<T>(options: {
359
+ name: string;
360
+ arguments: unknown[];
361
+ errorSource?: Error | undefined;
362
+ }): Promise<T>;
354
363
 
355
- export { Locator, convertElementToCssSelector, getByAltTextSelector, getByLabelSelector, getByPlaceholderSelector, getByRoleSelector, getByTestIdSelector, getByTextSelector, getByTitleSelector, getIframeScale, processTimeoutOptions, selectorEngine };
364
+ export { Locator, convertElementToCssSelector, ensureAwaited, getByAltTextSelector, getByLabelSelector, getByPlaceholderSelector, getByRoleSelector, getByTestIdSelector, getByTextSelector, getByTitleSelector, getIframeScale, processTimeoutOptions, selectorEngine, triggerCommandWithTrace };
package/dist/locators.js CHANGED
@@ -1 +1 @@
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";
1
+ export{L as Locator,n as convertElementToCssSelector,o as ensureAwaited,q as getByAltTextSelector,r as getByLabelSelector,s as getByPlaceholderSelector,t as getByRoleSelector,u as getByTestIdSelector,v as getByTextSelector,w as getByTitleSelector,x as getIframeScale,p as processTimeoutOptions,y as selectorEngine,z as triggerCommandWithTrace}from"./index-BvKPfXh9.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.3",
4
+ "version": "4.1.0-beta.5",
5
5
  "description": "Browser running for Vitest",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -51,17 +51,17 @@
51
51
  "providers"
52
52
  ],
53
53
  "peerDependencies": {
54
- "vitest": "4.1.0-beta.3"
54
+ "vitest": "4.1.0-beta.5"
55
55
  },
56
56
  "dependencies": {
57
+ "@blazediff/core": "1.9.1",
57
58
  "magic-string": "^0.30.21",
58
- "pixelmatch": "7.1.0",
59
59
  "pngjs": "^7.0.0",
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.3",
64
- "@vitest/utils": "4.1.0-beta.3"
63
+ "@vitest/mocker": "4.1.0-beta.5",
64
+ "@vitest/utils": "4.1.0-beta.5"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@opentelemetry/api": "^1.9.0",
@@ -73,8 +73,8 @@
73
73
  "ivya": "^1.7.1",
74
74
  "mime": "^4.1.0",
75
75
  "pathe": "^2.0.3",
76
- "@vitest/runner": "4.1.0-beta.3",
77
- "vitest": "4.1.0-beta.3"
76
+ "@vitest/runner": "4.1.0-beta.5",
77
+ "vitest": "4.1.0-beta.5"
78
78
  },
79
79
  "scripts": {
80
80
  "typecheck": "tsc -p ./src/client/tsconfig.json --noEmit",