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

Allowed describe and it/test to take in functions again #12484

Merged
merged 18 commits into from Feb 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

- `[babel-jest]` Export `createTransformer` function ([#12399](https://github.com/facebook/jest/pull/12399))
- `[expect]` Expose `AsymmetricMatchers`, `MatcherFunction` and `MatcherFunctionWithState` interfaces ([#12363](https://github.com/facebook/jest/pull/12363), [#12376](https://github.com/facebook/jest/pull/12376))
- `[jest-circus, jest-jasmine2]` Allowed classes and functions as `describe` and `it`/`test` names ([#12484](https://github.com/facebook/jest/pull/12484))
- `[jest-cli, jest-config]` [**BREAKING**] Remove `testURL` config, use `testEnvironmentOptions.url` instead ([#10797](https://github.com/facebook/jest/pull/10797))
- `[jest-config]` [**BREAKING**] Stop shipping `jest-environment-jsdom` by default ([#12354](https://github.com/facebook/jest/pull/12354))
- `[jest-config]` [**BREAKING**] Stop shipping `jest-jasmine2` by default ([#12355](https://github.com/facebook/jest/pull/12355))
Expand Down
17 changes: 15 additions & 2 deletions e2e/__tests__/__snapshots__/globals.test.ts.snap
Expand Up @@ -81,13 +81,26 @@ Time: <<REPLACED>>
Ran all test suites."
`;

exports[`function as descriptor 1`] = `
exports[`function as describe() descriptor 1`] = `
"PASS __tests__/functionAsDescriptor.test.js
Foo
✓ it"
`;

exports[`function as descriptor 2`] = `
exports[`function as describe() descriptor 2`] = `
"Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: <<REPLACED>>
Ran all test suites."
`;

exports[`function as it() descriptor 1`] = `
"PASS __tests__/functionAsDescriptor.test.js
✓ Foo"
`;

exports[`function as it() descriptor 2`] = `
"Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Expand Down
18 changes: 17 additions & 1 deletion e2e/__tests__/globals.test.ts
Expand Up @@ -262,7 +262,7 @@ test('cannot test with no implementation with expand arg', () => {
expect(exitCode).toBe(1);
});

test('function as descriptor', () => {
test('function as describe() descriptor', () => {
const filename = 'functionAsDescriptor.test.js';
const content = `
function Foo() {}
Expand All @@ -279,3 +279,19 @@ test('function as descriptor', () => {
expect(summary).toMatchSnapshot();
expect(exitCode).toBe(0);
});

test('function as it() descriptor', () => {
const filename = 'functionAsDescriptor.test.js';
const content = `
function Foo() {}
it(Foo, () => {});
`;

writeFiles(TEST_DIR, {[filename]: content});
const {stderr, exitCode} = runJest(DIR);

const {summary, rest} = extractSummary(stderr);
expect(rest).toMatchSnapshot();
expect(summary).toMatchSnapshot();
expect(exitCode).toBe(0);
});
Expand Up @@ -33,6 +33,24 @@ run_finish
unhandledErrors: 0"
`;

exports[`function descriptors 1`] = `
"start_describe_definition: describer
add_test: One
finish_describe_definition: describer
run_start
run_describe_start: ROOT_DESCRIBE_BLOCK
run_describe_start: describer
test_start: One
test_fn_start: One
test_fn_success: One
test_done: One
run_describe_finish: describer
run_describe_finish: ROOT_DESCRIBE_BLOCK
run_finish

unhandledErrors: 0"
`;

exports[`simple test 1`] = `
"start_describe_definition: describe
add_hook: beforeEach
Expand Down
10 changes: 10 additions & 0 deletions packages/jest-circus/src/__tests__/baseTest.test.ts
Expand Up @@ -20,6 +20,16 @@ test('simple test', () => {
expect(stdout).toMatchSnapshot();
});

test('function descriptors', () => {
const {stdout} = runTest(`
describe(function describer() {}, () => {
test(class One {}, () => {});
})
`);

expect(stdout).toMatchSnapshot();
});

test('failures', () => {
const {stdout} = runTest(`
describe('describe', () => {
Expand Down
10 changes: 7 additions & 3 deletions packages/jest-circus/src/__tests__/circusItTestError.test.ts
Expand Up @@ -35,11 +35,13 @@ describe('test/it error throwing', () => {
'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.',
);
});
it("it throws an error when first argument isn't a string", () => {
it("it throws an error when first argument isn't valid", () => {
expect(() => {
// @ts-expect-error: Easy, we're testing runtime errors here
circusIt(() => {});
}).toThrowError('Invalid first argument, () => {}. It must be a string.');
}).toThrowError(
'Invalid first argument, () => {}. It must be a named class, named function, number, or string.',
);
});
it('it throws an error when callback function is not a function', () => {
expect(() => {
Expand All @@ -66,7 +68,9 @@ describe('test/it error throwing', () => {
expect(() => {
// @ts-expect-error: Easy, we're testing runtime errors here
circusTest(() => {});
}).toThrowError('Invalid first argument, () => {}. It must be a string.');
}).toThrowError(
'Invalid first argument, () => {}. It must be a named class, named function, number, or string.',
);
});
it('test throws an error when callback function is not a function', () => {
expect(() => {
Expand Down
39 changes: 24 additions & 15 deletions packages/jest-circus/src/index.ts
Expand Up @@ -7,24 +7,24 @@

import type {Circus, Global} from '@jest/types';
import {bind as bindEach} from 'jest-each';
import {ErrorWithStack, isPromise} from 'jest-util';
import {ErrorWithStack, convertDescriptorToString, isPromise} from 'jest-util';
import {dispatchSync} from './state';

export {setState, getState, resetState} from './state';
export {default as run} from './run';

type THook = (fn: Circus.HookFn, timeout?: number) => void;
type DescribeFn = (
blockName: Circus.BlockName,
blockName: Circus.BlockNameLike,
blockFn: Circus.BlockFn,
) => void;

const describe = (() => {
const describe = (blockName: Circus.BlockName, blockFn: Circus.BlockFn) =>
const describe = (blockName: Circus.BlockNameLike, blockFn: Circus.BlockFn) =>
_dispatchDescribe(blockFn, blockName, describe);
const only = (blockName: Circus.BlockName, blockFn: Circus.BlockFn) =>
const only = (blockName: Circus.BlockNameLike, blockFn: Circus.BlockFn) =>
_dispatchDescribe(blockFn, blockName, only, 'only');
const skip = (blockName: Circus.BlockName, blockFn: Circus.BlockFn) =>
const skip = (blockName: Circus.BlockNameLike, blockFn: Circus.BlockFn) =>
_dispatchDescribe(blockFn, blockName, skip, 'skip');

describe.each = bindEach(describe, false);
Expand All @@ -40,7 +40,7 @@ const describe = (() => {

const _dispatchDescribe = (
blockFn: Circus.BlockFn,
blockName: Circus.BlockName,
blockName: Circus.BlockNameLike,
describeFn: DescribeFn,
mode?: Circus.BlockMode,
) => {
Expand All @@ -54,6 +54,13 @@ const _dispatchDescribe = (
asyncError.message = `Invalid second argument, ${blockFn}. It must be a callback function.`;
throw asyncError;
}
try {
blockName = convertDescriptorToString(blockName);
} catch (error) {
asyncError.message = (error as Error).message;
throw asyncError;
}

dispatchSync({
asyncError,
blockName,
Expand Down Expand Up @@ -107,22 +114,22 @@ const afterAll: THook = (fn, timeout) =>

const test: Global.It = (() => {
const test = (
testName: Circus.TestName,
testName: Circus.TestNameLike,
fn: Circus.TestFn,
timeout?: number,
): void => _addTest(testName, undefined, fn, test, timeout);
const skip = (
testName: Circus.TestName,
testName: Circus.TestNameLike,
fn?: Circus.TestFn,
timeout?: number,
): void => _addTest(testName, 'skip', fn, skip, timeout);
const only = (
testName: Circus.TestName,
testName: Circus.TestNameLike,
fn: Circus.TestFn,
timeout?: number,
): void => _addTest(testName, 'only', fn, test.only, timeout);

test.todo = (testName: Circus.TestName, ...rest: Array<any>): void => {
test.todo = (testName: Circus.TestNameLike, ...rest: Array<any>): void => {
if (rest.length > 0 || typeof testName !== 'string') {
throw new ErrorWithStack(
'Todo must be called with only a description.',
Expand All @@ -133,23 +140,25 @@ const test: Global.It = (() => {
};

const _addTest = (
testName: Circus.TestName,
testName: Circus.TestNameLike,
mode: Circus.TestMode,
fn: Circus.TestFn | undefined,
testFn: (
testName: Circus.TestName,
testName: Circus.TestNameLike,
fn: Circus.TestFn,
timeout?: number,
) => void,
timeout?: number,
) => {
const asyncError = new ErrorWithStack(undefined, testFn);

if (typeof testName !== 'string') {
asyncError.message = `Invalid first argument, ${testName}. It must be a string.`;

try {
testName = convertDescriptorToString(testName);
} catch (error) {
asyncError.message = (error as Error).message;
throw asyncError;
}

if (fn === undefined) {
asyncError.message =
'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.';
Expand Down
Expand Up @@ -78,7 +78,7 @@ export const initialize = async ({

globalsObject.test.concurrent = (test => {
const concurrent = (
testName: string,
testName: Global.TestNameLike,
testFn: Global.ConcurrentTestFn,
timeout?: number,
) => {
Expand All @@ -96,7 +96,7 @@ export const initialize = async ({
};

const only = (
testName: string,
testName: Global.TestNameLike,
testFn: Global.ConcurrentTestFn,
timeout?: number,
) => {
Expand Down
5 changes: 3 additions & 2 deletions packages/jest-each/src/bind.ts
Expand Up @@ -7,7 +7,7 @@
*/

import type {Global} from '@jest/types';
import {ErrorWithStack} from 'jest-util';
import {ErrorWithStack, convertDescriptorToString} from 'jest-util';
import convertArrayTable from './table/array';
import convertTemplateTable from './table/template';
import {
Expand Down Expand Up @@ -37,10 +37,11 @@ export default function bind<EachCallback extends Global.TestCallback>(
...taggedTemplateData: Global.TemplateData
) =>
function eachBind(
title: string,
title: Global.BlockNameLike,
test: Global.EachTestFn<EachCallback>,
timeout?: number,
): void {
title = convertDescriptorToString(title);
try {
const tests = isArrayTable(taggedTemplateData)
? buildArrayTests(title, table)
Expand Down
12 changes: 8 additions & 4 deletions packages/jest-jasmine2/src/__tests__/itTestError.test.ts
Expand Up @@ -14,11 +14,13 @@ describe('test/it error throwing', () => {
'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.',
);
});
it("it throws an error when first argument isn't a string", () => {
it("it throws an error when first argument isn't valid", () => {
expect(() => {
// @ts-expect-error
it(() => {});
}).toThrowError('Invalid first argument, () => {}. It must be a string.');
}).toThrowError(
'Invalid first argument, () => {}. It must be a named class, named function, number, or string.',
);
});
it('it throws an error when callback function is not a function', () => {
expect(() => {
Expand All @@ -35,11 +37,13 @@ describe('test/it error throwing', () => {
'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.',
);
});
test("test throws an error when first argument isn't a string", () => {
test("test throws an error when first argument isn't valid", () => {
expect(() => {
// @ts-expect-error
test(() => {});
}).toThrowError('Invalid first argument, () => {}. It must be a string.');
}).toThrowError(
'Invalid first argument, () => {}. It must be a named class, named function, number, or string.',
);
});
test('test throws an error when callback function is not a function', () => {
expect(() => {
Expand Down