diff --git a/.gitignore b/.gitignore index 02634697bf..8808186ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ docs/_dist mocha.js .karma/ !lib/mocha.js +/types ######################################### # NON-MOCHA STUFF GOES BELOW THIS THING # diff --git a/globals.d.ts b/globals.d.ts new file mode 100644 index 0000000000..18d747f9b5 --- /dev/null +++ b/globals.d.ts @@ -0,0 +1,11 @@ +import * as Mocha from './'; + +declare global { + const after: typeof Mocha.after; + const afterEach: typeof Mocha.afterEach; + const before: typeof Mocha.after; + const beforeEach: typeof Mocha.beforeEach; + const describe: typeof Mocha.describe; + const it: typeof Mocha.after; + const xit: typeof Mocha.xit; +} diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000000..527a101fcf --- /dev/null +++ b/index.d.ts @@ -0,0 +1,2 @@ +import * as Mocha from './types/mocha'; +export = Mocha; diff --git a/lib/cli/options.js b/lib/cli/options.js index c87552c542..3227033dfd 100644 --- a/lib/cli/options.js +++ b/lib/cli/options.js @@ -165,9 +165,8 @@ const parseMochaOpts = content => /** * Given filepath in `args.opts`, attempt to load and parse a `mocha.opts` file. - * @param {Object} [args] - Arguments object - * @param {string|boolean} [args.opts] - Filepath to mocha.opts; defaults to whatever's in `mocharc.opts`, or `false` to skip - * @returns {external:yargsParser.Arguments|void} If read, object containing parsed arguments + * @param {import('./options_t').MochaArgs} [args] - Arguments object + * @returns {import('yargs-parser').Arguments | void} If read, object containing parsed arguments * @memberof module:lib/cli/options * @see {@link /#mochaopts|mocha.opts} * @public @@ -210,11 +209,10 @@ module.exports.loadMochaOpts = loadMochaOpts; /** * Given path to config file in `args.config`, attempt to load & parse config file. - * @param {Object} [args] - Arguments object - * @param {string|boolean} [args.config] - Path to config file or `false` to skip + * @param {import('./options_t').MochaArgs} [args] - Arguments object * @public * @memberof module:lib/cli/options - * @returns {external:yargsParser.Arguments|void} Parsed config, or nothing if `args.config` is `false` + * @returns {import('yargs-parser').Arguments | void} Parsed config, or nothing if `args.config` is `false` */ const loadRc = (args = {}) => { if (args.config !== false) { @@ -227,11 +225,10 @@ module.exports.loadRc = loadRc; /** * Given path to `package.json` in `args.package`, attempt to load config from `mocha` prop. - * @param {Object} [args] - Arguments object - * @param {string|boolean} [args.config] - Path to `package.json` or `false` to skip + * @param {import('./options_t').MochaArgs} [args] - Arguments object * @public * @memberof module:lib/cli/options - * @returns {external:yargsParser.Arguments|void} Parsed config, or nothing if `args.package` is `false` + * @returns {import('yargs-parser').Arguments | void} Parsed config, or nothing if `args.package` is `false` */ const loadPkgRc = (args = {}) => { let result; @@ -275,7 +272,7 @@ module.exports.loadPkgRc = loadPkgRc; * @param {string|string[]} [argv] - Arguments to parse * @public * @memberof module:lib/cli/options - * @returns {external:yargsParser.Arguments} Parsed args from everything + * @returns {import('yargs-parser').Arguments} Parsed args from everything */ const loadOptions = (argv = []) => { let args = parse(argv); diff --git a/lib/cli/options_t.ts b/lib/cli/options_t.ts new file mode 100644 index 0000000000..bbcb20f519 --- /dev/null +++ b/lib/cli/options_t.ts @@ -0,0 +1,7 @@ +export interface MochaArgs { + /** Path to `package.json` or `false` to skip */ + config?: string | false; + package?: boolean; + /** Filepath to mocha.opts; defaults to whatever's in `mocharc.opts`, or `false` to skip */ + opts?: string | false; +} diff --git a/lib/cli/run-option-metadata.js b/lib/cli/run-option-metadata.js index d0bc92ffbe..cb0ef0e87d 100644 --- a/lib/cli/run-option-metadata.js +++ b/lib/cli/run-option-metadata.js @@ -9,7 +9,7 @@ /** * Dictionary of yargs option types to list of options having said type - * @type {{string:string[]}} + * @type {Record} * @private */ exports.types = { @@ -63,7 +63,7 @@ exports.types = { /** * Option aliases keyed by canonical option name. * Arrays used to reduce - * @type {{string:string[]}} + * @type {Record} * @private */ exports.aliases = { diff --git a/lib/runnable.js b/lib/runnable.js index 2d0c428d46..0ffee5f95f 100644 --- a/lib/runnable.js +++ b/lib/runnable.js @@ -64,7 +64,7 @@ utils.inherits(Runnable, EventEmitter); * * @private * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Maximum_delay_value} - * @param {number|string} ms - Timeout threshold value. + * @param {number|string} [ms] - Timeout threshold value. * @returns {Runnable} this * @chainable */ @@ -260,13 +260,14 @@ Runnable.prototype.resetTimeout = function() { return; } this.clearTimeout(); - this.timer = setTimeout(function() { + /** @type {number} */ + this.timer = /** @type {any} */ (setTimeout(function() { if (!self._enableTimeouts) { return; } self.callback(self._timeoutError(ms)); self.timedOut = true; - }, ms); + }, ms)); }; /** diff --git a/lib/utils.js b/lib/utils.js index 805d98d463..c93e3584b1 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -29,7 +29,10 @@ var assign = (exports.assign = require('object.assign').getPolyfill()); * @throws {TypeError} if either constructor is null, or if super constructor * lacks a prototype. */ -exports.inherits = util.inherits; +exports.inherits = function(ctor, superCtor) { + // Wrapper prevents type declarations from referring to @types/node + return util.inherits.call(this, ctor, superCtor); +}; /** * Escape special characters in the given string of html. @@ -708,7 +711,7 @@ exports.isPromise = function isPromise(value) { * Clamps a numeric value to an inclusive range. * * @param {number} value - Value to be clamped. - * @param {numer[]} range - Two element array specifying [min, max] range. + * @param {number[]} range - Two element array specifying [min, max] range. * @returns {number} clamped value */ exports.clamp = function clamp(value, range) { diff --git a/package-lock.json b/package-lock.json index e66be18a00..641f9a17a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -416,9 +416,9 @@ } }, "@types/node": { - "version": "10.12.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.29.tgz", - "integrity": "sha512-J/tnbnj8HcsBgCe2apZbdUpQ7hs4d7oZNTYA5bekWdP0sr2NGsOpI/HRdDroEi209tEvTcTtxhD0FfED3DhEcw==", + "version": "13.1.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.1.8.tgz", + "integrity": "sha512-6XzyyNM9EKQW4HKuzbo/CkOIjn/evtCmsU+MUM1xDfJ+3/rNjBttM1NgN7AOQvN6tP1Sl1D1PIKMreTArnxM9A==", "dev": true }, "@types/normalize-package-data": { @@ -460,6 +460,21 @@ "@types/unist": "*" } }, + "@types/yargs": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.0.tgz", + "integrity": "sha512-TITCsNxRSDVmq3kPGuUdKZTPTfHeswsUGIjxSe8SB4EBKTPA0DO0y4yWI95kZ2hfqJAYxmu+gxzjOwdumB5S0g==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "dev": true + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -17263,9 +17278,9 @@ "dev": true }, "typescript": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.1.tgz", - "integrity": "sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==", + "version": "3.7.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", + "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", "dev": true }, "typescript-eslint-parser": { diff --git a/package-scripts.js b/package-scripts.js index 0e414e1542..0332947d66 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -23,8 +23,18 @@ function test(testName, mochaParams) { module.exports = { scripts: { build: { - script: `browserify -e browser-entry.js --plugin ./scripts/dedefine --ignore './lib/cli/*.js' --ignore 'chokidar' --ignore 'fs' --ignore 'glob' --ignore 'path' --ignore 'supports-color' -o mocha.js`, - description: 'Build browser bundle' + default: { + script: 'nps build.types build.browser', + description: 'Build' + }, + types: { + script: 'tsc -p . || true', // swallowing until I finish fixing all semantic errors + description: 'Build type declarations' + }, + browser: { + script: `browserify -e browser-entry.js --plugin ./scripts/dedefine --ignore './lib/cli/*.js' --ignore 'chokidar' --ignore 'fs' --ignore 'glob' --ignore 'path' --ignore 'supports-color' -o mocha.js`, + description: 'Build browser bundle' + } }, lint: { default: { @@ -64,7 +74,7 @@ module.exports = { }, test: { default: { - script: 'nps lint test.node test.browser test.bundle', + script: 'nps lint test.node test.browser test.bundle test.types', description: 'Run all linters and all tests' }, node: { @@ -251,6 +261,10 @@ module.exports = { description: 'Run AMD bundle tests', hiddenFromHelp: true } + }, + types: { + script: 'cd test/types && tsc -p .', + description: 'Test emitted type declarations.' } }, coveralls: { diff --git a/package.json b/package.json index aa77237f94..5ecda6f4a7 100644 --- a/package.json +++ b/package.json @@ -521,6 +521,7 @@ "mocha": "./bin/mocha", "_mocha": "./bin/_mocha" }, + "types": "types", "directories": { "lib": "./lib", "test": "./test" @@ -564,6 +565,8 @@ "@11ty/eleventy": "^0.8.3", "@mocha/contributors": "^1.0.4", "@mocha/docdash": "^2.1.2", + "@types/node": "^13.1.8", + "@types/yargs": "^15.0.0", "acorn": "^7.0.0", "assetgraph-builder": "^6.10.1", "autoprefixer": "^9.6.0", @@ -615,6 +618,7 @@ "svgo": "^1.2.2", "through2": "^3.0.1", "to-vfile": "^5.0.3", + "typescript": "^3.7.5", "unexpected": "^10.40.2", "unexpected-eventemitter": "^1.1.3", "unexpected-sinon": "^10.11.2", diff --git a/test/types/mocha-tests.ts b/test/types/mocha-tests.ts new file mode 100644 index 0000000000..5cf209c16d --- /dev/null +++ b/test/types/mocha-tests.ts @@ -0,0 +1,1287 @@ +/// + +import { + after as importedAfter, + before as importedBefore, + afterEach as importedAfterEach, + beforeEach as importedBeforeEach, + describe as importedDescribe, + it as importedIt, + xit as importedXit +} from 'mocha'; + +import LocalMocha = require('mocha'); + +// Warning!! +// Don't refer node.d.ts!! +// See #22510. +(): number => setTimeout(() => 0, 0); + +declare let number: number; +declare let boolean: boolean; +declare let string: string; +declare let stringOrUndefined: string | undefined; +declare let any: any; + +// Use module augmentation to add a third-party interface or reporter +declare module 'mocha' { + interface InterfaceContributions { + 'third-party-interface': never; + } + interface ReporterContributions { + 'third-party-reporter': never; + } +} + +const thirdPartyInterface: Mocha.Interface = 'third-party-interface'; +const thirdPartyReporter: Mocha.Reporter = 'third-party-reporter'; + +// Lazy tests of compatibility between imported and global functions; should be identical +const _after: typeof after = importedAfter; +const _after2: typeof importedAfter = after; +const _before: typeof before = importedBefore; +const _before2: typeof importedBefore = before; +const _afterEach: typeof afterEach = importedAfterEach; +const _afterEach2: typeof importedAfterEach = afterEach; +const _beforeEach: typeof beforeEach = importedBeforeEach; +const _beforeEach2: typeof importedBeforeEach = beforeEach; +const _describe: typeof describe = importedDescribe; +const _describe2: typeof importedDescribe = describe; +const _it: typeof it = importedIt; +const _it2: typeof importedIt = it; +const _xit: typeof xit = importedXit; +const _xit2: typeof importedXit = xit; + +function test_bdd_describe() { + // $ExpectType Suite + describe('something', function() { + // $ExpectType Suite + this; + }); + + // $ExpectType Suite + describe.only('something', function() { + // $ExpectType Suite + this; + }); + + // $ExpectType void | Suite + describe.skip('something', function() { + // $ExpectType Suite + this; + }); +} + +function test_bdd_context() { + // $ExpectType Suite + context('something', function() { + // $ExpectType Suite + this; + }); + + // $ExpectType Suite + context.only('something', function() { + // $ExpectType Suite + this; + }); + + // $ExpectType void | Suite + context.skip('something', function() { + // $ExpectType Suite + this; + }); +} + +function test_bdd_xdescribe() { + // $ExpectType void | Suite + xdescribe('something', function() { + // $ExpectType Suite + this; + }); +} + +function test_bdd_xcontext() { + // $ExpectType void | Suite + xcontext('something', function() { + // $ExpectType Suite + this; + }); +} + +function test_tdd_suite() { + // $ExpectType Suite + suite('something', function() { + // $ExpectType Suite + this; + }); + + // $ExpectType Suite + suite.only('something', function() { + // $ExpectType Suite + this; + }); + + // $ExpectType void | Suite + suite.skip('something', function() { + // $ExpectType Suite + this; + }); +} + +function test_qunit_suite() { + // $ExpectType Suite + suite('some context'); + + // $ExpectType Suite + suite.only('some context'); +} + +function test_bdd_it() { + // $ExpectType Test + it(function doesSomething(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + it(async function doesSomething() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + it('does something', function(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + it('does something', async function() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + it.only(function doesSomething(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + it.only(async function doesSomething() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + it.only('does something', function(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + it.only('does something', async function() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + it.skip(function doesSomething(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + it.skip(async function doesSomething() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + it.skip('does something', function(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + it.skip('does something', async function() { + // $ExpectType Context + this; + }); + + // $ExpectType void + it.retries(number); +} + +function test_bdd_xit() { + // $ExpectType Test + xit(function doesSomething(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + xit(async function doesSomething() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + xit('does something', function(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + xit('does something', async function() { + // $ExpectType Context + this; + }); +} + +function test_bdd_specify() { + // $ExpectType Test + specify(function doesSomething(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + specify(async function doesSomething() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + specify('does something', function(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + specify('does something', async function() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + specify.only(function doesSomething(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + specify.only(async function doesSomething() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + specify.only('does something', function(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + specify.only('does something', async function() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + specify.skip(function doesSomething(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + specify.skip(async function doesSomething() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + specify.skip('does something', function(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + specify.skip('does something', async function() { + // $ExpectType Context + this; + }); + + // $ExpectType void + specify.retries(number); +} + +function test_bdd_xspecify() { + // $ExpectType Test + xspecify(function doesSomething(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + xspecify(async function doesSomething() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + xspecify('does something', function(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + xspecify('does something', async function() { + // $ExpectType Context + this; + }); +} + +function test_tdd_qunit_test() { + // $ExpectType Test + test(function doesSomething(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + test(async function doesSomething() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + test('does something', function(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + test('does something', async function() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + test.only(function doesSomething(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + test.only(async function doesSomething() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + test.only('does something', function(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + test.only('does something', async function() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + test.skip(function doesSomething(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + test.skip(async function doesSomething() { + // $ExpectType Context + this; + }); + + // $ExpectType Test + test.skip('does something', function(done) { + // $ExpectType Done + done; + + // $ExpectType Context + this; + }); + + // $ExpectType Test + test.skip('does something', async function() { + // $ExpectType Context + this; + }); + + // $ExpectType void + test.retries(number); +} + +function test_bdd_qunit_before() { + before(function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + before(async function() { + // $ExpectType Context + this; + }); + + before('description', function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + before('description', async function() { + // $ExpectType Context + this; + }); +} + +function test_tdd_setup() { + setup(function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + setup(async function() { + // $ExpectType Context + this; + }); + + setup('description', function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + setup('description', async function() { + // $ExpectType Context + this; + }); +} + +function test_bdd_qunit_after() { + after(function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + after(async function() { + // $ExpectType Context + this; + }); + + after('description', function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + after('description', async function() { + // $ExpectType Context + this; + }); +} + +function test_tdd_teardown() { + teardown(function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + teardown(async function() { + // $ExpectType Context + this; + }); + + teardown('description', function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + teardown('description', async function() { + // $ExpectType Context + this; + }); +} + +function test_bdd_qunit_beforeEach() { + beforeEach(function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + beforeEach(async function() { + // $ExpectType Context + this; + }); + + beforeEach('description', function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + beforeEach('description', async function() { + // $ExpectType Context + this; + }); +} + +function test_tdd_suiteSetup() { + suiteSetup(function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + suiteSetup(async function() { + // $ExpectType Context + this; + }); + + suiteSetup('description', function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + suiteSetup('description', async function() { + // $ExpectType Context + this; + }); +} + +function test_bdd_qunit_afterEach() { + afterEach(function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + afterEach(async function() { + // $ExpectType Context + this; + }); + + afterEach('description', function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + afterEach('description', async function() { + // $ExpectType Context + this; + }); +} + +function test_tdd_suiteTeardown() { + suiteTeardown(function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + suiteTeardown(async function() { + // $ExpectType Context + this; + }); + + suiteTeardown('description', function(done) { + // $ExpectType Done + done; + // $ExpectType Context + this; + }); + + suiteTeardown('description', async function() { + // $ExpectType Context + this; + }); +} + +function test_Context(ctx: LocalMocha.Context, runnable: LocalMocha.Runnable) { + // $ExpectType never + ctx.skip(); // throws + + // $ExpectType boolean + ctx.enableTimeouts(); + + // $ExpectType Context + ctx.enableTimeouts(boolean); + + // $ExpectType number + ctx.retries(); + + // $ExpectType Context + ctx.retries(number); + + // $ExpectType Runnable + ctx.runnable(); + + // $ExpectType Context + ctx.runnable(runnable); + + // $ExpectType number + ctx.slow(); + + // $ExpectType Context + ctx.slow(number); + + // $ExpectType number + ctx.timeout(); + + // $ExpectType Context + ctx.timeout(number); + + // $ExpectType Test | undefined + ctx.currentTest; + + // $ExpectType Runnable | undefined + ctx.test; + + ctx["extended"] = any; + + // $ExpectType any + ctx["extended"]; + + ctx.enableTimeouts(boolean) + .retries(number) + .runnable(runnable) + .slow(number) + .timeout(number) + .skip(); +} + +function test_reporter_string(localMocha: LocalMocha) { + // $ExpectType BrowserMocha + mocha.reporter('html'); + + const m: Mocha = localMocha.reporter('html'); +} + +function test_reporter_function(localMocha: LocalMocha) { + // $ExpectType BrowserMocha + mocha.reporter(class extends LocalMocha.reporters.Base { }); + + const m: Mocha = localMocha.reporter(class extends LocalMocha.reporters.Base { }); +} + +function test_browser_mocha_setup_slow_option() { + // $ExpectType BrowserMocha + mocha.setup({ slow: 25 }); +} + +function test_browser_mocha_setup_timeout_option() { + // $ExpectType BrowserMocha + mocha.setup({ timeout: 25 }); +} + +function test_browser_mocha_setup_globals_option() { + // $ExpectType BrowserMocha + mocha.setup({ globals: ['mocha'] }); +} + +function test_browser_mocha_setup_ui_option() { + // $ExpectType BrowserMocha + mocha.setup({ ui: 'bdd' }); +} + +function test_browser_mocha_setup_reporter_string_option() { + // $ExpectType BrowserMocha + mocha.setup({ reporter: 'html' }); +} + +function test_browser_mocha_setup_require_stringArray_option() { + // $ExpectType BrowserMocha + mocha.setup({ require: ['ts-node/register'] }); +} + +function test_browser_mocha_setup_reporter_function_option() { + // $ExpectType BrowserMocha + mocha.setup({ reporter: class extends LocalMocha.reporters.Base { } }); +} + +function test_browser_mocha_setup_bail_option() { + // $ExpectType BrowserMocha + mocha.setup({ bail: false }); +} + +function test_browser_mocha_setup_ignore_leaks_option() { + // $ExpectType BrowserMocha + mocha.setup({ ignoreLeaks: false }); +} + +function test_browser_mocha_setup_grep_string_option() { + // $ExpectType BrowserMocha + mocha.setup({ grep: "describe" }); +} + +function test_browser_mocha_setup_grep_regex_option() { + // $ExpectType BrowserMocha + mocha.setup({ grep: new RegExp('describe') }); +} + +function test_browser_mocha_setup_grep_regex_literal_option() { + // $ExpectType BrowserMocha + mocha.setup({ grep: /(expect|should)/i }); +} + +function test_browser_mocha_setup_all_options() { + // $ExpectType BrowserMocha + mocha.setup({ + slow: 25, + timeout: 25, + ui: 'bdd', + globals: ['mocha'], + reporter: 'html', + bail: true, + ignoreLeaks: true, + grep: 'test', + require: ['ts-node/register'] // TODO: It doesn't appear this is actually supported. Should it be removed? + }); +} + +function test_constructor_slow_option() { + const m: Mocha = new LocalMocha({ slow: 25 }); +} + +function test_constructor_timeout_option() { + const m: Mocha = new LocalMocha({ timeout: 25 }); +} + +function test_constructor_globals_option() { + const m: Mocha = new LocalMocha({ globals: ['mocha'] }); +} + +function test_constructor_ui_option() { + const m: Mocha = new LocalMocha({ ui: 'bdd' }); +} + +function test_constructor_reporter_string_option() { + const m: Mocha = new LocalMocha({ reporter: 'html' }); +} + +function test_constructor_reporter_function_option() { + const m: Mocha = new LocalMocha({ reporter: class extends LocalMocha.reporters.Base { } }); +} + +function test_constructor_bail_option() { + const m: Mocha = new LocalMocha({ bail: false }); +} + +function test_constructor_ignore_leaks_option() { + const m: Mocha = new LocalMocha({ ignoreLeaks: false }); +} + +function test_constructor_grep_string_option() { + const m: Mocha = new LocalMocha({ grep: "describe" }); +} + +function test_constructor_grep_regex_option() { + const m: Mocha = new LocalMocha({ grep: new RegExp('describe') }); +} + +function test_constructor_grep_regex_literal_option() { + const m: Mocha = new LocalMocha({ grep: /(expect|should)/i }); +} + +function test_constructor_all_options() { + const m: Mocha = new LocalMocha({ + slow: 25, + timeout: 25, + ui: 'bdd', + globals: ['mocha'], + reporter: 'html', + bail: true, + ignoreLeaks: true, + grep: 'test' + }); +} + +function test_run(localMocha: LocalMocha) { + // $ExpectType Runner + mocha.run(); + + // $ExpectType Runner + mocha.run((failures) => { + // $ExpectType number + failures; + }); + + // $ExpectType Runner + localMocha.run(); + + // $ExpectType Runner + localMocha.run((failures) => { + // $ExpectType number + failures; + }); +} + +function test_growl() { + mocha.growl(); +} + +function test_chaining() { + new LocalMocha({ slow: 25 }) + .growl() + .reporter('html') + .reporter(class extends LocalMocha.reporters.Base { }); +} + +function test_require_constructor_empty() { + const instance = new LocalMocha(); +} + +function test_require_constructor_noOptions() { + const instance = new LocalMocha({}); +} + +function test_require_constructor_allOptions() { + const instance = new LocalMocha({ + grep: /[a-z]*/, + ui: 'tdd', + reporter: 'dot', + timeout: 500, + bail: true + }); +} + +function test_require_fluentParams() { + const instance = new LocalMocha(); + + instance.bail(true) + .bail() + .addFile('foo.js') + .reporter('dot') + .ui('bdd') + .grep('[a-z]*') + .grep(/[a-z]*/) + .invert() + .ignoreLeaks(true) + .checkLeaks() + .growl() + .globals('foo') + .globals(['bar', 'zap']) + .useColors(true) + .useInlineDiffs(true) + .timeout(500) + .slow(100) + .enableTimeouts(true) + .asyncOnly() + .noHighlighting() + .run(); +} + +function test_throwError() { + mocha.throwError(new Error("I'm an error!")); +} + +function test_mochaRunner_properties(runner: LocalMocha.Runner, suite: LocalMocha.Suite) { + // $Expecttype Runner + runner.abort(); + + // $ExpectType Suite + runner.suite; + + // $ExpectType boolean + runner.started; + + // $ExpectType number + runner.total; + + // $ExpectType number + runner.failures; + + // $ExpectType Runner + runner.grep(/regex/, false); + + // $ExpectType number + runner.grepTotal(suite); + + // $ExpectType string[] + runner.globals(); + + // $ExpectType Runner + runner.globals(["hello", "world"]); + + // $ExpectType Runner + runner.run(); + + // $ExpectType Runner + runner.run((failures) => { + // $ExpectType number + failures; + }); +} + +function test_base_reporter_properties(reporter: LocalMocha.reporters.Base) { + // $ExpectType number + reporter.stats.failures; + + // $ExpectType number + reporter.stats.passes; + + // $ExpectType number + reporter.stats.pending; + + // $ExpectType number + reporter.stats.suites; + + // $ExpectType number + reporter.stats.tests; + + // $ExpectType Date | undefined + reporter.stats.start; + + // $ExpectType Date | undefined + reporter.stats.end; + + // $ExpectType number | undefined + reporter.stats.duration; +} + +function test_runner_events(runner: LocalMocha.Runner) { + // $ExpectType Runner + runner.on("start", () => {}); + + // $ExpectType Runner + runner.on("end", () => {}); + + // $ExpectType Runner + runner.on("suite", (suite) => { + // $ExpectType Suite + suite; + }); + + // $ExpectType Runner + runner.on("suite end", (suite) => { + // $ExpectType Suite + suite; + }); + + // $ExpectType Runner + runner.on("test", (test) => { + // $ExpectType Test + test; + }); + + // $ExpectType Runner + runner.on("test end", (test) => { + // $ExpectType Test + test; + }); + + // $ExpectType Runner + runner.on("hook", (hook) => { + // $ExpectType Hook + hook; + }); + + // $ExpectType Runner + runner.on("hook end", (hook) => { + // $ExpectType Hook + hook; + }); + + // $ExpectType Runner + runner.on("pass", (test) => { + // $ExpectType Test + test; + }); + + // $ExpectType Runner + runner.on("fail", (test, err) => { + // $ExpectType Test + test; + + // $ExpectType any + err; + }); + + // $ExpectType Runner + runner.on("pending", (test) => { + // $ExpectType Test + test; + }); +} + +function test_runnable_events(runnable: LocalMocha.Runnable) { + // $ExpectType Runnable + runnable.on("error", (error) => { + // $ExpectType any + error; + }); +} + +function test_suite_events(suite: LocalMocha.Suite) { + // $ExpectType Suite + suite.on("beforeAll", (hook) => { + // $ExpectType Hook + hook; + }); + + // $ExpectType Suite + suite.on("afterAll", (hook) => { + // $ExpectType Hook + hook; + }); + + // $ExpectType Suite + suite.on("beforeEach", (hook) => { + // $ExpectType Hook + hook; + }); + + // $ExpectType Suite + suite.on("afterEach", (hook) => { + // $ExpectType Hook + hook; + }); + + // $ExpectType Suite + suite.on("run", () => { }); + + // $ExpectType Suite + suite.on("pre-require", (context, file, mocha) => { + // $ExpectType MochaGlobals + context; + // $ExpectType string + file; + const m: Mocha = mocha; + }); + + // $ExpectType Suite + suite.on("require", (module, file, mocha) => { + // $ExpectType any + module; + // $ExpectType string + file; + const m: Mocha = mocha; + }); + + // $ExpectType Suite + suite.on("post-require", (context, file, mocha) => { + // $ExpectType MochaGlobals + context; + // $ExpectType string + file; + const m: Mocha = mocha; + }); +} + +function test_backcompat_Suite(suite: Mocha.Suite, iSuite: Mocha.ISuite, iSuiteContext: Mocha.ISuiteCallbackContext, iTest: Mocha.ITest, iContext: Mocha.IContext) { + iSuite = suite; + iSuiteContext = suite; + suite.addTest(iTest); + suite.addSuite(iSuite); + LocalMocha.Suite.create(iSuite, string); + new LocalMocha.Suite(string, iContext); +} + +function test_backcompat_Runner(runner: Mocha.Runner, iRunner: Mocha.IRunner, iSuite: Mocha.ISuite) { + iRunner = runner; + runner.grepTotal(iSuite); +} + +function test_backcompat_Runnable(runnable: Mocha.Runnable, iRunnable: Mocha.IRunnable) { + iRunnable = runnable; +} + +function test_backcompat_Test(test: Mocha.Test, iTest: Mocha.ITest) { + iTest = test; +} + +function test_backcompat_Hook(hook: Mocha.Hook, iHook: Mocha.IHook) { + iHook = hook; +} + +function test_backcompat_Context(context: Mocha.Context, iContext: Mocha.IContext, + iHookContext: Mocha.IHookCallbackContext, iBeforeAfterContext: Mocha.IBeforeAndAfterContext, + iTestContext: Mocha.ITestCallbackContext, iRunnable: Mocha.IRunnable) { + iContext = context; + iHookContext = context; + iBeforeAfterContext = context; + iTestContext = context; + context.runnable(iRunnable); +} + +function test_backcompat_Base(iRunner: Mocha.IRunner) { + new LocalMocha.reporters.Base(iRunner); +} + +function test_backcompat_XUnit(iRunner: Mocha.IRunner) { + new LocalMocha.reporters.XUnit(iRunner); +} + +function test_backcompat_Progress(iRunner: Mocha.IRunner) { + new LocalMocha.reporters.Progress(iRunner); +} + +import common = require("mocha/lib/interfaces/common"); + +function test_interfaces_common(suites: Mocha.Suite[], context: Mocha.MochaGlobals, localMocha: Mocha, + fn: Mocha.Func | Mocha.AsyncFunc, test: Mocha.Test) { + const funcs = common(suites, context, localMocha); + // $ExpectType CommonFunctions + funcs; + + funcs.before(fn); + funcs.before(string, fn); + funcs.beforeEach(fn); + funcs.beforeEach(string, fn); + funcs.after(fn); + funcs.after(string, fn); + funcs.afterEach(fn); + funcs.afterEach(string, fn); + + // $ExpectType Suite + funcs.suite.create({ title: string }); + funcs.suite.create({ title: string, file: string, fn: () => {}, pending: boolean, isOnly: boolean }); + + // $ExpectType Suite + funcs.suite.only({ title: string }); + funcs.suite.only({ title: string, file: string, fn: () => {}, pending: boolean, isOnly: boolean }); + + // $ExpectType Suite + funcs.suite.skip({ title: string }); + funcs.suite.skip({ title: string, file: string, fn: () => {}, pending: boolean, isOnly: boolean }); + + // $ExpectType Test + funcs.test.only(mocha, test); + funcs.test.skip(string); + funcs.test.retries(number); +} + +// mocha-typescript (https://www.npmjs.com/package/mocha-typescript/) augments +// the mocha functions and enables them to work as test class decorators. +declare module "mocha" { + interface SuiteFunction { + (target: TFunction): TFunction | void; + } + interface PendingSuiteFunction { + (target: TFunction): TFunction | void; + } + interface ExclusiveSuiteFunction { + (target: TFunction): TFunction | void; + } + interface TestFunction { + (target: Object, propertyKey: string | symbol): void; + } + interface PendingTestFunction { + (target: Object, propertyKey: string | symbol): void; + } + interface ExclusiveTestFunction { + (target: Object, propertyKey: string | symbol): void; + } +} + +@suite +class TestClass1 { + @test method1() {} + @test.only method2() {} + @test.skip method3() {} +} + +@suite.skip +class TestClass2 { +} + +@suite.only +class TestClass3 { +} +// end of augmentations used by mocha-typescript diff --git a/test/types/tsconfig.json b/test/types/tsconfig.json new file mode 100644 index 0000000000..474fc7ccab --- /dev/null +++ b/test/types/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "noEmit": true, + "experimentalDecorators": true, + "types": [], + "target": "ES2015", + "module": "CommonJS" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..09b5083e0a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "include": ["lib"], + "compilerOptions": { + "rootDir": "lib", + "declarationDir": "types", + "allowJs": true, + "checkJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "sourceMap": true, + "declarationMap": true, + "newLine": "lf", + + "target": "ES2015", + "types": ["node"] + } +}