@altano/vitest-plugins 0.0.2 → 0.1.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @altano/vitest-plugins@0.0.2 lint /home/runner/work/npm-packages/npm-packages/packages/vitest-plugins
2
+ > @altano/vitest-plugins@0.1.0 lint /home/runner/work/npm-packages/npm-packages/packages/vitest-plugins
3
3
  > eslint package.json src/**/*.ts*
4
4
 
5
5
  Warning: React version was set to "detect" in eslint-plugin-react settings, but the "react" package is not installed. Assuming latest React version for linting.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @altano/vitest-plugins
2
2
 
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f2dda42: new filesystem matchers
8
+
3
9
  ## 0.0.2
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -90,7 +90,7 @@ If you want to assert anything more complicated (e.g. an error contains some sub
90
90
 
91
91
  ### toMatchError
92
92
 
93
- Assert on any part of an error object (e.g. the stack):
93
+ Verify any part of an error object (e.g. the stack):
94
94
 
95
95
  ```ts
96
96
  expect(new Error("face")).toMatchError(
@@ -102,7 +102,7 @@ expect(new Error("face")).toMatchError(
102
102
 
103
103
  ### toThrowErrorMatching
104
104
 
105
- Assert on any part of a _thrown_ error object (e.g. the stack):
105
+ Verify any part of a _thrown_ error object (e.g. the stack):
106
106
 
107
107
  ```ts
108
108
  expect(() => {
@@ -113,3 +113,35 @@ expect(() => {
113
113
  }),
114
114
  );
115
115
  ```
116
+
117
+ ### toBePath
118
+
119
+ Verify the realpath (canonical path) of expected. More lenient than a string check when dealing with logical paths, symlinks, etc.
120
+
121
+ ```ts
122
+ expect("/private/some/path").toBePath("/some/path");
123
+ ```
124
+
125
+ ### toBeFile
126
+
127
+ Verify a file exists
128
+
129
+ ```ts
130
+ expect(import.meta.filename).toBeFile();
131
+ ```
132
+
133
+ ### toBeDirectory
134
+
135
+ Verify a directory exists
136
+
137
+ ```ts
138
+ expect("/").toBeDirectory();
139
+ ```
140
+
141
+ ### toEqualFile
142
+
143
+ Verify that the given path matches the contents of another file
144
+
145
+ ```ts
146
+ expect("/some/file.txt").toEqualFile("/other/file.txt");
147
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@altano/vitest-plugins",
3
- "version": "0.0.2",
3
+ "version": "0.1.0",
4
4
  "description": "Custom matchers and snapshot serializers to enhance vitest",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -38,6 +38,7 @@
38
38
  "typescript": "^5.4.2",
39
39
  "unified": "^10.1.2",
40
40
  "vfile": "^5.3.7",
41
+ "vite": "^5.2.8",
41
42
  "vitest": "^1.4.0",
42
43
  "@altano/tsconfig": "0.0.0",
43
44
  "eslint-config-altano": "0.0.0"
@@ -1,9 +1,17 @@
1
1
  import { expect } from "vitest";
2
2
 
3
+ import toBePath from "./toBePath";
4
+ import toBeFile from "./toBeFile";
5
+ import toBeDirectory from "./toBeDirectory";
6
+ import toEqualFile from "./toEqualFile";
3
7
  import toMatchError from "./toMatchError";
4
8
  import toThrowErrorMatching from "./toThrowErrorMatching";
5
9
 
6
10
  expect.extend({
11
+ toBePath,
12
+ toBeFile,
13
+ toBeDirectory,
14
+ toEqualFile,
7
15
  toMatchError,
8
16
  toThrowErrorMatching,
9
17
  });
@@ -0,0 +1,21 @@
1
+ import type { Matcher } from "./matcher";
2
+ import fs from "node:fs";
3
+
4
+ function directoryExists(path: string): boolean {
5
+ try {
6
+ const stats = fs.statSync(path);
7
+ return stats.isDirectory();
8
+ } catch (e) {
9
+ return false;
10
+ }
11
+ }
12
+
13
+ const toBeDirectory: Matcher = function (path: string) {
14
+ return {
15
+ message: () =>
16
+ `Received is not a directory (or cannot be accessed by the current user)`,
17
+ pass: directoryExists(path),
18
+ };
19
+ };
20
+
21
+ export default toBeDirectory;
@@ -0,0 +1,22 @@
1
+ import type { Matcher } from "./matcher";
2
+ import fs from "node:fs";
3
+
4
+ function fileExists(path: string): boolean {
5
+ try {
6
+ const stats = fs.statSync(path);
7
+ return stats.isFile();
8
+ } catch (e) {
9
+ return false;
10
+ }
11
+ }
12
+
13
+ const toBeFile: Matcher = function (path: string) {
14
+ return {
15
+ message: () =>
16
+ `${path} is not a file (or cannot be accessed by the current user)`,
17
+ pass: fileExists(path),
18
+ actual: path,
19
+ };
20
+ };
21
+
22
+ export default toBeFile;
@@ -0,0 +1,27 @@
1
+ import type { Matcher } from "./matcher";
2
+ import { realpathSync } from "node:fs";
3
+
4
+ function getRealpath(path: string): string | null {
5
+ try {
6
+ return realpathSync(path);
7
+ } catch (_e) {
8
+ return null;
9
+ }
10
+ }
11
+
12
+ const toBePath: Matcher = function (received: string, expected: string) {
13
+ const receivedRealpath = getRealpath(received);
14
+ const expectedRealpath = getRealpath(expected);
15
+
16
+ return {
17
+ message: () =>
18
+ receivedRealpath == null || expectedRealpath == null
19
+ ? `One of the paths wasn't an accessible file on disk and couldn't be compared`
20
+ : `The realpath of the received path did not match the expected one`,
21
+ pass: this.equals(receivedRealpath, expectedRealpath),
22
+ actual: receivedRealpath,
23
+ expected: expectedRealpath,
24
+ };
25
+ };
26
+
27
+ export default toBePath;
@@ -0,0 +1,26 @@
1
+ import type { Matcher } from "./matcher";
2
+ import fs from "node:fs";
3
+ import crypto from "node:crypto";
4
+
5
+ function getChecksum(filepath: string): string {
6
+ const hash = crypto.createHash("md5");
7
+ hash.update(fs.readFileSync(filepath));
8
+ return hash.digest("hex");
9
+ }
10
+
11
+ const toEqualFile: Matcher = function (
12
+ receivedPathOfFile: string,
13
+ pathOfFileWithExpectedContents: string,
14
+ ) {
15
+ const receivedChecksum = getChecksum(receivedPathOfFile);
16
+ const expectedChecksum = getChecksum(pathOfFileWithExpectedContents);
17
+ return {
18
+ message: () =>
19
+ `The checksum of the file contents of the files do not match`,
20
+ pass: this.equals(receivedChecksum, expectedChecksum),
21
+ actual: `Checksum = ${receivedChecksum}`,
22
+ expected: `Checksum = ${expectedChecksum}`,
23
+ };
24
+ };
25
+
26
+ export default toEqualFile;
@@ -3,6 +3,13 @@
3
3
  import type { Assertion, AsymmetricMatchersContaining } from "vitest";
4
4
 
5
5
  interface CustomMatchers<R = unknown> {
6
+ /**
7
+ * compare the realpath (canonical path) of expected/received
8
+ */
9
+ toBePath(expected: string): R;
10
+ toBeFile(): R;
11
+ toBeDirectory(): R;
12
+ toEqualFile(pathOfFileWithExpectedContents: string): R;
6
13
  toMatchError(expected: unknown): R;
7
14
  toThrowErrorMatching(expected: unknown): R;
8
15
  }
@@ -0,0 +1,31 @@
1
+ import os from "node:os";
2
+ import { describe, expect, it } from "vitest";
3
+ import "../../../src/matchers/setup";
4
+
5
+ describe("matchers", () => {
6
+ describe("toBeDirectory", () => {
7
+ it("should recognize the root", () => {
8
+ expect("/").toBeDirectory();
9
+ });
10
+ it("should recognize the home directory", () => {
11
+ expect(os.homedir()).toBeDirectory();
12
+ });
13
+ it("should recognize this test's directory", () => {
14
+ expect(import.meta.dirname).toBeDirectory();
15
+ });
16
+ it("should not match on garbage", () => {
17
+ expect("fucking garbage").not.toBeDirectory();
18
+ });
19
+ it("should not match on a made up dir", () => {
20
+ expect("/some/plausible/horseshit").not.toBeDirectory();
21
+ });
22
+ it("should NOT recognize this test file as a directory", () => {
23
+ expect(import.meta.filename).not.toBeDirectory();
24
+ });
25
+ it("should throw an error when not a directory", () => {
26
+ expect(() => {
27
+ expect("face").toBeDirectory();
28
+ }).toThrowErrorMatchingInlineSnapshot(`[Error: Received is not a directory (or cannot be accessed by the current user)]`);
29
+ });
30
+ });
31
+ });
@@ -0,0 +1,20 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import "../../../src/matchers/setup";
3
+
4
+ describe("matchers", () => {
5
+ describe("toBeFile", () => {
6
+ it("should recognize this test file as a file", () => {
7
+ expect(import.meta.filename).toBeFile();
8
+ });
9
+ it("should NOT recognize this test directory as a file", () => {
10
+ expect(import.meta.dirname).not.toBeFile();
11
+ });
12
+ it("should throw an error when not a file", () => {
13
+ expect(() => {
14
+ expect("face").toBeFile();
15
+ }).toThrowErrorMatchingInlineSnapshot(
16
+ `[Error: face is not a file (or cannot be accessed by the current user)]`,
17
+ );
18
+ });
19
+ });
20
+ });
@@ -0,0 +1,30 @@
1
+ import path from "node:path";
2
+ import { describe, expect, it } from "vitest";
3
+ import "../../../src/matchers/setup";
4
+
5
+ describe("matchers", () => {
6
+ describe("toBePath", () => {
7
+ it("should recognize this test file as a file", () => {
8
+ const weirdPath = path.join(
9
+ import.meta.dirname,
10
+ "..",
11
+ path.basename(import.meta.dirname),
12
+ );
13
+ expect(import.meta.dirname).toBePath(weirdPath);
14
+ });
15
+ it("should throw an error for a non-matching path", () => {
16
+ const wrongPath = path.join(import.meta.dirname, "..");
17
+ expect(() => {
18
+ expect(import.meta.dirname).toBePath(wrongPath);
19
+ }).toThrowErrorMatchingInlineSnapshot(`[Error: The realpath of the received path did not match the expected one]`);
20
+ });
21
+ it("should throw an error for a path that isn't accessible", () => {
22
+ const nonExistentPath = path.join(import.meta.dirname, "face");
23
+ expect(() => {
24
+ expect(import.meta.dirname).toBePath(nonExistentPath);
25
+ }).toThrowErrorMatchingInlineSnapshot(
26
+ `[Error: One of the paths wasn't an accessible file on disk and couldn't be compared]`,
27
+ );
28
+ });
29
+ });
30
+ });
@@ -0,0 +1,26 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import "../../../src/matchers/setup";
3
+ import path from "node:path";
4
+
5
+ describe("matchers", () => {
6
+ describe("toEqualFile", () => {
7
+ // Okay this is a silly test but *shrug*
8
+ it("should produce a hash of this test file", () => {
9
+ expect(import.meta.filename).toEqualFile(import.meta.filename);
10
+ });
11
+ it("should fail when given two differing files", async () => {
12
+ expect(() => {
13
+ expect(import.meta.filename).toEqualFile(
14
+ path.join(import.meta.dirname, "toBePath.spec.ts"),
15
+ );
16
+ }).toThrowErrorMatchingInlineSnapshot(
17
+ `[Error: The checksum of the file contents of the files do not match]`,
18
+ );
19
+ });
20
+ it("should fail when given a non-existent file", async () => {
21
+ expect(() => {
22
+ expect("faaaaaaaaaace").toEqualFile("khaaaaaaaaan");
23
+ }).toThrowErrorMatchingInlineSnapshot(`[Error: ENOENT: no such file or directory, open 'faaaaaaaaaace']`);
24
+ });
25
+ });
26
+ });