Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: web support (browser as an environment) #891

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b03f67b
feat: first trial
Aslemammad Feb 22, 2022
c6358f7
tests for web
Aslemammad Mar 3, 2022
eaa3035
web plugin
Aslemammad Mar 3, 2022
3a1a768
web export
Aslemammad Mar 3, 2022
7e711eb
pnpm lock
Aslemammad Mar 3, 2022
228c475
update
Aslemammad Mar 3, 2022
f468fce
simple usecase works
Aslemammad Mar 7, 2022
7563558
watch and a key work now
Aslemammad Mar 8, 2022
fc100a2
finally works
Aslemammad Mar 11, 2022
0c4b40d
cleanup
Aslemammad Mar 12, 2022
0dde6f1
cross hashing
Aslemammad Mar 12, 2022
d5e8b4c
update
Aslemammad Mar 12, 2022
59faff4
I hate prettier
Aslemammad Mar 12, 2022
19f4de2
trying to see if tests pass
Aslemammad Mar 12, 2022
61188c7
update pnpm lock
Aslemammad Mar 13, 2022
3edc6cc
Merge branch 'main' of github.com:vitest-dev/vitest into feat/web
Aslemammad Mar 13, 2022
c37ca41
done
Aslemammad Mar 13, 2022
80330aa
update pnpm lock
Aslemammad Mar 13, 2022
6606a2e
update
Aslemammad Mar 13, 2022
978836f
update
Aslemammad Mar 13, 2022
bd4828a
remove @ts-expect-error
Aslemammad Mar 13, 2022
a59e2d7
add ts-ignore
Aslemammad Mar 13, 2022
6e8239d
update readme
Aslemammad Mar 13, 2022
f94b94c
chore: add ui + fix collect tests on Windows
userquin Mar 13, 2022
532e530
add style
Aslemammad Mar 13, 2022
7796c06
change iframe
Aslemammad Mar 13, 2022
1cddb48
change name
Aslemammad Mar 13, 2022
5065b98
remove comma
Aslemammad Mar 18, 2022
4f9a913
Merge branch 'main' of github.com:vitest-dev/vitest into feat/web
Aslemammad Mar 18, 2022
2771c44
chore: lint
antfu Apr 3, 2022
c397847
chore: lint
antfu Apr 3, 2022
e4621af
Merge branch 'main' into feat/web
antfu Apr 3, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"packageManager": "[email protected]",
"description": "A blazing fast unit test framework powered by Vite",
"scripts": {
"build": "pnpm -r --filter {packages} run build",
"build": "pnpm -r --filter {packages} --filter !web run build",
"ci": "ni && nr typecheck && nr lint && nr build && nr test:all",
"dev": "pnpm -r --parallel --filter vitest --filter ui --filter web-worker run dev",
"docs": "npm -C docs run dev",
Expand All @@ -26,7 +26,9 @@
"typecheck": "tsc --noEmit",
"ui:build": "vite build packages/ui",
"ui:dev": "vite packages/ui",
"ui:test": "npm -C packages/ui run test:run"
"ui:test": "npm -C packages/ui run test:run",
"web:build": "pnpm -r --filter web run build",
"web:dev": "pnpm -r --filter web run dev"
},
"devDependencies": {
"@antfu/eslint-config": "^0.19.4",
Expand Down Expand Up @@ -57,7 +59,7 @@
"rollup-plugin-esbuild": "^4.8.2",
"rollup-plugin-license": "^2.6.1",
"typescript": "^4.6.3",
"vite": "2.9.0-beta.9",
"vite": "^2.8.6",
"vitepress": "^0.22.3",
"vitest": "workspace:*",
"vue": "^3.2.31"
Expand Down
6 changes: 3 additions & 3 deletions packages/vite-node/src/externalize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { existsSync } from 'fs'
import fs from 'fs'
import { isNodeBuiltin, isValidNodeImport } from 'mlly'
import type { DepsHandlingOptions } from './types'
import { slash } from './utils'
Expand Down Expand Up @@ -26,7 +26,7 @@ export function guessCJSversion(id: string): string | undefined {
id.replace(ESM_EXT_RE, '.cjs.js'),
id.replace(ESM_EXT_RE, '.js'),
]) {
if (existsSync(i))
if (fs.existsSync(i))
return i
}
}
Expand All @@ -36,7 +36,7 @@ export function guessCJSversion(id: string): string | undefined {
id.replace(ESM_FOLDER_RE, '/cjs/$1'),
id.replace(ESM_FOLDER_RE, '/$1'),
]) {
if (existsSync(i))
if (fs.existsSync(i))
return i
}
}
Expand Down
7 changes: 7 additions & 0 deletions packages/vitest/LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,13 @@ Repository: git://github.com/kpdecker/jsdiff.git

