mirror of
https://github.com/denoland/std.git
synced 2024-11-21 20:50:22 +00:00
fix(expect): support expect.hasAssertions()
(#5901)
Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
This commit is contained in:
parent
ee9e3020e2
commit
6a4eb6cb91
13
expect/_assertion.ts
Normal file
13
expect/_assertion.ts
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { getAssertionState } from "@std/internal/assertion-state";
|
||||
|
||||
const assertionState = getAssertionState();
|
||||
|
||||
export function hasAssertions() {
|
||||
assertionState.setAssertionCheck(true);
|
||||
}
|
||||
|
||||
export function emitAssertionTrigger() {
|
||||
assertionState.setAssertionTriggered(true);
|
||||
}
|
33
expect/_assertion_test.ts
Normal file
33
expect/_assertion_test.ts
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { describe, it, test } from "@std/testing/bdd";
|
||||
import { expect } from "./expect.ts";
|
||||
|
||||
Deno.test("expect.hasAssertions() API", () => {
|
||||
describe("describe suite", () => {
|
||||
// FIXME(eryue0220): This test should throw `toThrowErrorMatchingSnapshot`
|
||||
it("should throw an error", () => {
|
||||
expect.hasAssertions();
|
||||
});
|
||||
|
||||
it("should pass", () => {
|
||||
expect.hasAssertions();
|
||||
expect("a").toEqual("a");
|
||||
});
|
||||
});
|
||||
|
||||
it("it() suite should pass", () => {
|
||||
expect.hasAssertions();
|
||||
expect("a").toEqual("a");
|
||||
});
|
||||
|
||||
// FIXME(eryue0220): This test should throw `toThrowErrorMatchingSnapshot`
|
||||
test("test suite should throw an error", () => {
|
||||
expect.hasAssertions();
|
||||
});
|
||||
|
||||
test("test suite should pass", () => {
|
||||
expect.hasAssertions();
|
||||
expect("a").toEqual("a");
|
||||
});
|
||||
});
|
@ -14,6 +14,7 @@ import type {
|
||||
Matchers,
|
||||
} from "./_types.ts";
|
||||
import { AssertionError } from "@std/assert/assertion-error";
|
||||
import { emitAssertionTrigger, hasAssertions } from "./_assertion.ts";
|
||||
import {
|
||||
addCustomEqualityTesters,
|
||||
getCustomEqualityTesters,
|
||||
@ -216,6 +217,8 @@ export function expect<T extends Expected = Expected>(
|
||||
} else {
|
||||
matcher(context, ...args);
|
||||
}
|
||||
|
||||
emitAssertionTrigger();
|
||||
}
|
||||
|
||||
return isPromised
|
||||
@ -488,3 +491,21 @@ expect.stringContaining = asymmetricMatchers.stringContaining as (
|
||||
expect.stringMatching = asymmetricMatchers.stringMatching as (
|
||||
pattern: string | RegExp,
|
||||
) => ReturnType<typeof asymmetricMatchers.stringMatching>;
|
||||
/**
|
||||
* `expect.hasAssertions` verifies that at least one assertion is called during a test.
|
||||
*
|
||||
* Note: expect.hasAssertions only can use in bdd function test suite, such as `test` or `it`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
*
|
||||
* import { test } from "@std/testing/bdd";
|
||||
* import { expect } from "@std/expect";
|
||||
*
|
||||
* test("it works", () => {
|
||||
* expect.hasAssertions();
|
||||
* expect("a").not.toBe("b");
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
expect.hasAssertions = hasAssertions as () => void;
|
||||
|
@ -60,19 +60,19 @@
|
||||
* - Utilities:
|
||||
* - {@linkcode expect.addEqualityTester}
|
||||
* - {@linkcode expect.extend}
|
||||
* - {@linkcode expect.hasAssertions}
|
||||
*
|
||||
* Only these functions are still not available:
|
||||
* - Matchers:
|
||||
* - `toMatchSnapShot`
|
||||
* - `toMatchInlineSnapShot`
|
||||
* - `toThrowErrorMatchingSnapShot`
|
||||
* - `toThrowErrorMatchingInlineSnapShot`
|
||||
* - `toMatchInlineSnapshot`
|
||||
* - `toThrowErrorMatchingSnapshot`
|
||||
* - `toThrowErrorMatchingInlineSnapshot`
|
||||
* - Asymmetric matchers:
|
||||
* - `expect.objectContaining`
|
||||
* - `expect.not.objectContaining`
|
||||
* - Utilities:
|
||||
* - `expect.assertions`
|
||||
* - `expect.hasAssertions`
|
||||
* - `expect.addSnapshotSerializer`
|
||||
*
|
||||
* The tracking issue to add support for unsupported parts of the API is
|
||||
|
111
internal/assertion_state.ts
Normal file
111
internal/assertion_state.ts
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
/**
|
||||
* Check the test suite internal state
|
||||
*
|
||||
* @example Usage
|
||||
* ```ts no-eval
|
||||
* import { AssertionState } from "@std/internal";
|
||||
*
|
||||
* const assertionState = new AssertionState();
|
||||
* ```
|
||||
*/
|
||||
export class AssertionState {
|
||||
#state: {
|
||||
assertionCheck: boolean;
|
||||
assertionTriggered: boolean;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.#state = {
|
||||
assertionCheck: false,
|
||||
assertionTriggered: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* If `expect.hasAssertions` called, then through this method to update #state.assertionCheck value.
|
||||
*
|
||||
* @param val Set #state.assertionCheck's value
|
||||
*
|
||||
* @example Usage
|
||||
* ```ts no-eval
|
||||
* import { AssertionState } from "@std/internal";
|
||||
*
|
||||
* const assertionState = new AssertionState();
|
||||
* assertionState.setAssertionCheck(true);
|
||||
* ```
|
||||
*/
|
||||
setAssertionCheck(val: boolean) {
|
||||
this.#state.assertionCheck = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* If any matchers was called, `#state.assertionTriggered` will be set through this method.
|
||||
*
|
||||
* @param val Set #state.assertionTriggered's value
|
||||
*
|
||||
* @example Usage
|
||||
* ```ts no-eval
|
||||
* import { AssertionState } from "@std/internal";
|
||||
*
|
||||
* const assertionState = new AssertionState();
|
||||
* assertionState.setAssertionTriggered(true);
|
||||
* ```
|
||||
*/
|
||||
setAssertionTriggered(val: boolean) {
|
||||
this.#state.assertionTriggered = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Assertion internal state, if `#state.assertionCheck` is set true, but
|
||||
* `#state.assertionTriggered` is still false, then should throw an Assertion Error.
|
||||
*
|
||||
* @returns a boolean value, that the test suite is satisfied with the check. If not,
|
||||
* it should throw an AssertionError.
|
||||
*
|
||||
* @example Usage
|
||||
* ```ts no-eval
|
||||
* import { AssertionState } from "@std/internal";
|
||||
*
|
||||
* const assertionState = new AssertionState();
|
||||
* if (assertionState.checkAssertionErrorStateAndReset()) {
|
||||
* // throw AssertionError("");
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
checkAssertionErrorStateAndReset(): boolean {
|
||||
const result = this.#state.assertionCheck &&
|
||||
!this.#state.assertionTriggered;
|
||||
|
||||
this.#resetAssertionState();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#resetAssertionState(): void {
|
||||
this.#state = {
|
||||
assertionCheck: false,
|
||||
assertionTriggered: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const assertionState = new AssertionState();
|
||||
|
||||
/**
|
||||
* return an instance of AssertionState
|
||||
*
|
||||
* @returns AssertionState
|
||||
*
|
||||
* @example Usage
|
||||
* ```ts no-eval
|
||||
* import { getAssertionState } from "@std/internal";
|
||||
*
|
||||
* const assertionState = getAssertionState();
|
||||
* assertionState.setAssertionTriggered(true);
|
||||
* ```
|
||||
*/
|
||||
export function getAssertionState(): AssertionState {
|
||||
return assertionState;
|
||||
}
|
28
internal/assertion_state_test.ts
Normal file
28
internal/assertion_state_test.ts
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { assertEquals } from "@std/assert";
|
||||
import { AssertionState } from "./assertion_state.ts";
|
||||
|
||||
Deno.test("AssertionState checkAssertionErrorStateAndReset pass", () => {
|
||||
const assertionState = new AssertionState();
|
||||
assertionState.setAssertionTriggered(true);
|
||||
|
||||
assertEquals(assertionState.checkAssertionErrorStateAndReset(), false);
|
||||
});
|
||||
|
||||
Deno.test("AssertionState checkAssertionErrorStateAndReset pass", () => {
|
||||
const assertionState = new AssertionState();
|
||||
assertionState.setAssertionTriggered(true);
|
||||
|
||||
assertEquals(assertionState.checkAssertionErrorStateAndReset(), false);
|
||||
|
||||
assertionState.setAssertionCheck(true);
|
||||
assertEquals(assertionState.checkAssertionErrorStateAndReset(), true);
|
||||
});
|
||||
|
||||
Deno.test("AssertionState checkAssertionErrorStateAndReset fail", () => {
|
||||
const assertionState = new AssertionState();
|
||||
assertionState.setAssertionCheck(true);
|
||||
|
||||
assertEquals(assertionState.checkAssertionErrorStateAndReset(), true);
|
||||
});
|
@ -3,6 +3,7 @@
|
||||
"version": "1.0.3",
|
||||
"exports": {
|
||||
".": "./mod.ts",
|
||||
"./assertion-state": "./assertion_state.ts",
|
||||
"./build-message": "./build_message.ts",
|
||||
"./diff-str": "./diff_str.ts",
|
||||
"./diff": "./diff.ts",
|
||||
|
@ -36,6 +36,7 @@
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
export * from "./assertion_state.ts";
|
||||
export * from "./build_message.ts";
|
||||
export * from "./diff.ts";
|
||||
export * from "./diff_str.ts";
|
||||
|
@ -402,6 +402,8 @@
|
||||
* @module
|
||||
*/
|
||||
|
||||
import { getAssertionState } from "@std/internal/assertion-state";
|
||||
import { AssertionError } from "@std/assert/assertion-error";
|
||||
import {
|
||||
type DescribeDefinition,
|
||||
type HookNames,
|
||||
@ -565,6 +567,7 @@ export function it<T>(...args: ItArgs<T>) {
|
||||
"Cannot register new test cases after already registered test cases start running",
|
||||
);
|
||||
}
|
||||
const assertionState = getAssertionState();
|
||||
const options = itDefinition(...args);
|
||||
const { suite } = options;
|
||||
const testSuite = suite
|
||||
@ -594,6 +597,12 @@ export function it<T>(...args: ItArgs<T>) {
|
||||
} finally {
|
||||
TestSuiteInternal.runningCount--;
|
||||
}
|
||||
|
||||
if (assertionState.checkAssertionErrorStateAndReset()) {
|
||||
throw new AssertionError(
|
||||
"Expected at least one assertion to be called but received none",
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
if (ignore !== undefined) {
|
||||
|
Loading…
Reference in New Issue
Block a user