Skip to content

Commit

Permalink
feat!: split vitest into separate packages (#2575)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va committed Jan 2, 2023
1 parent e641a11 commit c8e6fb6
Show file tree
Hide file tree
Showing 44 changed files with 1,065 additions and 509 deletions.
17 changes: 17 additions & 0 deletions packages/expect/README.md
@@ -0,0 +1,17 @@
# @vitest/expect

Jest's expect matchers as a Chai plugin.

## Usage

```js
import * as chai from 'chai'
import { JestAsymmetricMatchers, JestChaiExpect, JestExtend } from '@vitest/expect'

// allows using expect.extend instead of chai.use to extend plugins
chai.use(JestExtend)
// adds all jest matchers to expect
chai.use(JestChaiExpect)
// adds asymmetric matchers like stringContaining, objectContaining
chai.use(JestAsymmetricMatchers)
```
37 changes: 37 additions & 0 deletions packages/expect/package.json
@@ -0,0 +1,37 @@
{
"name": "@vitest/expect",
"type": "module",
"version": "0.26.2",
"description": "Jest's expect matchers as a Chai plugin",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/vitest-dev/vitest.git",
"directory": "packages/expect"
},
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./*": "./*"
},
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "rimraf dist && rollup -c",
"dev": "rollup -c --watch",
"prepublishOnly": "pnpm build"
},
"dependencies": {
"@vitest/spy": "workspace:*",
"@vitest/utils": "workspace:*",
"chai": "^4.3.7",
"picocolors": "^1.0.0"
}
}
51 changes: 51 additions & 0 deletions packages/expect/rollup.config.js
@@ -0,0 +1,51 @@
import { builtinModules } from 'module'
import esbuild from 'rollup-plugin-esbuild'
import dts from 'rollup-plugin-dts'
import { defineConfig } from 'rollup'
import pkg from './package.json'

const external = [
...builtinModules,
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
]

const plugins = [
esbuild({
target: 'node14',
}),
]

export default defineConfig([
{
input: 'src/index.ts',
output: {
dir: 'dist',
format: 'esm',
entryFileNames: '[name].js',
chunkFileNames: 'chunk-[name].js',
},
external,
plugins,
onwarn,
},
{
input: 'src/index.ts',
output: {
dir: 'dist',
entryFileNames: '[name].d.ts',
format: 'esm',
},
external,
plugins: [
dts({ respectExternal: true }),
],
onwarn,
},
])

function onwarn(message) {
if (['EMPTY_BUNDLE', 'CIRCULAR_DEPENDENCY'].includes(message.code))
return
console.error(message)
}
@@ -1,3 +1,3 @@
export const GLOBAL_EXPECT = Symbol.for('expect-global')
export const MATCHERS_OBJECT = Symbol.for('matchers-object')
export const JEST_MATCHERS_OBJECT = Symbol.for('$$jest-matchers-object')
export const GLOBAL_EXPECT = Symbol.for('expect-global')
7 changes: 7 additions & 0 deletions packages/expect/src/index.ts
@@ -0,0 +1,7 @@
export * from './jest-asymmetric-matchers'
export * from './jest-utils'
export * from './constants'
export * from './types'
export { getState, setState } from './state'
export { JestChaiExpect } from './jest-expect'
export { JestExtend } from './jest-extend'
@@ -1,6 +1,6 @@
import type { ChaiPlugin, MatcherState } from '../../types/chai'
import type { ChaiPlugin, MatcherState } from './types'
import { GLOBAL_EXPECT } from './constants'
import { getState } from './jest-expect'
import { getState } from './state'
import * as matcherUtils from './jest-matcher-utils'

import { equals, isA } from './jest-utils'
Expand Down
@@ -1,44 +1,14 @@
import c from 'picocolors'
import { AssertionError } from 'chai'
import type { EnhancedSpy } from '../spy'
import { isMockFunction } from '../spy'
import { addSerializer } from '../snapshot/port/plugins'
import type { Constructable, Test } from '../../types'
import { assertTypes } from '../../utils'
import { unifiedDiff } from '../../utils/diff'
import type { ChaiPlugin, MatcherState } from '../../types/chai'
import { assertTypes, unifiedDiff } from '@vitest/utils'
import type { Constructable } from '@vitest/utils'
import type { EnhancedSpy } from '@vitest/spy'
import { isMockFunction } from '@vitest/spy'
import type { ChaiPlugin } from './types'
import { arrayBufferEquality, generateToBeMessage, iterableEquality, equals as jestEquals, sparseArrayEquality, subsetEquality, typeEquality } from './jest-utils'
import type { AsymmetricMatcher } from './jest-asymmetric-matchers'
import { stringify } from './jest-matcher-utils'
import { GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, MATCHERS_OBJECT } from './constants'

if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) {
const globalState = new WeakMap<Vi.ExpectStatic, MatcherState>()
const matchers = Object.create(null)
Object.defineProperty(globalThis, MATCHERS_OBJECT, {
get: () => globalState,
})
Object.defineProperty(globalThis, JEST_MATCHERS_OBJECT, {
configurable: true,
get: () => ({
state: globalState.get((globalThis as any)[GLOBAL_EXPECT]),
matchers,
}),
})
}

export const getState = <State extends MatcherState = MatcherState>(expect: Vi.ExpectStatic): State =>
(globalThis as any)[MATCHERS_OBJECT].get(expect)

export const setState = <State extends MatcherState = MatcherState>(
state: Partial<State>,
expect: Vi.ExpectStatic,
): void => {
const map = (globalThis as any)[MATCHERS_OBJECT]
const current = map.get(expect) || {}
Object.assign(current, state)
map.set(expect, current)
}
import { JEST_MATCHERS_OBJECT } from './constants'

// Jest Expect Compact
export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
Expand Down Expand Up @@ -681,7 +651,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
return result.call(this, ...args)
},
(err: any) => {
throw new Error(`promise rejected "${toString(err)}" instead of resolving`)
throw new Error(`promise rejected "${String(err)}" instead of resolving`)
},
)
}
Expand Down Expand Up @@ -710,7 +680,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
return async (...args: any[]) => {
return wrapper.then(
(value: any) => {
throw new Error(`promise resolved "${toString(value)}" instead of rejecting`)
throw new Error(`promise resolved "${String(value)}" instead of rejecting`)
},
(err: any) => {
utils.flag(this, 'object', err)
Expand All @@ -723,19 +693,4 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {

return proxy
})

utils.addMethod(
chai.expect,
'addSnapshotSerializer',
addSerializer,
)
}

function toString(value: any) {
try {
return `${value}`
}
catch (_error) {
return 'unknown'
}
}
Expand Up @@ -4,11 +4,10 @@ import type {
MatcherState,
MatchersObject,
SyncExpectationResult,
} from '../../types/chai'
import { getSnapshotClient } from '../snapshot/chai'
} from './types'
import { JEST_MATCHERS_OBJECT } from './constants'
import { AsymmetricMatcher } from './jest-asymmetric-matchers'
import { getState } from './jest-expect'
import { getState } from './state'

import * as matcherUtils from './jest-matcher-utils'

Expand Down Expand Up @@ -39,7 +38,6 @@ const getMatcherState = (assertion: Chai.AssertionStatic & Chai.Assertion, expec
equals,
// needed for built-in jest-snapshots, but we don't use it
suppressedErrors: [],
snapshotState: getSnapshotClient().snapshotState!,
}

return {
Expand Down
@@ -1,36 +1,15 @@
// we are using only the ones needed by @testing-library/jest-dom
// if you need more, just ask

import c from 'picocolors'
import type { PrettyFormatOptions } from 'pretty-format'
import { format as prettyFormat, plugins as prettyFormatPlugins } from 'pretty-format'
import { unifiedDiff } from '../../utils/diff'
import type { DiffOptions, MatcherHintOptions } from '../../types/matcher-utils'
import { stringify, unifiedDiff } from '@vitest/utils'
import type { DiffOptions, MatcherHintOptions } from './types'

export { stringify }

export const EXPECTED_COLOR = c.green
export const RECEIVED_COLOR = c.red
export const INVERTED_COLOR = c.inverse
export const BOLD_WEIGHT = c.bold
export const DIM_COLOR = c.dim

const {
AsymmetricMatcher,
DOMCollection,
DOMElement,
Immutable,
ReactElement,
ReactTestComponent,
} = prettyFormatPlugins

const PLUGINS = [
ReactTestComponent,
ReactElement,
DOMElement,
DOMCollection,
Immutable,
AsymmetricMatcher,
]

export function matcherHint(
matcherName: string,
received = 'received',
Expand All @@ -39,12 +18,12 @@ export function matcherHint(
) {
const {
comment = '',
expectedColor = EXPECTED_COLOR,
isDirectExpectCall = false, // seems redundant with received === ''
isNot = false,
promise = '',
receivedColor = RECEIVED_COLOR,
secondArgument = '',
expectedColor = EXPECTED_COLOR,
receivedColor = RECEIVED_COLOR,
secondArgumentColor = EXPECTED_COLOR,
} = options
let hint = ''
Expand Down Expand Up @@ -102,35 +81,6 @@ const SPACE_SYMBOL = '\u{00B7}' // middle dot
const replaceTrailingSpaces = (text: string): string =>
text.replace(/\s+$/gm, spaces => SPACE_SYMBOL.repeat(spaces.length))

export function stringify(object: unknown, maxDepth = 10, { maxLength, ...options }: PrettyFormatOptions & { maxLength?: number } = {}): string {
const MAX_LENGTH = maxLength ?? 10000
let result

try {
result = prettyFormat(object, {
maxDepth,
escapeString: false,
// min: true,
plugins: PLUGINS,
...options,
})
}
catch {
result = prettyFormat(object, {
callToJSON: false,
maxDepth,
escapeString: false,
// min: true,
plugins: PLUGINS,
...options,
})
}

return result.length >= MAX_LENGTH && maxDepth > 1
? stringify(object, Math.floor(maxDepth / 2))
: result
}

export const printReceived = (object: unknown): string =>
RECEIVED_COLOR(replaceTrailingSpaces(stringify(object)))
export const printExpected = (value: unknown): string =>
Expand Down
Expand Up @@ -21,8 +21,9 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { isObject } from '../../utils'
import type { Tester } from '../../types/chai'

import { isObject } from '@vitest/utils'
import type { Tester } from './types'

// Extracted out of jasmine 2.5.2
export function equals(
Expand Down
30 changes: 30 additions & 0 deletions packages/expect/src/state.ts
@@ -0,0 +1,30 @@
import type { MatcherState } from './types'
import { GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, MATCHERS_OBJECT } from './constants'

if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) {
const globalState = new WeakMap<Vi.ExpectStatic, MatcherState>()
const matchers = Object.create(null)
Object.defineProperty(globalThis, MATCHERS_OBJECT, {
get: () => globalState,
})
Object.defineProperty(globalThis, JEST_MATCHERS_OBJECT, {
configurable: true,
get: () => ({
state: globalState.get((globalThis as any)[GLOBAL_EXPECT]),
matchers,
}),
})
}

export const getState = <State extends MatcherState = MatcherState>(expect: Vi.ExpectStatic): State =>
(globalThis as any)[MATCHERS_OBJECT].get(expect)

export const setState = <State extends MatcherState = MatcherState>(
state: Partial<State>,
expect: Vi.ExpectStatic,
): void => {
const map = (globalThis as any)[MATCHERS_OBJECT]
const current = map.get(expect) || {}
Object.assign(current, state)
map.set(expect, current)
}

0 comments on commit c8e6fb6

Please sign in to comment.