aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-06-01 00:19:33 -0700
committerGravatar GitHub <noreply@github.com> 2023-06-01 00:19:33 -0700
commita4ccd4e0b4cc19f534bf639f30b7e4218400e1e8 (patch)
tree250f89bddd6e6c920645db2ad39bb7edf576edf0 /packages
parentcb0f76aa73f6b85667b57015a77ac39d9c78aa0b (diff)
parent689434e012a47b9be897f6d90d6aa211b13dfc19 (diff)
downloadbun-a4ccd4e0b4cc19f534bf639f30b7e4218400e1e8.tar.gz
bun-a4ccd4e0b4cc19f534bf639f30b7e4218400e1e8.tar.zst
bun-a4ccd4e0b4cc19f534bf639f30b7e4218400e1e8.zip
Merge branch 'main' into jarred/portjarred/port
Diffstat (limited to 'packages')
-rw-r--r--packages/bun-internal-test/runners/bun/runner.ts3
-rw-r--r--packages/bun-internal-test/src/runner.node.mjs82
-rw-r--r--packages/bun-macro-relay/README.md123
-rw-r--r--packages/bun-macro-relay/__generated__/FooOperation.graphql.ts3
-rw-r--r--packages/bun-macro-relay/bun-macro-relay.tsx79
-rwxr-xr-xpackages/bun-macro-relay/bun.lockbbin1166 -> 0 bytes
-rw-r--r--packages/bun-macro-relay/package.json15
-rw-r--r--packages/bun-macro-relay/test/foo.tsx11
-rw-r--r--packages/bun-macro-relay/tsconfig.json6
-rw-r--r--packages/bun-types/bun-test.d.ts275
10 files changed, 265 insertions, 332 deletions
diff --git a/packages/bun-internal-test/runners/bun/runner.ts b/packages/bun-internal-test/runners/bun/runner.ts
index 3fb2660be..43bf4586f 100644
--- a/packages/bun-internal-test/runners/bun/runner.ts
+++ b/packages/bun-internal-test/runners/bun/runner.ts
@@ -318,7 +318,7 @@ export function parseTest(stderr: string, options: ParseTestOptions = {}): Parse
};
};
const parseTestLine = (line: string): Test | undefined => {
- const match = /^(✓|‚úì|✗|‚úó|-|✎) (.*)$/.exec(line);
+ const match = /^(✓|‚úì|✗|‚úó|»|-|✎) (.*)$/.exec(line);
if (!match) {
return undefined;
}
@@ -333,6 +333,7 @@ export function parseTest(stderr: string, options: ParseTestOptions = {}): Parse
case "‚úó":
status = "fail";
break;
+ case "»":
case "-":
status = "skip";
break;
diff --git a/packages/bun-internal-test/src/runner.node.mjs b/packages/bun-internal-test/src/runner.node.mjs
index f540b424f..4ded315ff 100644
--- a/packages/bun-internal-test/src/runner.node.mjs
+++ b/packages/bun-internal-test/src/runner.node.mjs
@@ -23,30 +23,6 @@ function* findTests(dir, query) {
}
}
-function dump(buf) {
- var offset = 0,
- length = buf.byteLength;
- while (offset < length) {
- try {
- const wrote = writeSync(1, buf);
- offset += wrote;
- if (offset < length) {
- try {
- fsyncSync(1);
- } catch (e) {}
-
- buf = buf.slice(wrote);
- }
- } catch (e) {
- if (e.code === "EAGAIN") {
- continue;
- }
-
- throw e;
- }
- }
-}
-
var failingTests = [];
async function runTest(path) {
@@ -58,7 +34,7 @@ async function runTest(path) {
status: exitCode,
error: timedOut,
} = spawnSync("bun", ["test", path], {
- stdio: ["ignore", "pipe", "pipe"],
+ stdio: "inherit",
timeout: 1000 * 60 * 3,
env: {
...process.env,
@@ -75,64 +51,8 @@ async function runTest(path) {
failingTests.push(name);
if (timedOut) console.error(timedOut);
}
-
- if (isAction && !passed) {
- findErrors(stdout);
- findErrors(stderr);
- }
-
- if (isAction) {
- const prefix = passed ? "PASS" : `FAIL`;
- action.startGroup(`${prefix} - ${name}`);
- }
-
- stdout && stdout?.byteLength && dump(stdout);
- stderr && stderr?.byteLength && dump(stderr);
-
- if (isAction) {
- action.endGroup();
- }
}
-function findErrors(data) {
- const text = new StringDecoder().write(new Buffer(data.buffer)).replaceAll(/\u001b\[.*?m/g, "");
- let index = 0;
- do {
- index = text.indexOf("error: ", index);
- if (index === -1) {
- break;
- }
-
- const messageEnd = text.indexOf("\n", index);
- if (messageEnd === -1) {
- break;
- }
- const message = text.slice(index + 7, messageEnd);
- index = text.indexOf("at ", index);
- if (index === -1) {
- break;
- }
- const startAt = index;
- index = text.indexOf("\n", index);
- if (index === -1) {
- break;
- }
- const at = text.slice(startAt + 3, index);
- let file = at.slice(0, at.indexOf(":"));
- if (file.length === 0) {
- continue;
- }
-
- const startLine = at.slice(at.indexOf(":") + 1, at.indexOf(":") + 1 + at.slice(at.indexOf(":") + 1).indexOf(":"));
- const startColumn = at.slice(at.indexOf(":") + 1 + at.slice(at.indexOf(":") + 1).indexOf(":") + 1);
-
- if (file.startsWith("/")) {
- file = relative(cwd, file);
- }
-
- action.error(message, { file, startLine, startColumn });
- } while (index !== -1);
-}
var tests = [];
var testFileNames = [];
for (const path of findTests(resolve(cwd, "test"))) {
diff --git a/packages/bun-macro-relay/README.md b/packages/bun-macro-relay/README.md
deleted file mode 100644
index 157163af5..000000000
--- a/packages/bun-macro-relay/README.md
+++ /dev/null
@@ -1,123 +0,0 @@
-# bun-macro-relay
-
-This lets you use Facebook's [Relay](https://github.com/facebook/relay) framework (GraphQL) with bun.
-
-Specifically, this implements the bun equivalent of [`babel-plugin-relay`](https://github.com/facebook/relay/tree/main/packages/babel-plugin-relay). It parses `graphql` queries, but does not compile/save them to your artifacts directory, you still need [`relay-compiler`](https://github.com/facebook/relay/tree/main/packages/relay-compiler) for that.
-
-## Installation
-
-```
-npm install -D bun-macro-relay
-```
-
-## Usage
-
-With three lines in your project's `bunfig.toml`, `react-relay` works automatically with bun.
-
-Add this to your `bunfig.toml`:
-
-```toml
-[macros]
-react-relay = {graphql = "bun-macro-relay"}
-relay-runtime = {graphql = "bun-macro-relay"}
-```
-
-This tells bun to automatically pretend every import statement to `react-relay` with a `graphql` import came from `macro:bun-macro-relay/bun-macro-relay.tsx`.
-
-Effectively, it applies this diff in-memory so you can use `bun-macro-relay` without making other changes to your code:
-
-```js
-// bun will remap this import:
-import { graphql } from "react-relay";
-
-// To this:
-import { graphql } from "macro:bun-macro-relay/bun-macro-relay.tsx";
-```
-
-You can still use the other imports from `react-relay`. It only affects the `graphql` export from `react-relay`.
-
-```js
-// bun will remap this import:
-import { graphql, useFragment } from "react-relay";
-
-// To this:
-import { graphql } from "macro:bun-macro-relay/bun-macro-relay.tsx";
-import { useFragment } from "react-relay";
-```
-
-Ultimately, the `graphql` import should no longer appear in transpiled output:
-
-```js
-import { useFragment } from "react-relay";
-```
-
-If you'd rather not modify your project's `package.json`, you can do this instead:
-
-```js
-import { graphql } from "macro:bun-macro-relay";
-```
-
-## Configuration
-
-For performance reasons, `bun-macro-relay` does not read `relay-config`. That means your Relay configuration will _not_ be honored.
-
-Fortunately, the only configuration option relevant to `bun-macro-relay` is modifying the artifacts directory (the directory where `relay-compiler` saves compiled `.graphql` files).
-
-You can still change that with `bun-macro-relay`.
-
-### Changing the artifacts directory
-
-Pass the `BUN_MACRO_RELAY_ARTIFACT_DIRECTORY` environment variable to bun:
-
-```bash
-BUN_MACRO_RELAY_ARTIFACT_DIRECTORY="__generated__" bun
-```
-
-You can also save it in `.env`, `.env.local`, or `.env.dev`. The path should be relative to the directory containing the project's package.json without a leading `.` or `./`. You can also pass it an absolute path.
-
-## What does `bun-macro-relay` actually do?
-
-1. Parses GraphQL (using the same `graphql` npm package as babel-plugin-relay)
-2. Injects an import to the correct compiled GraphQL file in the Relay artifacts directory
-3. Replaces the use of the `graphql` template literal with the `default` import from the compiled GraphQL file.
-
-Here's an example.
-
-Input:
-
-```tsx
-import { graphql, useLazyLoadQuery } from "react-relay";
-
-const Tweet = () => {
- const data = useLazyLoadQuery(
- graphql`
- query TweetQuery {
- ...Tweet_tweet
- }
- `,
- {}
- );
- if (!data.tweet) return null;
- return <TweetComponent tweet={data.tweet} />;
-};
-```
-
-Output:
-
-```jsx
-import TweetQuery from "../__generated__/TweetQuery.graphql.ts";
-import { useLazyLoadQuery } from "react-relay";
-
-const Tweet = () => {
- const data = useLazyLoadQuery(TweetQuery, {});
- if (!data.tweet) return null;
- return <TweetComponent tweet={data.tweet} />;
-};
-```
-
-bun automatically transpiles JSX & TypeScript, but that's not relevant to this example.
-
-### What does `bun-macro-relay` not do?
-
-1. This first version doesn't hash the contents of the `graphql` query, so it won't detect when the GraphQL query is out of sync with the compiled `.graphql` file in development. However, if you're running Relay's CLI, bun's hot module reloading will automatically update. As long as you run Relay's CLI, it shouldn't matter. This will be fixed eventually (have to expose a native MD5 hashing function)
-2. Compile GraphQL. You still need to use `relay-compiler` for that.
diff --git a/packages/bun-macro-relay/__generated__/FooOperation.graphql.ts b/packages/bun-macro-relay/__generated__/FooOperation.graphql.ts
deleted file mode 100644
index 4c83371c9..000000000
--- a/packages/bun-macro-relay/__generated__/FooOperation.graphql.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export class FooOperation {}
-
-export default FooOperation;
diff --git a/packages/bun-macro-relay/bun-macro-relay.tsx b/packages/bun-macro-relay/bun-macro-relay.tsx
deleted file mode 100644
index f7899698d..000000000
--- a/packages/bun-macro-relay/bun-macro-relay.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import { parse, print } from "graphql/index.js";
-import { resolve } from "path";
-
-//
-// 1. Parse the GraphQL tag.
-// 2. From the parsed GraphQL query, get the AST definition.
-// 3. From the AST definition, inject an import to that file inside the artifact directory
-// 4. (TODO) MD5 the printed source text
-// 5. (TODO) At runtime, if md5 !== import.md5, then warn the user that the query has changed
-// but the file hasn't been updated so it must be reloaded.
-// 6. Replace the TemplateLiteral with the default identifier from the injected import
-let artifactDirectory: string = `__generated__`;
-
-const { RELAY_ARTIFACT_DIRECTORY, BUN_MACRO_RELAY_ARTIFACT_DIRECTORY } =
- Bun.env;
-
-if (RELAY_ARTIFACT_DIRECTORY) {
- artifactDirectory = RELAY_ARTIFACT_DIRECTORY;
-}
-
-if (BUN_MACRO_RELAY_ARTIFACT_DIRECTORY) {
- artifactDirectory = BUN_MACRO_RELAY_ARTIFACT_DIRECTORY;
-}
-
-artifactDirectory = resolve(artifactDirectory);
-
-export function graphql(node) {
- let query;
-
- if (node instanceof <call />) {
- query = node.arguments[0].toString();
- } else if (node instanceof <template />) {
- query = node.toString();
- }
-
- if (typeof query !== "string" || query.length === 0) {
- throw new Error("BunMacroRelay: Unexpected empty graphql string.");
- }
-
- const ast = parse(query);
-
- if (ast.definitions.length === 0) {
- throw new Error("BunMacroRelay: Unexpected empty graphql tag.");
- }
-
- const definition = ast.definitions[0];
-
- if (
- definition.kind !== "FragmentDefinition" &&
- definition.kind !== "OperationDefinition"
- ) {
- throw new Error(
- `BunMacroRelay: Expected a fragment, mutation, query, or subscription, got "${definition.kind}"`,
- );
- }
-
- const graphqlDefinition = definition;
-
- const definitionName = graphqlDefinition.name && graphqlDefinition.name.value;
- if (!definitionName) {
- throw new Error("GraphQL operations and fragments must contain names");
- }
-
- const identifiername = `${definitionName}_$gql`;
-
- const importStmt = (
- <import
- default={identifiername}
- path={`${artifactDirectory}/${definitionName}.graphql`}
- />
- );
-
- return (
- <>
- <inject>{importStmt}</inject>
- <id to={importStmt.namespace[identifiername]} pure />
- </>
- );
-}
diff --git a/packages/bun-macro-relay/bun.lockb b/packages/bun-macro-relay/bun.lockb
deleted file mode 100755
index 03fefa6d0..000000000
--- a/packages/bun-macro-relay/bun.lockb
+++ /dev/null
Binary files differ
diff --git a/packages/bun-macro-relay/package.json b/packages/bun-macro-relay/package.json
deleted file mode 100644
index 772fd5f1c..000000000
--- a/packages/bun-macro-relay/package.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "name": "bun-macro-relay",
- "version": "0.1.2",
- "module": "bun-macro-relay.tsx",
- "license": "MIT",
- "peerDependencies": {
- "graphql": "^15.0.0"
- },
- "devDependencies": {
- "graphql": "^15.6.0"
- },
- "files": [
- "bun-macro-relay.tsx"
- ]
-}
diff --git a/packages/bun-macro-relay/test/foo.tsx b/packages/bun-macro-relay/test/foo.tsx
deleted file mode 100644
index fbb54f551..000000000
--- a/packages/bun-macro-relay/test/foo.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { graphql } from "react-relay";
-
-export const Foo = () => {
- const definition = graphql`
- query FooOperation {
- foo
- }
- `;
-
- return <div>{definition.operation.name}</div>;
-};
diff --git a/packages/bun-macro-relay/tsconfig.json b/packages/bun-macro-relay/tsconfig.json
deleted file mode 100644
index 19d4ac2e6..000000000
--- a/packages/bun-macro-relay/tsconfig.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "compilerOptions": {
- "baseUrl": ".",
- "paths": {}
- }
-}
diff --git a/packages/bun-types/bun-test.d.ts b/packages/bun-types/bun-test.d.ts
index 425832c7f..a5cee34c3 100644
--- a/packages/bun-types/bun-test.d.ts
+++ b/packages/bun-types/bun-test.d.ts
@@ -33,7 +33,41 @@ declare module "bun:test" {
*/
export type Describe = {
(label: string, fn: () => void): void;
- skip: (label: string, fn: () => void) => void;
+ /**
+ * Skips all other tests, except this group of tests.
+ *
+ * @param label the label for the tests
+ * @param fn the function that defines the tests
+ */
+ only(label: string, fn: () => void): void;
+ /**
+ * Skips this group of tests.
+ *
+ * @param label the label for the tests
+ * @param fn the function that defines the tests
+ */
+ skip(label: string, fn: () => void): void;
+ /**
+ * Marks this group of tests as to be written or to be fixed.
+ *
+ * @param label the label for the tests
+ * @param fn the function that defines the tests
+ */
+ todo(label: string, fn?: () => void): void;
+ /**
+ * Runs this group of tests, only if `condition` is true.
+ *
+ * This is the opposite of `describe.skipIf()`.
+ *
+ * @param condition if these tests should run
+ */
+ if(condition: boolean): (label: string, fn: () => void) => void;
+ /**
+ * Skips this group of tests, if `condition` is true.
+ *
+ * @param condition if these tests should be skipped
+ */
+ skipIf(condition: boolean): (label: string, fn: () => void) => void;
};
/**
* Describes a group of related tests.
@@ -122,6 +156,31 @@ declare module "bun:test" {
| (() => void | Promise<unknown>)
| ((done: (err?: unknown) => void) => void),
): void;
+ export type TestOptions = {
+ /**
+ * Sets the timeout for the test in milliseconds.
+ *
+ * If the test does not complete within this time, the test will fail with:
+ * ```ts
+ * 'Timeout: test {name} timed out after 5000ms'
+ * ```
+ *
+ * @default 5000 // 5 seconds
+ */
+ timeout?: number;
+ /**
+ * Sets the number of times to retry the test if it fails.
+ *
+ * @default 0
+ */
+ retry?: number;
+ /**
+ * Sets the number of times to repeat the test, regardless of whether it passed or failed.
+ *
+ * @default 0
+ */
+ repeats?: number;
+ };
/**
* Runs a test.
*
@@ -135,8 +194,13 @@ declare module "bun:test" {
* expect(response.ok).toBe(true);
* });
*
+ * test("can set a timeout", async () => {
+ * await Bun.sleep(100);
+ * }, 50); // or { timeout: 50 }
+ *
* @param label the label for the test
* @param fn the test function
+ * @param options the test timeout or options
*/
export type Test = {
(
@@ -145,45 +209,44 @@ declare module "bun:test" {
| (() => void | Promise<unknown>)
| ((done: (err?: unknown) => void) => void),
/**
- * @default 300_000 milliseconds (5 minutes)
- *
- * After this many milliseconds, the test will fail with an error message like:
- * ```ts
- * 'Timeout: test "name" timed out after 300_000ms'
- * ```
+ * - If a `number`, sets the timeout for the test in milliseconds.
+ * - If an `object`, sets the options for the test.
+ * - `timeout` sets the timeout for the test in milliseconds.
+ * - `retry` sets the number of times to retry the test if it fails.
+ * - `repeats` sets the number of times to repeat the test, regardless of whether it passed or failed.
*/
- timeoutMs?: number,
+ options?: number | TestOptions,
): void;
/**
* Skips all other tests, except this test.
- * @deprecated Not yet implemented.
*
* @param label the label for the test
* @param fn the test function
- * @param timeoutMs the timeout for the test
+ * @param options the test timeout or options
*/
only(
label: string,
fn:
| (() => void | Promise<unknown>)
| ((done: (err?: unknown) => void) => void),
- timeoutMs?: number,
+ options?: number | TestOptions,
): void;
/**
* Skips this test.
*
* @param label the label for the test
* @param fn the test function
+ * @param options the test timeout or options
*/
skip(
label: string,
fn:
| (() => void | Promise<unknown>)
| ((done: (err?: unknown) => void) => void),
- timeoutMs?: number,
+ options?: number | TestOptions,
): void;
/**
- * Indicate a test is yet to be written or implemented correctly.
+ * Marks this test as to be written or to be fixed.
*
* When a test function is passed, it will be marked as `todo` in the test results
* as long the test does not pass. When the test passes, the test will be marked as
@@ -192,13 +255,45 @@ declare module "bun:test" {
*
* @param label the label for the test
* @param fn the test function
+ * @param options the test timeout or options
*/
todo(
label: string,
fn?:
| (() => void | Promise<unknown>)
| ((done: (err?: unknown) => void) => void),
+ options?: number | TestOptions,
): void;
+ /**
+ * Runs this test, if `condition` is true.
+ *
+ * This is the opposite of `test.skipIf()`.
+ *
+ * @param condition if the test should run
+ */
+ if(
+ condition: boolean,
+ ): (
+ label: string,
+ fn:
+ | (() => void | Promise<unknown>)
+ | ((done: (err?: unknown) => void) => void),
+ options?: number | TestOptions,
+ ) => void;
+ /**
+ * Skips this test, if `condition` is true.
+ *
+ * @param condition if the test should be skipped
+ */
+ skipIf(
+ condition: boolean,
+ ): (
+ label: string,
+ fn:
+ | (() => void | Promise<unknown>)
+ | ((done: (err?: unknown) => void) => void),
+ options?: number | TestOptions,
+ ) => void;
};
/**
* Runs a test.
@@ -533,6 +628,160 @@ declare module "bun:test" {
* expect(new Set()).toBeEmpty();
*/
toBeEmpty(): void;
+ /**
+ * Asserts that a value is `null` or `undefined`.
+ *
+ * @example
+ * expect(null).toBeNil();
+ * expect(undefined).toBeNil();
+ */
+ toBeNil(): void;
+ /**
+ * Asserts that a value is a `boolean`.
+ *
+ * @example
+ * expect(true).toBeBoolean();
+ * expect(false).toBeBoolean();
+ * expect(null).not.toBeBoolean();
+ * expect(0).not.toBeBoolean();
+ */
+ toBeBoolean(): void;
+ /**
+ * Asserts that a value is `true`.
+ *
+ * @example
+ * expect(true).toBeTrue();
+ * expect(false).not.toBeTrue();
+ * expect(1).not.toBeTrue();
+ */
+ toBeTrue(): void;
+ /**
+ * Asserts that a value is `false`.
+ *
+ * @example
+ * expect(false).toBeFalse();
+ * expect(true).not.toBeFalse();
+ * expect(0).not.toBeFalse();
+ */
+ toBeFalse(): void;
+ /**
+ * Asserts that a value is a `number`.
+ *
+ * @example
+ * expect(1).toBeNumber();
+ * expect(3.14).toBeNumber();
+ * expect(NaN).toBeNumber();
+ * expect(BigInt(1)).not.toBeNumber();
+ */
+ toBeNumber(): void;
+ /**
+ * Asserts that a value is a `number`, and is an integer.
+ *
+ * @example
+ * expect(1).toBeInteger();
+ * expect(3.14).not.toBeInteger();
+ * expect(NaN).not.toBeInteger();
+ */
+ toBeInteger(): void;
+ /**
+ * Asserts that a value is a `number`, and is not `NaN` or `Infinity`.
+ *
+ * @example
+ * expect(1).toBeFinite();
+ * expect(3.14).toBeFinite();
+ * expect(NaN).not.toBeFinite();
+ * expect(Infinity).not.toBeFinite();
+ */
+ toBeFinite(): void;
+ /**
+ * Asserts that a value is a positive `number`.
+ *
+ * @example
+ * expect(1).toBePositive();
+ * expect(-3.14).not.toBePositive();
+ * expect(NaN).not.toBePositive();
+ */
+ toBePositive(): void;
+ /**
+ * Asserts that a value is a negative `number`.
+ *
+ * @example
+ * expect(-3.14).toBeNegative();
+ * expect(1).not.toBeNegative();
+ * expect(NaN).not.toBeNegative();
+ */
+ toBeNegative(): void;
+ /**
+ * Asserts that a value is a number between a start and end value.
+ *
+ * @param start the start number (inclusive)
+ * @param end the end number (exclusive)
+ */
+ toBeWithin(start: number, end: number): void;
+ /**
+ * Asserts that a value is a `symbol`.
+ *
+ * @example
+ * expect(Symbol("foo")).toBeSymbol();
+ * expect("foo").not.toBeSymbol();
+ */
+ toBeSymbol(): void;
+ /**
+ * Asserts that a value is a `function`.
+ *
+ * @example
+ * expect(() => {}).toBeFunction();
+ */
+ toBeFunction(): void;
+ /**
+ * Asserts that a value is a `Date` object.
+ *
+ * To check if a date is valid, use `toBeValidDate()` instead.
+ *
+ * @example
+ * expect(new Date()).toBeDate();
+ * expect(new Date(null)).toBeDate();
+ * expect("2020-03-01").not.toBeDate();
+ */
+ toBeDate(): void;
+ /**
+ * Asserts that a value is a valid `Date` object.
+ *
+ * @example
+ * expect(new Date()).toBeValidDate();
+ * expect(new Date(null)).not.toBeValidDate();
+ * expect("2020-03-01").not.toBeValidDate();
+ */
+ toBeValidDate(): void;
+ /**
+ * Asserts that a value is a `string`.
+ *
+ * @example
+ * expect("foo").toBeString();
+ * expect(new String("bar")).toBeString();
+ * expect(123).not.toBeString();
+ */
+ toBeString(): void;
+ /**
+ * Asserts that a value includes a `string`.
+ *
+ * For non-string values, use `toContain()` instead.
+ *
+ * @param expected the expected substring
+ */
+ toInclude(expected: string): void;
+ /**
+ * Asserts that a value starts with a `string`.
+ *
+ * @param expected the string to start with
+ */
+ toStartWith(expected: string): void;
+ /**
+ * Asserts that a value ends with a `string`.
+ *
+ * @param expected the string to end with
+ */
+ toEndWith(expected: string): void;
};
}