From aa239bf56dcc83396b6fdd1e907abbc979d981b6 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Fri, 18 Mar 2022 01:55:59 +0800 Subject: [PATCH] fix: inline `mockdate` and fix negative thread time (#972) --- packages/vite-node/src/server.ts | 5 +- packages/vitest/LICENSE.md | 29 ------- packages/vitest/package.json | 1 - packages/vitest/src/integrations/mockdate.ts | 91 ++++++++++++++++++++ packages/vitest/src/integrations/timers.ts | 8 +- packages/vitest/src/node/reporters/base.ts | 2 +- packages/vitest/src/runtime/setup.ts | 9 +- pnpm-lock.yaml | 6 -- 8 files changed, 105 insertions(+), 46 deletions(-) create mode 100644 packages/vitest/src/integrations/mockdate.ts diff --git a/packages/vite-node/src/server.ts b/packages/vite-node/src/server.ts index a4b9d917dcf1..4cf736745aa0 100644 --- a/packages/vite-node/src/server.ts +++ b/packages/vite-node/src/server.ts @@ -6,6 +6,9 @@ import { toFilePath, withInlineSourcemap } from './utils' export * from './externalize' +// store the original reference to avoid it been mocked +const RealDate = Date + export class ViteNodeServer { private fetchPromiseMap = new Map>() private transformPromiseMap = new Map>() @@ -78,7 +81,7 @@ export class ViteNodeServer { const filePath = toFilePath(id, this.server.config.root) const module = this.server.moduleGraph.getModuleById(id) - const timestamp = module?.lastHMRTimestamp || Date.now() + const timestamp = module?.lastHMRTimestamp || RealDate.now() const cache = this.fetchCache.get(filePath) if (timestamp && cache && cache.timestamp >= timestamp) return cache.result diff --git a/packages/vitest/LICENSE.md b/packages/vitest/LICENSE.md index 21b2e5607a42..176151d2d35a 100644 --- a/packages/vitest/LICENSE.md +++ b/packages/vitest/LICENSE.md @@ -1231,35 +1231,6 @@ Repository: unjs/mlly --------------------------------------- -## mockdate -License: MIT -By: Bob Lauer -Repository: https://github.com/boblauer/MockDate.git - -> The MIT License (MIT) -> -> Copyright (c) 2014 Bob Lauer -> -> Permission is hereby granted, free of charge, to any person obtaining a copy -> of this software and associated documentation files (the "Software"), to deal -> in the Software without restriction, including without limitation the rights -> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -> copies of the Software, and to permit persons to whom the Software is -> furnished to do so, subject to the following conditions: -> -> The above copyright notice and this permission notice shall be included in all -> copies or substantial portions of the Software. -> -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -> SOFTWARE. - ---------------------------------------- - ## natural-compare License: MIT By: Lauri Rooden diff --git a/packages/vitest/package.json b/packages/vitest/package.json index fec4072ac732..15d1967ad296 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -118,7 +118,6 @@ "magic-string": "^0.26.1", "micromatch": "^4.0.4", "mlly": "^0.4.3", - "mockdate": "^3.0.5", "natural-compare": "^1.4.0", "pathe": "^0.2.0", "picocolors": "^1.0.0", diff --git a/packages/vitest/src/integrations/mockdate.ts b/packages/vitest/src/integrations/mockdate.ts new file mode 100644 index 000000000000..83a5a7768746 --- /dev/null +++ b/packages/vitest/src/integrations/mockdate.ts @@ -0,0 +1,91 @@ +/* Ported from https://github.com/boblauer/MockDate/blob/master/src/mockdate.ts */ +/* +The MIT License (MIT) + +Copyright (c) 2014 Bob Lauer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +export const RealDate = Date + +let now: null | number = null + +class MockDate extends RealDate { + constructor() + constructor(value: number | string) + constructor(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number) + constructor(y?: number | string, m?: number, d?: number, h?: number, M?: number, s?: number, ms?: number) { + super() + + let date: any + switch (arguments.length) { + case 0: + if (now !== null) + date = new RealDate(now.valueOf()) + else + date = new RealDate() + break + case 1: + date = new RealDate(y!) + break + default: + d = typeof d === 'undefined' ? 1 : d + h = h || 0 + M = M || 0 + s = s || 0 + ms = ms || 0 + date = new RealDate(y as number, m!, d, h, M, s, ms) + break + } + + return date + } +} + +// MockDate.prototype = RealDate.prototype + +MockDate.UTC = RealDate.UTC + +MockDate.now = function() { + return new MockDate().valueOf() +} + +MockDate.parse = function(dateString) { + return RealDate.parse(dateString) +} + +MockDate.toString = function() { + return RealDate.toString() +} + +export function mockDate(date: string | number | Date): void { + const dateObj = new RealDate(date.valueOf()) + if (isNaN(dateObj.getTime())) + throw new TypeError(`mockdate: The time set is an invalid date: ${date}`) + + // @ts-expect-error global + globalThis.Date = MockDate + + now = dateObj.valueOf() +} + +export function resetDate(): void { + globalThis.Date = RealDate +} diff --git a/packages/vitest/src/integrations/timers.ts b/packages/vitest/src/integrations/timers.ts index 2a0f4a2427c0..2e5ed102dfaa 100644 --- a/packages/vitest/src/integrations/timers.ts +++ b/packages/vitest/src/integrations/timers.ts @@ -12,7 +12,7 @@ import type { import { withGlobal, } from '@sinonjs/fake-timers' -import MockDate from 'mockdate' +import { RealDate, mockDate, resetDate } from './mockdate' export class FakeTimers { private _clock!: InstalledClock @@ -20,7 +20,7 @@ export class FakeTimers { private _fakingDate: boolean private _fakeTimers: FakeTimerWithContext private _maxLoops: number - private _now = Date.now + private _now = RealDate.now constructor({ global, @@ -83,7 +83,7 @@ export class FakeTimers { useRealTimers(): void { if (this._fakingDate) { - MockDate.reset() + resetDate() this._fakingDate = false } @@ -127,7 +127,7 @@ export class FakeTimers { this._clock.setSystemTime(now) } else { - MockDate.set(now ?? this.getRealSystemTime()) + mockDate(now ?? this.getRealSystemTime()) this._fakingDate = true } } diff --git a/packages/vitest/src/node/reporters/base.ts b/packages/vitest/src/node/reporters/base.ts index cd99698972ba..badf690c57e1 100644 --- a/packages/vitest/src/node/reporters/base.ts +++ b/packages/vitest/src/node/reporters/base.ts @@ -135,7 +135,7 @@ export abstract class BaseReporter implements Reporter { } const executionTime = this.end - this.start - const threadTime = files.reduce((acc, test) => acc + (test.result?.duration || 0) + (test.collectDuration || 0), 0) + const threadTime = files.reduce((acc, test) => acc + Math.max(0, test.result?.duration || 0) + Math.max(0, test.collectDuration || 0), 0) const padTitle = (str: string) => c.dim(`${str.padStart(10)} `) const time = (time: number) => { diff --git a/packages/vitest/src/runtime/setup.ts b/packages/vitest/src/runtime/setup.ts index 03c4a18e35fb..477a597be824 100644 --- a/packages/vitest/src/runtime/setup.ts +++ b/packages/vitest/src/runtime/setup.ts @@ -5,6 +5,7 @@ import type { ResolvedConfig } from '../types' import { getWorkerState, toArray } from '../utils' import * as VitestIndex from '../index' import { resetRunOnceCounter } from '../integrations/run-once' +import { RealDate } from '../integrations/mockdate' import { rpc } from './rpc' let globalSetup = false @@ -63,7 +64,7 @@ export function setupConsoleLogSpy() { type: 'stdout', content: stdoutBuffer.map(i => String(i)).join(''), taskId: getWorkerState().current?.id, - time: stdoutTime || Date.now(), + time: stdoutTime || RealDate.now(), }) } stdoutBuffer.length = 0 @@ -75,7 +76,7 @@ export function setupConsoleLogSpy() { type: 'stderr', content: stderrBuffer.map(i => String(i)).join(''), taskId: getWorkerState().current?.id, - time: stderrTime || Date.now(), + time: stderrTime || RealDate.now(), }) } stderrBuffer.length = 0 @@ -84,7 +85,7 @@ export function setupConsoleLogSpy() { const stdout = new Writable({ write(data, encoding, callback) { - stdoutTime = stdoutTime || Date.now() + stdoutTime = stdoutTime || RealDate.now() stdoutBuffer.push(data) schedule() callback() @@ -92,7 +93,7 @@ export function setupConsoleLogSpy() { }) const stderr = new Writable({ write(data, encoding, callback) { - stderrTime = stderrTime || Date.now() + stderrTime = stderrTime || RealDate.now() stderrBuffer.push(data) schedule() callback() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72d454c2a608..1742d10dd149 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -609,7 +609,6 @@ importers: magic-string: ^0.26.1 micromatch: ^4.0.4 mlly: ^0.4.3 - mockdate: ^3.0.5 natural-compare: ^1.4.0 pathe: ^0.2.0 picocolors: ^1.0.0 @@ -660,7 +659,6 @@ importers: magic-string: 0.26.1 micromatch: 4.0.4 mlly: 0.4.3 - mockdate: 3.0.5 natural-compare: 1.4.0 pathe: 0.2.0 picocolors: 1.0.0 @@ -14411,10 +14409,6 @@ packages: /mlly/0.4.3: resolution: {integrity: sha512-xezyv7hnfFPuiDS3AiJuWs0OxlvooS++3L2lURvmh/1n7UG4O2Ehz9UkwWgg3wyLEPKGVfJLlr2DjjTCl9UJTg==} - /mockdate/3.0.5: - resolution: {integrity: sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==} - dev: true - /moment/2.29.1: resolution: {integrity: sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==} dev: true