---------------------------------------

## eastasianwidth
License: MIT
By: Masaki Komagata
Repository: git://github.com/komagata/eastasianwidth.git

---------------------------------------

## emoji-regex
License: MIT
By: Mathias Bynens
Expand Down
9 changes: 9 additions & 0 deletions packages/vitest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
"import": "./dist/node.js",
"types": "./dist/node.d.ts"
},
"./web": {
"import": "./dist/web.js",
"types": "./dist/web.d.ts"
},
"./config": {
"import": "./dist/config.js",
"require": "./dist/config.cjs",
Expand All @@ -61,11 +65,15 @@
},
"peerDependencies": {
"@vitest/ui": "*",
"@vitest/web": "*",
"c8": "*",
"happy-dom": "*",
"jsdom": "*"
},
"peerDependenciesMeta": {
"@vitest/web": {
"optional": true
},
"@vitest/ui": {
"optional": true
},
Expand Down Expand Up @@ -99,6 +107,7 @@
"@types/prompts": "^2.4.0",
"@types/sinonjs__fake-timers": "^8.1.2",
"@vitest/ui": "workspace:*",
"@vitest/web": "workspace:*",
"birpc": "^0.2.2",
"c8": "^7.11.0",
"cac": "^6.7.12",
Expand Down
4 changes: 4 additions & 0 deletions packages/vitest/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,19 @@ import pkg from './package.json'

const entries = [
'src/index.ts',
'src/web.ts',
'src/node/cli.ts',
'src/node.ts',
'src/runtime/worker.ts',
'src/runtime/entry.ts',
'src/integrations/jest-mock.ts',
'src/runtime/suite.ts',
]

const dtsEntries = [
'src/index.ts',
'src/node.ts',
'src/web.ts',
'src/config.ts',
]

Expand All @@ -34,6 +37,7 @@ const external = [
'worker_threads',
'inspector',
'c8',
'@vitest/web',
]

const plugins = [
Expand Down
69 changes: 59 additions & 10 deletions packages/vitest/src/api/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,19 @@ import { WebSocketServer } from 'ws'
import type { ModuleNode } from 'vite'
import { API_PATH } from '../constants'
import type { Vitest } from '../node'
import type { File, ModuleGraphData, Reporter, TaskResultPack, UserConsoleLog } from '../types'
import type {
File,
ModuleGraphData,
Reporter,
TaskResultPack,
UserConsoleLog,
} from '../types'
import { interpretSourcePos, parseStacktrace } from '../utils/source-map'
import type { TransformResultWithSource, WebSocketEvents, WebSocketHandlers } from './types'
import type {
TransformResultWithSource,
WebSocketEvents,
WebSocketHandlers,
} from './types'

export function setup(ctx: Vitest) {
const wss = new WebSocketServer({ noServer: true })
Expand All @@ -33,9 +43,26 @@ export function setup(ctx: Vitest) {
function setupClient(ws: WebSocket) {
const rpc = createBirpc<WebSocketEvents, WebSocketHandlers>(
{
async onWatcherStart() {
await ctx.report('onWatcherStart')
},
async onFinished() {
await ctx.report('onFinished')
},
async onCollected(files) {
ctx.state.collectFiles(files)
await ctx.report('onCollected', files)
},
async onTaskUpdate(packs) {
ctx.state.updateTasks(packs)
await ctx.report('onTaskUpdate', packs)
},
getFiles() {
return ctx.state.getFiles()
},
getPaths() {
return ctx.state.getPaths()
},
readFile(id) {
return fs.readFile(id, 'utf-8')
},
Expand All @@ -49,7 +76,8 @@ export function setup(ctx: Vitest) {
return ctx.config
},
async getTransformResult(id) {
const result: TransformResultWithSource | null | undefined = await ctx.vitenode.transformRequest(id)
const result: TransformResultWithSource | null | undefined
= await ctx.vitenode.transformRequest(id)
if (result) {
try {
result.source = result.source || (await fs.readFile(id, 'utf-8'))
Expand All @@ -66,7 +94,10 @@ export function setup(ctx: Vitest) {
function clearId(id?: string | null) {
return id?.replace(/\?v=\w+$/, '') || ''
}
async function get(mod?: ModuleNode, seen = new Map<ModuleNode, string>()) {
async function get(
mod?: ModuleNode,
seen = new Map<ModuleNode, string>(),
) {
if (!mod || !mod.id)
return
if (seen.has(mod))
Expand All @@ -82,8 +113,12 @@ export function setup(ctx: Vitest) {
else {
inlined.add(id)
}
const mods = Array.from(mod.importedModules).filter(i => i.id && !i.id.includes('/vitest/dist/'))
graph[id] = (await Promise.all(mods.map(m => get(m, seen)))).filter(Boolean) as string[]
const mods = Array.from(mod.importedModules).filter(
i => i.id && !i.id.includes('/vitest/dist/'),
)
graph[id] = (
await Promise.all(mods.map(m => get(m, seen)))
).filter(Boolean) as string[]
return id
}
await get(ctx.server.moduleGraph.getModuleById(id))
Expand Down Expand Up @@ -125,6 +160,14 @@ class WebSocketReporter implements Reporter {
public clients: Map<WebSocket, BirpcReturn<WebSocketEvents>>,
) {}

onPathsCollected(paths?: string[]) {
if (this.clients.size === 0)
return
this.clients.forEach((client) => {
client.onPathsCollected?.(paths)
})
}

onCollected(files?: File[]) {
if (this.clients.size === 0)
return
Expand All @@ -137,10 +180,16 @@ class WebSocketReporter implements Reporter {
if (this.clients.size === 0)
return

await Promise.all(packs.map(async(i) => {
if (i[1]?.error)
await interpretSourcePos(parseStacktrace(i[1].error as any), this.ctx)
}))
await Promise.all(
packs.map(async(i) => {
if (i[1]?.error) {
await interpretSourcePos(
parseStacktrace(i[1].error as any),
this.ctx,
)
}
}),
)

this.clients.forEach((client) => {
client.onTaskUpdate?.(packs)
Expand Down
9 changes: 7 additions & 2 deletions packages/vitest/src/api/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import type { TransformResult } from 'vite'
import type { File, ModuleGraphData, Reporter, ResolvedConfig } from '../types'
import type { File, ModuleGraphData, Reporter, ResolvedConfig, TaskResultPack } from '../types'

export interface TransformResultWithSource extends TransformResult {
source?: string
}

export interface WebSocketHandlers {
onWatcherStart: () => Promise<void>
onFinished(files?: File[]): Promise<void>
onCollected(files?: File[]): Promise<void>
onTaskUpdate(packs: TaskResultPack[]): void
getFiles(): File[]
getPaths(): string[]
getConfig(): ResolvedConfig
getModuleGraph(id: string): Promise<ModuleGraphData>
getTransformResult(id: string): Promise<TransformResultWithSource | undefined>
Expand All @@ -16,5 +21,5 @@ export interface WebSocketHandlers {
updateSnapshot(file?: File): Promise<void>
}

export interface WebSocketEvents extends Pick<Reporter, 'onCollected' | 'onFinished' | 'onTaskUpdate' | 'onUserConsoleLog'> {
export interface WebSocketEvents extends Pick<Reporter, 'onCollected' | 'onFinished' | 'onTaskUpdate' | 'onUserConsoleLog' | 'onPathsCollected'> {
}
2 changes: 1 addition & 1 deletion packages/vitest/src/integrations/chai/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import chai from 'chai'
import * as chai from 'chai'
import './setup'
import { getState, setState } from './jest-expect'

Expand Down
6 changes: 3 additions & 3 deletions packages/vitest/src/integrations/chai/jest-matcher-utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// we are using only the ones needed by @testing-library/jest-dom
// if you need more, just ask

import c from 'picocolors'
import type { Formatter } from 'picocolors/types'
import { format as prettyFormat, plugins as prettyFormatPlugins } from 'pretty-format'
import { unifiedDiff } from '../../node/diff'

export const EXPECTED_COLOR = c.green
export const RECEIVED_COLOR = c.red
Expand Down Expand Up @@ -166,6 +164,8 @@ export interface DiffOptions {

// TODO: do something with options
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function diff(a: any, b: any, options?: DiffOptions) {
export async function diff(a: any, b: any, options?: DiffOptions) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function cannot be async, it is used by extended jest matchers. @testing-library for example will not work as expected

const { unifiedDiff } = await import('../../node/diff')

return unifiedDiff(stringify(a), stringify(b))
}
2 changes: 1 addition & 1 deletion packages/vitest/src/integrations/chai/setup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import chai from 'chai'
import * as chai from 'chai'
import Subset from 'chai-subset'
import { SnapshotPlugin } from '../snapshot/chai'
import { JestExtend } from './jest-extend'
Expand Down
16 changes: 10 additions & 6 deletions packages/vitest/src/integrations/coverage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { existsSync, promises as fs } from 'fs'
import fs from 'fs'
import { createRequire } from 'module'
import { pathToFileURL } from 'url'
import type { Profiler } from 'inspector'
Expand All @@ -23,11 +23,13 @@ export function resolveC8Options(options: C8Options, root: string): ResolvedC8Op
}

export async function cleanCoverage(options: ResolvedC8Options, clean = true) {
if (clean && existsSync(options.reportsDirectory))
await fs.rm(options.reportsDirectory, { recursive: true, force: true })
const fsp = fs.promises

if (!existsSync(options.tempDirectory))
await fs.mkdir(options.tempDirectory, { recursive: true })
if (clean && fs.existsSync(options.reportsDirectory))
await fsp.rm(options.reportsDirectory, { recursive: true, force: true })

if (!fs.existsSync(options.tempDirectory))
await fsp.mkdir(options.tempDirectory, { recursive: true })
}

const require = createRequire(import.meta.url)
Expand All @@ -42,6 +44,8 @@ export function takeCoverage() {
}

export async function reportCoverage(ctx: Vitest) {
const fsp = fs.promises

takeCoverage()

// eslint-disable-next-line @typescript-eslint/no-var-requires
Expand All @@ -62,7 +66,7 @@ export async function reportCoverage(ctx: Vitest) {

let code: string | undefined
try {
code = (await fs.readFile(file)).toString()
code = (await fsp.readFile(file)).toString()
}
catch {}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { promises as fs } from 'fs'
import _fs from 'fs'
import type MagicString from 'magic-string'
import { rpc } from '../../../runtime/rpc'
import { getOriginalPos, lineSplitRE, numberToPos, posToNumber } from '../../../utils/source-map'
Expand All @@ -14,6 +14,7 @@ export interface InlineSnapshot {
export async function saveInlineSnapshots(
snapshots: Array<InlineSnapshot>,
) {
const fs = _fs?.promises
const MagicString = (await import('magic-string')).default
const files = new Set(snapshots.map(i => i.file))
await Promise.all(Array.from(files).map(async(file) => {
Expand Down
4 changes: 3 additions & 1 deletion packages/vitest/src/integrations/snapshot/port/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import fs, { promises as fsp } from 'fs'
import fs from 'fs'
import path from 'pathe'
import naturalCompare from 'natural-compare'
import type { OptionsReceived as PrettyFormatOptions } from 'pretty-format'
Expand Down Expand Up @@ -152,6 +152,8 @@ export async function saveSnapshotFile(
)

ensureDirectoryExists(snapshotPath)

const fsp = fs?.promises
await fsp.writeFile(
snapshotPath,
`${writeSnapshotVersion()}\n\n${snapshots.join('\n\n')}\n`,
Expand Down
1 change: 1 addition & 0 deletions packages/vitest/src/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ cli
.option('--globals', 'inject apis globally')
.option('--global', 'deprecated, use --globals')
.option('--dom', 'mock browser api with happy-dom')
.option('--web', 'run tests natively in the browser')
.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)')
Expand Down
Loading