@altano/vitest-plugins 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +25 -3
- package/package.json +12 -12
- package/src/matchers/response/toHaveHeader.ts +26 -0
- package/src/matchers/response/toHaveStatus.ts +20 -0
- package/src/matchers/setup.ts +10 -2
- package/src/matchers/toHaveExifProperty.ts +32 -0
- package/src/matchers/types.d.ts +40 -1
- package/src/serializers/html.ts +39 -0
- package/src/serializers/setup.ts +6 -0
- package/src/serializers/url.ts +21 -0
- package/src/serializers/vFile.ts +1 -1
- package/tests/unit/matchers/readme.spec.ts +2 -2
- package/tests/unit/matchers/response/toHaveHeader.spec.ts +49 -0
- package/tests/unit/matchers/response/toHaveStatus.spec.ts +42 -0
- package/tests/unit/matchers/toHaveExifProperty.spec.ts +59 -0
- package/tests/unit/matchers/toMatchError.spec.ts +2 -2
- package/tests/unit/serializers/html.spec.ts +58 -0
- package/tests/unit/serializers/url.spec.ts +14 -0
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/_ eslint-disable @typescript-eslint/no-empty-object-type _/
|
|
2
|
+
|
|
1
3
|
# vitest-plugins
|
|
2
4
|
|
|
3
5
|
[](https://www.npmjs.com/package/@altano/vitest-plugins)  
|
|
@@ -79,6 +81,14 @@ FormattedVFile {
|
|
|
79
81
|
|
|
80
82
|
Will replace any instances of `process.cwd()` with `<cwd>` in the snapshot. Useful when serializing strings that contain absolute paths, since those will be different on other machines running the tests.
|
|
81
83
|
|
|
84
|
+
### URLs
|
|
85
|
+
|
|
86
|
+
Canonicalizes parts of a URL that might not be deterministic in tests. Tries to be conservative, so at the moment, this only strips the port from localhost urls, e.g. `http://localhost:1234` is converted to `http://localhost`.
|
|
87
|
+
|
|
88
|
+
### HTML
|
|
89
|
+
|
|
90
|
+
HTML is prettier formatted. Only works on well-formed HTML that starts with `<html` or `<!doctype`. To avoid infinitely reformatting the HTML, it is preprended with `<!-- Formatted HTML -->`.
|
|
91
|
+
|
|
82
92
|
## Matchers
|
|
83
93
|
|
|
84
94
|
Vitest's error matchers let you match against the error message, but not the rest of the Error object:
|
|
@@ -124,7 +134,7 @@ expect("/private/some/path").toBePath("/some/path");
|
|
|
124
134
|
|
|
125
135
|
### toBeFile
|
|
126
136
|
|
|
127
|
-
Verify a file exists
|
|
137
|
+
Verify a file exists (on the filesystem)
|
|
128
138
|
|
|
129
139
|
```ts
|
|
130
140
|
expect(import.meta.filename).toBeFile();
|
|
@@ -132,7 +142,7 @@ expect(import.meta.filename).toBeFile();
|
|
|
132
142
|
|
|
133
143
|
### toBeDirectory
|
|
134
144
|
|
|
135
|
-
Verify a directory exists
|
|
145
|
+
Verify a directory exists (on the filesystem)
|
|
136
146
|
|
|
137
147
|
```ts
|
|
138
148
|
expect("/").toBeDirectory();
|
|
@@ -140,8 +150,20 @@ expect("/").toBeDirectory();
|
|
|
140
150
|
|
|
141
151
|
### toEqualFile
|
|
142
152
|
|
|
143
|
-
Verify that the given path
|
|
153
|
+
Verify that the contents of a file at a given path match the contents of a file at a another path
|
|
144
154
|
|
|
145
155
|
```ts
|
|
146
156
|
expect("/some/file.txt").toEqualFile("/other/file.txt");
|
|
147
157
|
```
|
|
158
|
+
|
|
159
|
+
### toHaveExifProperty
|
|
160
|
+
|
|
161
|
+
Similar to the `toHaveProperty` matcher, but checks exif properties on the given buffer (or any object parseable by the `exifr` library).
|
|
162
|
+
|
|
163
|
+
### toHaveHeader (on Response objects)
|
|
164
|
+
|
|
165
|
+
Verify that Response has an expected header and optional value
|
|
166
|
+
|
|
167
|
+
### toHaveStatus (on Response objects)
|
|
168
|
+
|
|
169
|
+
Verify that a Response has an expected status code
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@altano/vitest-plugins",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Custom matchers and snapshot serializers to enhance vitest",
|
|
5
5
|
"homepage": "https://github.com/altano/npm-packages/tree/main/packages/vitest-plugins",
|
|
6
6
|
"repository": {
|
|
@@ -26,22 +26,21 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@prettier/sync": "^0.5.2",
|
|
29
|
+
"exifr": "^7.1.3",
|
|
29
30
|
"serialize-error": "^11.0.3",
|
|
30
31
|
"vfile": "^6.0.3",
|
|
31
|
-
"vitest": "^
|
|
32
|
+
"vitest": "^3.0.9"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"@
|
|
35
|
-
"@
|
|
36
|
-
"@vitest/
|
|
37
|
-
"
|
|
38
|
-
"eslint": "^9.
|
|
39
|
-
"mdast-util-mdxjs-esm": "^2.0.1",
|
|
35
|
+
"@types/node": "^22.13.10",
|
|
36
|
+
"@vitest/coverage-v8": "^3.0.9",
|
|
37
|
+
"@vitest/expect": "^3.0.9",
|
|
38
|
+
"depcheck": "^1.4.7",
|
|
39
|
+
"eslint": "^9.22.0",
|
|
40
40
|
"prettier": "^3.3.3",
|
|
41
|
-
"typescript": "^5.
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"vitest": "^2.1.3",
|
|
41
|
+
"typescript": "^5.8.2",
|
|
42
|
+
"vite": "^6.2.3",
|
|
43
|
+
"vitest": "^3.0.9",
|
|
45
44
|
"@altano/tsconfig": "0.0.0"
|
|
46
45
|
},
|
|
47
46
|
"publishConfig": {
|
|
@@ -50,6 +49,7 @@
|
|
|
50
49
|
"scripts": {
|
|
51
50
|
"check:types:src": "tsc --noEmit",
|
|
52
51
|
"check:types:test": "tsc --noEmit --project ./tests/tsconfig.json",
|
|
52
|
+
"check:unused-dependencies": "depcheck",
|
|
53
53
|
"clean": "rm -rf .turbo && rm -rf .tsbuildinfo && rm -rf node_modules && rm -rf dist",
|
|
54
54
|
"format": "prettier --check src",
|
|
55
55
|
"format:fix": "prettier --write src",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Matcher } from "../matcher.js";
|
|
2
|
+
|
|
3
|
+
const toHaveHeader: Matcher = function (
|
|
4
|
+
response: Response,
|
|
5
|
+
header: string,
|
|
6
|
+
value?: string | number,
|
|
7
|
+
) {
|
|
8
|
+
const headers = response.headers;
|
|
9
|
+
const hasProperty = headers.has(header);
|
|
10
|
+
const receivedValue = headers.get(header);
|
|
11
|
+
|
|
12
|
+
const { isNot } = this;
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
message: () =>
|
|
16
|
+
value === undefined
|
|
17
|
+
? `Received ${isNot ? "should not have" : "does not have"} the header "${header}"`
|
|
18
|
+
: `Header "${header}" ${isNot ? "should not be" : "is not"} expected value`,
|
|
19
|
+
pass:
|
|
20
|
+
hasProperty && (value === undefined || this.equals(receivedValue, value)),
|
|
21
|
+
actual: receivedValue,
|
|
22
|
+
expected: value,
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default toHaveHeader;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Matcher } from "../matcher.js";
|
|
2
|
+
|
|
3
|
+
const toHaveStatus: Matcher = function (
|
|
4
|
+
response: Response,
|
|
5
|
+
expectedStatus: number,
|
|
6
|
+
) {
|
|
7
|
+
const receivedStatus = response.status;
|
|
8
|
+
|
|
9
|
+
const { isNot } = this;
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
message: () =>
|
|
13
|
+
`Status ${isNot ? "should not be expected value" : "is not expected value"}`,
|
|
14
|
+
pass: this.equals(receivedStatus, expectedStatus),
|
|
15
|
+
actual: receivedStatus,
|
|
16
|
+
expected: expectedStatus,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default toHaveStatus;
|
package/src/matchers/setup.ts
CHANGED
|
@@ -6,12 +6,20 @@ import toBeDirectory from "./toBeDirectory.js";
|
|
|
6
6
|
import toEqualFile from "./toEqualFile.js";
|
|
7
7
|
import toMatchError from "./toMatchError.js";
|
|
8
8
|
import toThrowErrorMatching from "./toThrowErrorMatching.js";
|
|
9
|
+
import toHaveExifProperty from "./toHaveExifProperty.js";
|
|
10
|
+
import toHaveHeader from "./response/toHaveHeader.js";
|
|
11
|
+
import toHaveStatus from "./response/toHaveStatus.js";
|
|
9
12
|
|
|
10
13
|
expect.extend({
|
|
11
|
-
toBePath,
|
|
12
|
-
toBeFile,
|
|
13
14
|
toBeDirectory,
|
|
15
|
+
toBeFile,
|
|
16
|
+
toBePath,
|
|
14
17
|
toEqualFile,
|
|
18
|
+
toHaveExifProperty,
|
|
15
19
|
toMatchError,
|
|
16
20
|
toThrowErrorMatching,
|
|
21
|
+
|
|
22
|
+
// Response matchers
|
|
23
|
+
toHaveHeader,
|
|
24
|
+
toHaveStatus,
|
|
17
25
|
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { parse } from "exifr";
|
|
2
|
+
import type { Matcher } from "./matcher.js";
|
|
3
|
+
|
|
4
|
+
export type Input = Parameters<typeof parse>[0];
|
|
5
|
+
|
|
6
|
+
const toHaveExifProperty: Matcher = async function (
|
|
7
|
+
buffer: Input,
|
|
8
|
+
property: string,
|
|
9
|
+
value?: string | number,
|
|
10
|
+
) {
|
|
11
|
+
const exifData = (await parse(buffer)) as unknown;
|
|
12
|
+
const hasProperty = Object.prototype.hasOwnProperty.call(exifData, property);
|
|
13
|
+
|
|
14
|
+
const receivedValue =
|
|
15
|
+
// @ts-expect-error we're checking for property existence, so obviously this
|
|
16
|
+
// might not be valid. don't need a TS error to tell us that.
|
|
17
|
+
exifData[property] as unknown;
|
|
18
|
+
const { isNot } = this;
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
message: () =>
|
|
22
|
+
value === undefined
|
|
23
|
+
? `Received ${isNot ? "should not have" : "does not have"} the property "${property}"`
|
|
24
|
+
: `Property "${property}" ${isNot ? "should not be expected value" : "is not expected value"}`,
|
|
25
|
+
pass:
|
|
26
|
+
hasProperty && (value === undefined || this.equals(receivedValue, value)),
|
|
27
|
+
actual: receivedValue,
|
|
28
|
+
expected: value,
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default toHaveExifProperty;
|
package/src/matchers/types.d.ts
CHANGED
|
@@ -4,18 +4,57 @@ import type { Assertion, AsymmetricMatchersContaining } from "vitest";
|
|
|
4
4
|
|
|
5
5
|
interface CustomMatchers<R = unknown> {
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Verify the realpath (canonical path) of expected. More lenient than a
|
|
8
|
+
* string check when dealing with logical paths, symlinks, etc.
|
|
8
9
|
*/
|
|
9
10
|
toBePath(expected: string): R;
|
|
11
|
+
/** Verify a file exists (on the filesystem) */
|
|
10
12
|
toBeFile(): R;
|
|
13
|
+
/** Verify a directory exists (on the filesystem) */
|
|
11
14
|
toBeDirectory(): R;
|
|
15
|
+
/** Verify that the contents of a file at a given path match the contents of a file at a another path */
|
|
12
16
|
toEqualFile(pathOfFileWithExpectedContents: string): R;
|
|
17
|
+
/**
|
|
18
|
+
* Similar to the `toHaveProperty` matcher, but checks exif properties on the
|
|
19
|
+
* given buffer (or any object parseable by the `exifr` library).
|
|
20
|
+
*/
|
|
21
|
+
toHaveExifProperty(property: string, value?: string | number): Promise<R>;
|
|
22
|
+
/**
|
|
23
|
+
* Verify any part of an error object (e.g. the stack):
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* expect(new Error("face")).toMatchError(
|
|
27
|
+
* expect.objectContaining({
|
|
28
|
+
* stack: expect.stringContaining("readme.spec.ts"),
|
|
29
|
+
* }),
|
|
30
|
+
* );
|
|
31
|
+
*/
|
|
13
32
|
toMatchError(expected: unknown): R;
|
|
33
|
+
/**
|
|
34
|
+
* Verify any part of a _thrown_ error object (e.g. the stack):
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* expect(() => {
|
|
38
|
+
* throw new Error("face");
|
|
39
|
+
* }).toThrowErrorMatching(
|
|
40
|
+
* expect.objectContaining({
|
|
41
|
+
* stack: expect.stringContaining("readme.spec.ts"),
|
|
42
|
+
* }),
|
|
43
|
+
* );
|
|
44
|
+
*/
|
|
14
45
|
toThrowErrorMatching(expected: unknown): R;
|
|
15
46
|
}
|
|
16
47
|
|
|
48
|
+
interface ResponseMatchers<R = unknown> {
|
|
49
|
+
/** Verify that Response has an expected header and optional value */
|
|
50
|
+
toHaveHeader(header: string, value?: string | number): R;
|
|
51
|
+
/** Verify that a Response has an expected status code */
|
|
52
|
+
toHaveStatus(expectedStatus: number): R;
|
|
53
|
+
}
|
|
54
|
+
|
|
17
55
|
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
18
56
|
declare module "vitest" {
|
|
19
57
|
interface Assertion<T> extends CustomMatchers<T> {}
|
|
58
|
+
interface Assertion<T> extends ResponseMatchers<T> {}
|
|
20
59
|
interface AsymmetricMatchersContaining extends CustomMatchers {}
|
|
21
60
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import prettier from "@prettier/sync";
|
|
2
|
+
import prettierConfig from "./.prettierrc.json" with { type: "json" };
|
|
3
|
+
import type { Options } from "prettier";
|
|
4
|
+
import type { SnapshotSerializer } from "vitest";
|
|
5
|
+
|
|
6
|
+
const htmlPattern = /^(<html|<!doctype)/gi;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Prettier format html
|
|
10
|
+
*/
|
|
11
|
+
function formatHtml(unformattedHtml: string): string {
|
|
12
|
+
try {
|
|
13
|
+
const formattedHtml = prettier
|
|
14
|
+
.format(unformattedHtml, {
|
|
15
|
+
filepath: "index.html",
|
|
16
|
+
...(prettierConfig as Options),
|
|
17
|
+
})
|
|
18
|
+
.trim();
|
|
19
|
+
return `<!-- Formatted HTML -->
|
|
20
|
+
${formattedHtml}`;
|
|
21
|
+
} catch (e: unknown) {
|
|
22
|
+
// Prettier can't parse this text as html. That's fine, pass it through
|
|
23
|
+
// unformatted.
|
|
24
|
+
return unformattedHtml;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const serializer: SnapshotSerializer = {
|
|
29
|
+
serialize(val: string, config, indentation, depth, refs, printer) {
|
|
30
|
+
const formattedHtml = formatHtml(val);
|
|
31
|
+
// Wrap the text to prevent infinite recursion
|
|
32
|
+
return printer(formattedHtml, config, indentation, depth, refs);
|
|
33
|
+
},
|
|
34
|
+
test(val) {
|
|
35
|
+
return typeof val === "string" && htmlPattern.test(val);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default serializer;
|
package/src/serializers/setup.ts
CHANGED
|
@@ -3,5 +3,11 @@ import { expect } from "vitest";
|
|
|
3
3
|
import absolutePathSerializer from "./absolutePath.js";
|
|
4
4
|
expect.addSnapshotSerializer(absolutePathSerializer);
|
|
5
5
|
|
|
6
|
+
import urlSerializer from "./url.js";
|
|
7
|
+
expect.addSnapshotSerializer(urlSerializer);
|
|
8
|
+
|
|
6
9
|
import vFileSerializer from "./vFile.js";
|
|
7
10
|
expect.addSnapshotSerializer(vFileSerializer);
|
|
11
|
+
|
|
12
|
+
import htmlSerializer from "./html.js";
|
|
13
|
+
expect.addSnapshotSerializer(htmlSerializer);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SnapshotSerializer } from "vitest";
|
|
2
|
+
|
|
3
|
+
const localhostPattern = /(https?:\/\/localhost):[0-9]+/gi;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Strip the (potentially random) port from the URL
|
|
7
|
+
*/
|
|
8
|
+
function stripPort(str: string): string {
|
|
9
|
+
return str.replaceAll(localhostPattern, "$1");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const urlSerializer: SnapshotSerializer = {
|
|
13
|
+
serialize(val: string, config, indentation, depth, refs, printer) {
|
|
14
|
+
return printer(stripPort(val), config, indentation, depth, refs);
|
|
15
|
+
},
|
|
16
|
+
test(val) {
|
|
17
|
+
return typeof val === "string" && localhostPattern.test(val);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default urlSerializer;
|
package/src/serializers/vFile.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import prettier from "@prettier/sync";
|
|
2
|
-
import prettierConfig from "./.prettierrc.json";
|
|
2
|
+
import prettierConfig from "./.prettierrc.json" with { type: "json" };
|
|
3
3
|
import { VFile, type Value as VFileValue } from "vfile";
|
|
4
4
|
import type { Options } from "prettier";
|
|
5
5
|
import type { SnapshotSerializer } from "vitest";
|
|
@@ -5,7 +5,7 @@ describe("readme", () => {
|
|
|
5
5
|
it("should describe toMatchError", () => {
|
|
6
6
|
expect(new Error("face")).toMatchError(
|
|
7
7
|
expect.objectContaining({
|
|
8
|
-
stack: expect.stringContaining("readme.spec.ts"),
|
|
8
|
+
stack: expect.stringContaining("readme.spec.ts") as unknown,
|
|
9
9
|
}),
|
|
10
10
|
);
|
|
11
11
|
});
|
|
@@ -14,7 +14,7 @@ describe("readme", () => {
|
|
|
14
14
|
throw new Error("face");
|
|
15
15
|
}).toThrowErrorMatching(
|
|
16
16
|
expect.objectContaining({
|
|
17
|
-
stack: expect.stringContaining("readme.spec.ts"),
|
|
17
|
+
stack: expect.stringContaining("readme.spec.ts") as unknown,
|
|
18
18
|
}),
|
|
19
19
|
);
|
|
20
20
|
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import "../../../../src/matchers/setup";
|
|
3
|
+
|
|
4
|
+
describe("toHaveHeader", () => {
|
|
5
|
+
const r = new Response("", {
|
|
6
|
+
headers: {
|
|
7
|
+
"Content-Type": "image/png",
|
|
8
|
+
},
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("should pass when the property exists", async () => {
|
|
12
|
+
expect(r).toHaveHeader("Content-Type");
|
|
13
|
+
});
|
|
14
|
+
it("should pass when the property doesn't exist", async () => {
|
|
15
|
+
expect(r).not.toHaveHeader("face");
|
|
16
|
+
});
|
|
17
|
+
it("should pass when the property value is correct", async () => {
|
|
18
|
+
expect(r).toHaveHeader("Content-Type", "image/png");
|
|
19
|
+
});
|
|
20
|
+
it("should pass when the property value is incorrect", async () => {
|
|
21
|
+
expect(r).not.toHaveHeader("Content-Type", 15);
|
|
22
|
+
expect(r).not.toHaveHeader("face", 15);
|
|
23
|
+
});
|
|
24
|
+
it("should error when the property should not exist but does", async () => {
|
|
25
|
+
expect(() =>
|
|
26
|
+
expect(r).not.toHaveHeader("Content-Type"),
|
|
27
|
+
).toThrowErrorMatchingInlineSnapshot(`[Error: Received should not have the header "Content-Type"]`);
|
|
28
|
+
});
|
|
29
|
+
it("should error when the property should exist but doesn't", async () => {
|
|
30
|
+
expect(() =>
|
|
31
|
+
expect(r).toHaveHeader("face"),
|
|
32
|
+
).toThrowErrorMatchingInlineSnapshot(`[Error: Received does not have the header "face"]`);
|
|
33
|
+
});
|
|
34
|
+
it("should error when the value should not match but does", async () => {
|
|
35
|
+
expect(() =>
|
|
36
|
+
expect(r).not.toHaveHeader("Content-Type", "image/png"),
|
|
37
|
+
).toThrowErrorMatchingInlineSnapshot(`[Error: Header "Content-Type" should not be expected value]`);
|
|
38
|
+
});
|
|
39
|
+
it("should error when the value should match but doesn't", async () => {
|
|
40
|
+
expect(() =>
|
|
41
|
+
expect(r).toHaveHeader("Content-Type", 17),
|
|
42
|
+
).toThrowErrorMatchingInlineSnapshot(`[Error: Header "Content-Type" is not expected value]`);
|
|
43
|
+
});
|
|
44
|
+
it("should error when a non-buffer is passed in", async () => {
|
|
45
|
+
expect(() =>
|
|
46
|
+
expect(1234).toHaveHeader("face"),
|
|
47
|
+
).toThrowErrorMatchingInlineSnapshot(`[TypeError: Cannot read properties of undefined (reading 'has')]`);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import "../../../../src/matchers/setup";
|
|
3
|
+
|
|
4
|
+
const getResponse = (status: number): Response =>
|
|
5
|
+
new Response("", {
|
|
6
|
+
status,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
describe("toHaveStatus", () => {
|
|
10
|
+
const status200 = getResponse(200);
|
|
11
|
+
const status404 = getResponse(404);
|
|
12
|
+
|
|
13
|
+
it("should pass when the status is correct", async () => {
|
|
14
|
+
expect(status200).toHaveStatus(200);
|
|
15
|
+
expect(status404).toHaveStatus(404);
|
|
16
|
+
});
|
|
17
|
+
it("should pass when the status is incorrect", async () => {
|
|
18
|
+
expect(status200).not.toHaveStatus(500);
|
|
19
|
+
expect(status404).not.toHaveStatus(500);
|
|
20
|
+
});
|
|
21
|
+
it("should error when the value should not match but does", async () => {
|
|
22
|
+
expect(() =>
|
|
23
|
+
expect(status200).not.toHaveStatus(200),
|
|
24
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
25
|
+
`[Error: Status should not be expected value]`,
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
it("should error when the value should match but doesn't", async () => {
|
|
29
|
+
expect(() =>
|
|
30
|
+
expect(status200).toHaveStatus(500),
|
|
31
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
32
|
+
`[Error: Status is not expected value]`,
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
it("should error when a non-Response is passed in", async () => {
|
|
36
|
+
expect(() =>
|
|
37
|
+
expect(1234).toHaveStatus(3),
|
|
38
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
39
|
+
`[Error: Status is not expected value]`,
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import "../../../src/matchers/setup";
|
|
3
|
+
|
|
4
|
+
describe("toHaveExifProperty", () => {
|
|
5
|
+
const buffer = Buffer.from(
|
|
6
|
+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2O4v3EyBwAGwgIsY3pRcwAAAABJRU5ErkJggg==",
|
|
7
|
+
"base64url",
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
it("should pass when the property exists", async () => {
|
|
11
|
+
await expect(buffer).toHaveExifProperty("ImageWidth");
|
|
12
|
+
});
|
|
13
|
+
it("should pass when the property doesn't exist", async () => {
|
|
14
|
+
await expect(buffer).not.toHaveExifProperty("face");
|
|
15
|
+
});
|
|
16
|
+
it("should pass when the property value is correct", async () => {
|
|
17
|
+
await expect(buffer).toHaveExifProperty("ImageHeight", 1);
|
|
18
|
+
await expect(buffer).toHaveExifProperty("ImageWidth", 1);
|
|
19
|
+
});
|
|
20
|
+
it("should pass when the property value is incorrect", async () => {
|
|
21
|
+
await expect(buffer).not.toHaveExifProperty("ImageWidth", 15);
|
|
22
|
+
await expect(buffer).not.toHaveExifProperty("face", 15);
|
|
23
|
+
});
|
|
24
|
+
it("should error when the property should not exist but does", async () => {
|
|
25
|
+
await expect(async () =>
|
|
26
|
+
expect(buffer).not.toHaveExifProperty("ImageWidth"),
|
|
27
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
28
|
+
`[Error: Received should not have the property "ImageWidth"]`,
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
it("should error when the property should exist but doesn't", async () => {
|
|
32
|
+
await expect(async () =>
|
|
33
|
+
expect(buffer).toHaveExifProperty("face"),
|
|
34
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
35
|
+
`[Error: Received does not have the property "face"]`,
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
it("should error when the value should not match but does", async () => {
|
|
39
|
+
await expect(async () =>
|
|
40
|
+
expect(buffer).not.toHaveExifProperty("ImageWidth", 1),
|
|
41
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
42
|
+
`[Error: Property "ImageWidth" should not be expected value]`,
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
it("should error when the value should match but doesn't", async () => {
|
|
46
|
+
await expect(async () =>
|
|
47
|
+
expect(buffer).toHaveExifProperty("ImageWidth", 17),
|
|
48
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
49
|
+
`[Error: Property "ImageWidth" is not expected value]`,
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
it("should error when a non-buffer is passed in", async () => {
|
|
53
|
+
await expect(async () =>
|
|
54
|
+
expect(1234).toHaveExifProperty("face"),
|
|
55
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
56
|
+
`[Error: Invalid input argument]`,
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -8,14 +8,14 @@ describe("matchers", () => {
|
|
|
8
8
|
expect.objectContaining({
|
|
9
9
|
message: "face",
|
|
10
10
|
name: "Error",
|
|
11
|
-
stack: expect.any(String),
|
|
11
|
+
stack: expect.any(String) as unknown,
|
|
12
12
|
}),
|
|
13
13
|
);
|
|
14
14
|
});
|
|
15
15
|
it("should match an error by substring", () => {
|
|
16
16
|
expect(new Error("your stupid face")).toMatchError(
|
|
17
17
|
expect.objectContaining({
|
|
18
|
-
message: expect.stringContaining("stupid"),
|
|
18
|
+
message: expect.stringContaining("stupid") as unknown,
|
|
19
19
|
}),
|
|
20
20
|
);
|
|
21
21
|
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import htmlSerializer from "../../../src/serializers/html.js";
|
|
4
|
+
expect.addSnapshotSerializer(htmlSerializer);
|
|
5
|
+
|
|
6
|
+
describe("serializers", () => {
|
|
7
|
+
describe("html", () => {
|
|
8
|
+
it("should prettier format the html", () => {
|
|
9
|
+
expect(
|
|
10
|
+
`<html><head><title>your face</title></head><body>is stupid</body></html>`,
|
|
11
|
+
).toMatchInlineSnapshot(`
|
|
12
|
+
"<!-- Formatted HTML -->
|
|
13
|
+
<html>
|
|
14
|
+
<head>
|
|
15
|
+
<title>your face</title>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
is stupid
|
|
19
|
+
</body>
|
|
20
|
+
</html>"
|
|
21
|
+
`);
|
|
22
|
+
});
|
|
23
|
+
it("should ignore already formatted html", () => {
|
|
24
|
+
const html = `
|
|
25
|
+
<html>
|
|
26
|
+
<head>
|
|
27
|
+
<title>your face</title>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
is stupid
|
|
31
|
+
</body>
|
|
32
|
+
</html>"
|
|
33
|
+
`;
|
|
34
|
+
expect(html).toMatchInlineSnapshot(`
|
|
35
|
+
"
|
|
36
|
+
<html>
|
|
37
|
+
<head>
|
|
38
|
+
<title>your face</title>
|
|
39
|
+
</head>
|
|
40
|
+
<body>
|
|
41
|
+
is stupid
|
|
42
|
+
</body>
|
|
43
|
+
</html>"
|
|
44
|
+
"
|
|
45
|
+
`);
|
|
46
|
+
});
|
|
47
|
+
it("should ignore non-html strings", () => {
|
|
48
|
+
expect(`I'm just some content that isn't html`).toMatchInlineSnapshot(
|
|
49
|
+
`"I'm just some content that isn't html"`,
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
it("should ignore malformed html", () => {
|
|
53
|
+
expect(`<html><p>oiajsdfpoiajsdf</a></q></face>`).toMatchInlineSnapshot(`
|
|
54
|
+
"<html><p>oiajsdfpoiajsdf</a></q></face>"
|
|
55
|
+
`);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import urlSerializer from "../../../src/serializers/url.js";
|
|
4
|
+
expect.addSnapshotSerializer(urlSerializer);
|
|
5
|
+
|
|
6
|
+
describe("serializers", () => {
|
|
7
|
+
describe("url", () => {
|
|
8
|
+
it("should remove the port in a localhost http[s] url", () => {
|
|
9
|
+
expect(
|
|
10
|
+
`String with http://localhost:1234/path/to/face url`,
|
|
11
|
+
).toMatchInlineSnapshot(`"String with http://localhost/path/to/face url"`);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
});
|