-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
12ce5d7
commit c57d344
Showing
21 changed files
with
3,127 additions
and
746 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/* | ||
* @japa/runner | ||
* | ||
* (c) Japa | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
import { | ||
Emitter, | ||
Refiner, | ||
Test as BaseTest, | ||
Suite as BaseSuite, | ||
Group as BaseGroup, | ||
Runner as BaseRunner, | ||
TestContext as BaseTestContext, | ||
} from '@japa/core' | ||
import type { DataSetNode, TestHooksCleanupHandler } from '@japa/core/types' | ||
|
||
export { Emitter, Refiner } | ||
|
||
/** | ||
* Test context carries context data for a given test. | ||
*/ | ||
export class TestContext extends BaseTestContext { | ||
/** | ||
* Register a cleanup function that runs after the test finishes | ||
* successfully or with an error. | ||
*/ | ||
declare cleanup: (cleanupCallback: TestHooksCleanupHandler<TestContext>) => void | ||
|
||
constructor(public test: Test) { | ||
super() | ||
this.cleanup = (cleanupCallback: TestHooksCleanupHandler<TestContext>) => { | ||
test.cleanup(cleanupCallback) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Test class represents an individual test and exposes API to tweak | ||
* its runtime behavior. | ||
*/ | ||
export class Test<TestData extends DataSetNode = undefined> extends BaseTest< | ||
TestContext, | ||
TestData | ||
> { | ||
/** | ||
* @inheritdoc | ||
*/ | ||
static disposeCallbacks = [] | ||
} | ||
|
||
/** | ||
* TestGroup is used to bulk configure a collection of tests and | ||
* define lifecycle hooks for them | ||
*/ | ||
export class Group extends BaseGroup<TestContext> {} | ||
|
||
/** | ||
* A suite is a collection of tests created around a given | ||
* testing type. For example: A suite for unit tests, a | ||
* suite for functional tests and so on. | ||
*/ | ||
export class Suite extends BaseSuite<TestContext> {} | ||
|
||
/** | ||
* Runner class is used to execute the tests | ||
*/ | ||
export class Runner extends BaseRunner<TestContext> {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* | ||
* @japa/runner | ||
* | ||
* (c) Japa | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
// @ts-ignore-error | ||
import getopts from 'getopts' | ||
import { colors } from '@poppinss/cliui' | ||
import type { CLIArgs } from './types.js' | ||
|
||
const ansi = colors.ansi() | ||
|
||
/** | ||
* Known commandline options. The user can still define additional flags and they | ||
* will be parsed aswell, but without any normalization | ||
*/ | ||
const OPTIONS = { | ||
string: ['tests', 'groups', 'tags', 'files', 'timeout', 'retries', 'reporter'], | ||
boolean: ['forceExit', 'help', 'matchAll'], | ||
alias: { | ||
forceExit: 'force-exit', | ||
matchAll: 'match-all', | ||
help: 'h', | ||
}, | ||
} | ||
|
||
/** | ||
* Help string to display when the `--help flag is used` | ||
*/ | ||
const GET_HELP = () => ` | ||
${ansi.yellow('@japa/runner v2.3.0')} | ||
${ansi.green('--tests')} ${ansi.dim('Filter by test titles')} | ||
${ansi.green('--groups')} ${ansi.dim('Filter by group titles')} | ||
${ansi.green('--tags')} ${ansi.dim('Filter by test tags')} | ||
${ansi.green('--files')} ${ansi.dim('Filter by tests file name')} | ||
${ansi.green('--force-exit')} ${ansi.dim('Forcefully exit the process')} | ||
${ansi.green('--timeout')} ${ansi.dim('Define global timeout for tests')} | ||
${ansi.green('--retries')} ${ansi.dim('Define global retries for tests')} | ||
${ansi.green('--reporter')} ${ansi.dim('Define reporter(s) to use')} | ||
${ansi.green('-h, --help')} ${ansi.dim('View commandline help')} | ||
${ansi.yellow('Examples:')} | ||
${ansi.dim('node bin/test.js --tags="@github"')} | ||
${ansi.dim('node bin/test.js --tags="~@github"')} | ||
${ansi.dim('node bin/test.js --tags="@github,@slow,@integration" --match-all')} | ||
${ansi.dim('node bin/test.js --force-exit')} | ||
${ansi.dim('node bin/test.js --files="user"')} | ||
${ansi.dim('node bin/test.js --files="functional/user"')} | ||
${ansi.dim('node bin/test.js --files="unit/user"')} | ||
${ansi.yellow('Notes:')} | ||
- When groups and tests filters are applied together. We will first filter the | ||
tests by group title and then apply the tests title filter. | ||
- The timeout defined on test object takes precedence over the ${ansi.green('--timeout')} flag. | ||
- The retries defined on test object takes precedence over the ${ansi.green('--retries')} flag. | ||
- The ${ansi.green('--files')} flag checks for the file names ending with the filter substring. | ||
` | ||
|
||
/** | ||
* CLI Parser is used to parse the commandline argument | ||
*/ | ||
export class CliParser { | ||
#argv: string[] | ||
|
||
constructor(argv: string[]) { | ||
this.#argv = argv | ||
} | ||
|
||
/** | ||
* Parses command-line arguments | ||
*/ | ||
parse(): CLIArgs { | ||
return getopts(this.#argv, OPTIONS) | ||
} | ||
|
||
/** | ||
* Returns the help string | ||
*/ | ||
getHelp() { | ||
return GET_HELP() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
/* | ||
* @japa/runner | ||
* | ||
* (c) Japa | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
import debug from './debug.js' | ||
import { Refiner } from '../modules/core/main.js' | ||
import type { CLIArgs, Config, Filters } from './types.js' | ||
|
||
/** | ||
* Defaults to use for configuration | ||
*/ | ||
const DEFAULTS = { | ||
files: [], | ||
timeout: 2000, | ||
retries: 1, | ||
forceExit: false, | ||
plugins: [], | ||
reporters: { | ||
activated: ['spec'], | ||
list: [{ name: 'spec', handler: {} as any }], | ||
}, | ||
importer: (filePath) => import(filePath.href), | ||
configureSuite: () => {}, | ||
} satisfies Config | ||
|
||
/** | ||
* Config manager is used to hydrate the configuration by merging | ||
* the defaults, user defined config and the command line | ||
* flags. | ||
* | ||
* The command line flags have the upmost priority | ||
*/ | ||
export class ConfigManager { | ||
#config: Config | ||
#cliArgs: CLIArgs | ||
|
||
constructor(config: Config, cliArgs: CLIArgs) { | ||
this.#config = config | ||
this.#cliArgs = cliArgs | ||
} | ||
|
||
/** | ||
* Processes a CLI argument and converts it to an | ||
* array of strings | ||
*/ | ||
#processAsArray(value: string | string[]): string[] { | ||
return Array.isArray(value) ? value : value.split(',').map((item: string) => item.trim()) | ||
} | ||
|
||
/** | ||
* Returns a copy of filters based upon the CLI | ||
* arguments. | ||
*/ | ||
#getCLIFilters(): Filters { | ||
const filters: Filters = {} | ||
|
||
if (this.#cliArgs.tags) { | ||
filters.tags = this.#processAsArray(this.#cliArgs.tags) | ||
} | ||
if (this.#cliArgs.tests) { | ||
filters.tests = this.#processAsArray(this.#cliArgs.tests) | ||
} | ||
if (this.#cliArgs.files) { | ||
filters.files = this.#processAsArray(this.#cliArgs.files) | ||
} | ||
if (this.#cliArgs.groups) { | ||
filters.groups = this.#processAsArray(this.#cliArgs.groups) | ||
} | ||
if (this.#cliArgs._ && this.#cliArgs._.length) { | ||
filters.suites = this.#processAsArray(this.#cliArgs._) | ||
} | ||
|
||
return filters | ||
} | ||
|
||
/** | ||
* Returns the timeout from the CLI args | ||
*/ | ||
#getCLITimeout(): number | undefined { | ||
if (this.#cliArgs.timeout) { | ||
const value = Number(this.#cliArgs.timeout) | ||
if (!Number.isNaN(value)) { | ||
return value | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Returns the retries from the CLI args | ||
*/ | ||
#getCLIRetries(): number | undefined { | ||
if (this.#cliArgs.retries) { | ||
const value = Number(this.#cliArgs.retries) | ||
if (!Number.isNaN(value)) { | ||
return value | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Returns reporters selected using the commandline | ||
* --reporter flag | ||
*/ | ||
#getCLIReporters(): string[] | undefined { | ||
if (this.#cliArgs.reporter) { | ||
return this.#processAsArray(this.#cliArgs.reporter) | ||
} | ||
} | ||
|
||
/** | ||
* Hydrates the config with user defined options and the | ||
* command-line flags. | ||
*/ | ||
hydrate(): Required<Config> { | ||
const cliFilters = this.#getCLIFilters() | ||
const cliRetries = this.#getCLIRetries() | ||
const cliTimeout = this.#getCLITimeout() | ||
const cliReporters = this.#getCLIReporters() | ||
|
||
debug('filters applied using CLI flags %O', cliFilters) | ||
|
||
const baseConfig: Omit<Required<Config>, 'files' | 'suites'> = { | ||
cwd: this.#config.cwd ?? process.cwd(), | ||
filters: Object.assign({}, this.#config.filters ?? {}, cliFilters), | ||
importer: this.#config.importer ?? DEFAULTS.importer, | ||
refiner: this.#config.refiner ?? new Refiner(), | ||
retries: cliRetries ?? this.#config.retries ?? DEFAULTS.retries, | ||
timeout: cliTimeout ?? this.#config.timeout ?? DEFAULTS.timeout, | ||
plugins: this.#config.plugins ?? DEFAULTS.plugins, | ||
forceExit: this.#config.forceExit ?? DEFAULTS.forceExit, | ||
reporters: this.#config.reporters ?? DEFAULTS.reporters, | ||
configureSuite: this.#config.configureSuite ?? DEFAULTS.configureSuite, | ||
setup: this.#config.setup || [], | ||
teardown: this.#config.teardown || [], | ||
} | ||
|
||
/** | ||
* Overwrite activated reporters when defined using CLI | ||
* flag | ||
*/ | ||
if (cliReporters) { | ||
baseConfig.reporters.activated = cliReporters | ||
} | ||
|
||
if ('files' in this.#config) { | ||
return { | ||
files: this.#config.files, | ||
...baseConfig, | ||
} | ||
} | ||
|
||
return { | ||
suites: this.#config.suites.map((suite) => { | ||
return { | ||
name: suite.name, | ||
files: suite.files, | ||
timeout: cliTimeout ?? suite.timeout ?? baseConfig.timeout, | ||
retries: cliRetries ?? suite.retries ?? baseConfig.retries, | ||
configure: suite.configure, | ||
} | ||
}), | ||
...baseConfig, | ||
} | ||
} | ||
} |
Oops, something went wrong.