mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
feat: Introduce Feature for file-system operations
Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>
This commit is contained in:
parent
5101101da2
commit
d56bd1bd69
6
packages/utility-features/file-system/.eslintrc.json
Normal file
6
packages/utility-features/file-system/.eslintrc.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "@k8slens/eslint-config/eslint",
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json"
|
||||
}
|
||||
}
|
||||
1
packages/utility-features/file-system/.prettierrc
Normal file
1
packages/utility-features/file-system/.prettierrc
Normal file
@ -0,0 +1 @@
|
||||
"@k8slens/eslint-config/prettier"
|
||||
21
packages/utility-features/file-system/index.ts
Normal file
21
packages/utility-features/file-system/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export type { DeleteFile } from "./src/delete-file/delete-file.injectable";
|
||||
export type { PathExists } from "./src/path-exists/path-exists.injectable";
|
||||
export type { ReadFile } from "./src/read-file/read-file.injectable";
|
||||
export type { ReadJsonFile } from "./src/read-json-file/read-json-file.injectable";
|
||||
export type { ReadYamlFile } from "./src/read-yaml-file/read-yaml-file.injectable";
|
||||
export type { WriteFile } from "./src/write-file/write-file.injectable";
|
||||
export type { WriteJsonFile } from "./src/write-json-file/write-json-file.injectable";
|
||||
export type { WriteYamlFile } from "./src/write-yaml-file/write-yaml-file.injectable";
|
||||
|
||||
export { deleteFileInjectionToken } from "./src/delete-file/delete-file.injectable";
|
||||
export { pathExistsInjectionToken } from "./src/path-exists/path-exists.injectable";
|
||||
export { readFileInjectionToken } from "./src/read-file/read-file.injectable";
|
||||
export { readJsonFileInjectionToken } from "./src/read-json-file/read-json-file.injectable";
|
||||
export { readYamlFileInjectionToken } from "./src/read-yaml-file/read-yaml-file.injectable";
|
||||
export { writeFileInjectionToken } from "./src/write-file/write-file.injectable";
|
||||
export { writeJsonFileInjectionToken } from "./src/write-json-file/write-json-file.injectable";
|
||||
export { writeYamlFileInjectionToken } from "./src/write-yaml-file/write-yaml-file.injectable";
|
||||
|
||||
export { fileSystemFeature } from "./src/feature";
|
||||
|
||||
export { testUtils } from "./src/test-utils";
|
||||
1
packages/utility-features/file-system/jest.config.js
Normal file
1
packages/utility-features/file-system/jest.config.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = require("@k8slens/jest").monorepoPackageConfig(__dirname).configForNode;
|
||||
43
packages/utility-features/file-system/package.json
Normal file
43
packages/utility-features/file-system/package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@k8slens/file-system",
|
||||
"private": false,
|
||||
"version": "0.1.0",
|
||||
"description": "TBD",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lensapp/lens.git"
|
||||
},
|
||||
"type": "commonjs",
|
||||
"author": {
|
||||
"name": "OpenLens Authors",
|
||||
"email": "info@k8slens.dev"
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/lensapp/lens",
|
||||
"scripts": {
|
||||
"build": "lens-webpack-build",
|
||||
"clean": "rimraf dist/",
|
||||
"test:unit": "jest --coverage --runInBand",
|
||||
"lint": "lens-lint",
|
||||
"lint:fix": "lens-lint --fix"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@k8slens/feature-core": "^6.5.0-alpha.5",
|
||||
"@ogre-tools/fp": "^16.1.0",
|
||||
"@ogre-tools/injectable": "^16.1.0",
|
||||
"@ogre-tools/injectable-extension-for-auto-registration": "^16.1.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lodash": "^4.17.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@k8slens/eslint-config": "^6.5.0-alpha.3",
|
||||
"@k8slens/react-testing-library-discovery": "^1.0.0-alpha.4",
|
||||
"@k8slens/webpack": "^6.5.0-alpha.6"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import fsInjectable from "../fs/fs.injectable";
|
||||
import type { AsyncCallSuccess } from "@lensapp/utils";
|
||||
import { getSuccess } from "@lensapp/utils";
|
||||
|
||||
export type DeleteFile = (filePath: string) => AsyncCallSuccess<void>;
|
||||
|
||||
export const deleteFileInjectionToken = getInjectionToken<DeleteFile>({
|
||||
id: "delete-file-injection-token",
|
||||
});
|
||||
|
||||
const deleteFileInjectable = getInjectable({
|
||||
id: "delete-file",
|
||||
|
||||
instantiate: (di): DeleteFile => {
|
||||
const unlink = di.inject(fsInjectable).unlink;
|
||||
|
||||
return async (filePath) => {
|
||||
await unlink(filePath);
|
||||
|
||||
return getSuccess(undefined);
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: deleteFileInjectionToken,
|
||||
});
|
||||
|
||||
export default deleteFileInjectable;
|
||||
@ -0,0 +1,56 @@
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import { registerFeature } from "@lensapp/feature-core";
|
||||
import { fileSystemFeature } from "../feature";
|
||||
import asyncFn, { AsyncFnMock } from "@async-fn/jest";
|
||||
import fsInjectable from "../fs/fs.injectable";
|
||||
import deleteFileInjectable, { DeleteFile } from "./delete-file.injectable";
|
||||
import { AsyncCallResult, getSuccess } from "@lensapp/utils";
|
||||
|
||||
describe("delete-file", () => {
|
||||
let fsUnlinkMock: AsyncFnMock<(filePath: string) => Promise<void>>;
|
||||
let deleteFile: DeleteFile;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = createContainer("irrelevant");
|
||||
|
||||
registerFeature(di, fileSystemFeature);
|
||||
|
||||
fsUnlinkMock = asyncFn();
|
||||
|
||||
const fsStub = {
|
||||
unlink: fsUnlinkMock,
|
||||
};
|
||||
|
||||
di.override(fsInjectable, () => fsStub as any);
|
||||
|
||||
deleteFile = di.inject(deleteFileInjectable);
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let actualPromise: AsyncCallResult<void>;
|
||||
|
||||
beforeEach(() => {
|
||||
actualPromise = deleteFile("./some-directory/some-file.js");
|
||||
});
|
||||
|
||||
it("calls for unlink from file system", () => {
|
||||
expect(fsUnlinkMock).toHaveBeenCalledWith("./some-directory/some-file.js");
|
||||
});
|
||||
|
||||
it("when unlink resolves, resolves with success", async () => {
|
||||
await fsUnlinkMock.resolve();
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual(getSuccess(undefined));
|
||||
});
|
||||
|
||||
it("when unlink rejects, rejects with the original error", () => {
|
||||
const someError = new Error("some-error");
|
||||
|
||||
fsUnlinkMock.reject(someError);
|
||||
|
||||
return expect(actualPromise).rejects.toBe(someError);
|
||||
});
|
||||
});
|
||||
});
|
||||
15
packages/utility-features/file-system/src/feature.ts
Normal file
15
packages/utility-features/file-system/src/feature.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration";
|
||||
import { getFeature } from "@k8slens/feature-core";
|
||||
|
||||
export const fileSystemFeature = getFeature({
|
||||
id: "fs",
|
||||
|
||||
register: (di) => {
|
||||
autoRegister({
|
||||
di,
|
||||
targetModule: module,
|
||||
|
||||
getRequireContexts: () => [require.context("./", true, /\.injectable\.(ts|tsx)$/)],
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,10 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import fse from "fs-extra";
|
||||
|
||||
const fsInjectable = getInjectable({
|
||||
id: "fs",
|
||||
instantiate: () => fse,
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default fsInjectable;
|
||||
15
packages/utility-features/file-system/src/fs/fs.test.ts
Normal file
15
packages/utility-features/file-system/src/fs/fs.test.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import fsInjectable from "./fs.injectable";
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import fse from "fs-extra";
|
||||
|
||||
describe("fs", () => {
|
||||
it("is fs-extra", () => {
|
||||
const di = createContainer("irrelevant");
|
||||
|
||||
di.register(fsInjectable);
|
||||
|
||||
const fs = di.inject(fsInjectable);
|
||||
|
||||
expect(fs).toBe(fse);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,27 @@
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import fsInjectable from "../fs/fs.injectable";
|
||||
import type { AsyncCallSuccess } from "@lensapp/utils";
|
||||
import { getSuccess } from "@lensapp/utils";
|
||||
|
||||
export type PathExists = (path: string) => AsyncCallSuccess<boolean>;
|
||||
|
||||
export const pathExistsInjectionToken = getInjectionToken<PathExists>({
|
||||
id: "path-exists-injection-token",
|
||||
});
|
||||
|
||||
const pathExistsInjectable = getInjectable({
|
||||
id: "path-exists",
|
||||
instantiate: (di): PathExists => {
|
||||
const pathExists = di.inject(fsInjectable).pathExists;
|
||||
|
||||
return async (filePath: string) => {
|
||||
const result = await pathExists(filePath);
|
||||
|
||||
return getSuccess(result);
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: pathExistsInjectionToken,
|
||||
});
|
||||
|
||||
export default pathExistsInjectable;
|
||||
@ -0,0 +1,58 @@
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import { registerFeature } from "@lensapp/feature-core";
|
||||
import { fileSystemFeature } from "../feature";
|
||||
import asyncFn, { AsyncFnMock } from "@async-fn/jest";
|
||||
import fsInjectable from "../fs/fs.injectable";
|
||||
import type { PathExists } from "./path-exists.injectable";
|
||||
import pathExistsInjectable from "./path-exists.injectable";
|
||||
import type { AsyncCallResult } from "@lensapp/utils";
|
||||
import { getSuccess } from "@lensapp/utils";
|
||||
|
||||
describe("path-exists", () => {
|
||||
let fsPathExistsMock: AsyncFnMock<(filePath: string) => Promise<boolean>>;
|
||||
let pathExists: PathExists;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = createContainer("irrelevant");
|
||||
|
||||
registerFeature(di, fileSystemFeature);
|
||||
|
||||
fsPathExistsMock = asyncFn();
|
||||
|
||||
const fsStub = {
|
||||
pathExists: fsPathExistsMock,
|
||||
};
|
||||
|
||||
di.override(fsInjectable, () => fsStub as any);
|
||||
|
||||
pathExists = di.inject(pathExistsInjectable);
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let actualPromise: AsyncCallResult<boolean>;
|
||||
|
||||
beforeEach(() => {
|
||||
actualPromise = pathExists("./some-directory/some-file.js");
|
||||
});
|
||||
|
||||
it("calls for filesystem", () => {
|
||||
expect(fsPathExistsMock).toHaveBeenCalledWith("./some-directory/some-file.js");
|
||||
});
|
||||
|
||||
it("when call resolves with true, resolves with true", async () => {
|
||||
await fsPathExistsMock.resolve(true);
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual(getSuccess(true));
|
||||
});
|
||||
|
||||
it("when call resolves with false, resolves with false", async () => {
|
||||
await fsPathExistsMock.resolve(false);
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual(getSuccess(false));
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,12 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import path from "path";
|
||||
|
||||
export type GetDirnameOfPath = (path: string) => string;
|
||||
|
||||
const getDirnameOfPathInjectable = getInjectable({
|
||||
id: "get-dirname-of-path",
|
||||
instantiate: (): GetDirnameOfPath => path.dirname,
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default getDirnameOfPathInjectable;
|
||||
@ -0,0 +1,15 @@
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import path from "path";
|
||||
import getDirnameOfPathInjectable from "./get-dirname.injectable";
|
||||
|
||||
describe("get-dirname", () => {
|
||||
it("is exactly dirname from path module", () => {
|
||||
const di = createContainer("irrelevant");
|
||||
|
||||
di.register(getDirnameOfPathInjectable);
|
||||
|
||||
const getDirnameOfPath = di.inject(getDirnameOfPathInjectable);
|
||||
|
||||
expect(getDirnameOfPath).toBe(path.dirname);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,28 @@
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import fsInjectable from "../fs/fs.injectable";
|
||||
import type { AsyncCallSuccess } from "@lensapp/utils";
|
||||
import { getSuccess } from "@lensapp/utils";
|
||||
|
||||
export type ReadFile = (filePath: string) => AsyncCallSuccess<string>;
|
||||
|
||||
export const readFileInjectionToken = getInjectionToken<ReadFile>({
|
||||
id: "read-file-injection-token",
|
||||
});
|
||||
|
||||
const readFileInjectable = getInjectable({
|
||||
id: "read-file",
|
||||
|
||||
instantiate: (di): ReadFile => {
|
||||
const { readFile } = di.inject(fsInjectable);
|
||||
|
||||
return async (filePath) => {
|
||||
const response = await readFile(filePath, "utf-8");
|
||||
|
||||
return getSuccess(response);
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: readFileInjectionToken,
|
||||
});
|
||||
|
||||
export default readFileInjectable;
|
||||
@ -0,0 +1,59 @@
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import { registerFeature } from "@lensapp/feature-core";
|
||||
import { fileSystemFeature } from "../feature";
|
||||
import fsInjectable from "../fs/fs.injectable";
|
||||
import asyncFn, { AsyncFnMock } from "@async-fn/jest";
|
||||
import type { ReadFile } from "./read-file.injectable";
|
||||
import readFileInjectable from "./read-file.injectable";
|
||||
import type { CallResult } from "@lensapp/utils";
|
||||
import { getSuccess } from "@lensapp/utils";
|
||||
|
||||
describe("read-file", () => {
|
||||
let fsReadFileMock: AsyncFnMock<(fileName: string, encoding: string) => Promise<string>>;
|
||||
|
||||
let readFile: ReadFile;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = createContainer("irrelevant");
|
||||
|
||||
registerFeature(di, fileSystemFeature);
|
||||
|
||||
fsReadFileMock = asyncFn();
|
||||
|
||||
const fsStub = {
|
||||
readFile: fsReadFileMock,
|
||||
};
|
||||
|
||||
di.override(fsInjectable, () => fsStub as any);
|
||||
|
||||
readFile = di.inject(readFileInjectable);
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let actualPromise: Promise<CallResult<string>>;
|
||||
|
||||
beforeEach(() => {
|
||||
actualPromise = readFile("some-file.js");
|
||||
});
|
||||
|
||||
it("calls for file from file system", () => {
|
||||
expect(fsReadFileMock).toHaveBeenCalledWith("some-file.js", "utf-8");
|
||||
});
|
||||
|
||||
it("when reading file resolves, resolves with success", async () => {
|
||||
await fsReadFileMock.resolve("some-content");
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual(getSuccess("some-content"));
|
||||
});
|
||||
|
||||
it("when reading file rejects, throws", async () => {
|
||||
const someError = new Error("some-error");
|
||||
|
||||
fsReadFileMock.reject(someError);
|
||||
|
||||
return expect(actualPromise).rejects.toBe(someError);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,28 @@
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { AsyncCallSuccess } from "@lensapp/utils";
|
||||
import { getSuccess } from "@lensapp/utils";
|
||||
import readFileInjectable from "../read-file/read-file.injectable";
|
||||
|
||||
export type ReadJsonFile = (filePath: string) => AsyncCallSuccess<any>;
|
||||
|
||||
export const readJsonFileInjectionToken = getInjectionToken<ReadJsonFile>({
|
||||
id: "read-json-file-injection-token",
|
||||
});
|
||||
|
||||
const readJsonFileInjectable = getInjectable({
|
||||
id: "read-json-file",
|
||||
|
||||
instantiate: (di): ReadJsonFile => {
|
||||
const readFile = di.inject(readFileInjectable);
|
||||
|
||||
return async (filePath) => {
|
||||
const call = await readFile(filePath);
|
||||
|
||||
return getSuccess(JSON.parse(call.response));
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: readJsonFileInjectionToken,
|
||||
});
|
||||
|
||||
export default readJsonFileInjectable;
|
||||
@ -0,0 +1,57 @@
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import readJsonFileInjectable, { ReadJsonFile } from "./read-json-file.injectable";
|
||||
import { registerFeature } from "@lensapp/feature-core";
|
||||
import { fileSystemFeature } from "../feature";
|
||||
import { CallResult, getSuccess } from "@lensapp/utils";
|
||||
import asyncFn, { AsyncFnMock } from "@async-fn/jest";
|
||||
import fsInjectable from "../fs/fs.injectable";
|
||||
|
||||
describe("read-json-file", () => {
|
||||
let fsReadFileMock: AsyncFnMock<(fileName: string, encoding: string) => Promise<string>>;
|
||||
|
||||
let readJsonFile: ReadJsonFile;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = createContainer("irrelevant");
|
||||
|
||||
registerFeature(di, fileSystemFeature);
|
||||
|
||||
fsReadFileMock = asyncFn();
|
||||
|
||||
const fsStub = {
|
||||
readFile: fsReadFileMock,
|
||||
};
|
||||
|
||||
di.override(fsInjectable, () => fsStub as any);
|
||||
|
||||
readJsonFile = di.inject(readJsonFileInjectable);
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let actualPromise: Promise<CallResult<any>>;
|
||||
|
||||
beforeEach(() => {
|
||||
actualPromise = readJsonFile("some-file.js");
|
||||
});
|
||||
|
||||
it("calls for file from file system", () => {
|
||||
expect(fsReadFileMock).toHaveBeenCalledWith("some-file.js", "utf-8");
|
||||
});
|
||||
|
||||
it("when reading file resolves, resolves with success", async () => {
|
||||
await fsReadFileMock.resolve(JSON.stringify({ some: "content" }));
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual(getSuccess({ some: "content" }));
|
||||
});
|
||||
|
||||
it("when reading file rejects, throws", async () => {
|
||||
const someError = new Error("some-error");
|
||||
|
||||
fsReadFileMock.reject(someError);
|
||||
|
||||
return expect(actualPromise).rejects.toBe(someError);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,31 @@
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { AsyncCallSuccess } from "@lensapp/utils";
|
||||
import { getSuccess } from "@lensapp/utils";
|
||||
import readFileInjectable from "../read-file/read-file.injectable";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
export type ReadYamlFile = (filePath: string) => AsyncCallSuccess<object>;
|
||||
|
||||
export const readYamlFileInjectionToken = getInjectionToken<ReadYamlFile>({
|
||||
id: "read-yaml-file-injection-token",
|
||||
});
|
||||
|
||||
const readYamlFileInjectable = getInjectable({
|
||||
id: "read-yaml-file",
|
||||
|
||||
instantiate: (di): ReadYamlFile => {
|
||||
const readFile = di.inject(readFileInjectable);
|
||||
|
||||
return async (filePath: string) => {
|
||||
const call = await readFile(filePath);
|
||||
|
||||
const parsedResponse = yaml.load(call.response) as object;
|
||||
|
||||
return getSuccess(parsedResponse);
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: readYamlFileInjectionToken,
|
||||
});
|
||||
|
||||
export default readYamlFileInjectable;
|
||||
@ -0,0 +1,57 @@
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import readYamlFileInjectable, { ReadYamlFile } from "./read-yaml-file.injectable";
|
||||
import { registerFeature } from "@lensapp/feature-core";
|
||||
import { fileSystemFeature } from "../feature";
|
||||
import { CallResult, getSuccess } from "@lensapp/utils";
|
||||
import asyncFn, { AsyncFnMock } from "@async-fn/jest";
|
||||
import fsInjectable from "../fs/fs.injectable";
|
||||
|
||||
describe("read-yaml-file", () => {
|
||||
let fsReadFileMock: AsyncFnMock<(fileName: string, encoding: string) => Promise<string>>;
|
||||
|
||||
let readYamlFile: ReadYamlFile;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = createContainer("irrelevant");
|
||||
|
||||
registerFeature(di, fileSystemFeature);
|
||||
|
||||
fsReadFileMock = asyncFn();
|
||||
|
||||
const fsStub = {
|
||||
readFile: fsReadFileMock,
|
||||
};
|
||||
|
||||
di.override(fsInjectable, () => fsStub as any);
|
||||
|
||||
readYamlFile = di.inject(readYamlFileInjectable);
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let actualPromise: Promise<CallResult<object>>;
|
||||
|
||||
beforeEach(() => {
|
||||
actualPromise = readYamlFile("some-file.js");
|
||||
});
|
||||
|
||||
it("calls for file from file system", () => {
|
||||
expect(fsReadFileMock).toHaveBeenCalledWith("some-file.js", "utf-8");
|
||||
});
|
||||
|
||||
it("when reading file resolves, resolves with success", async () => {
|
||||
await fsReadFileMock.resolve("some: content");
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual(getSuccess({ some: "content" }));
|
||||
});
|
||||
|
||||
it("when reading file rejects, throws", async () => {
|
||||
const someError = new Error("some-error");
|
||||
|
||||
fsReadFileMock.reject(someError);
|
||||
|
||||
return expect(actualPromise).rejects.toBe(someError);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,3 @@
|
||||
import { overrideFsWithFakes } from "./override-fs-with-fakes";
|
||||
|
||||
export const testUtils = { overrideFsWithFakes };
|
||||
@ -0,0 +1,47 @@
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import readFileInjectable from "../read-file/read-file.injectable";
|
||||
import pathExistsInjectable from "../path-exists/path-exists.injectable";
|
||||
import deleteFileInjectable from "../delete-file/delete-file.injectable";
|
||||
import { getSuccess } from "@lensapp/utils";
|
||||
import writeFileInjectable from "../write-file/write-file.injectable";
|
||||
|
||||
export const overrideFsWithFakes = (di: DiContainer, state = new Map()) => {
|
||||
const readFile = readFileFor(state);
|
||||
|
||||
di.override(readFileInjectable, () => readFile);
|
||||
|
||||
di.override(writeFileInjectable, () => async (filePath, contents) => {
|
||||
state.set(filePath, contents);
|
||||
|
||||
return getSuccess(undefined);
|
||||
});
|
||||
|
||||
di.override(
|
||||
pathExistsInjectable,
|
||||
() => (filePath: string) => Promise.resolve(getSuccess(state.has(filePath)))
|
||||
);
|
||||
|
||||
di.override(deleteFileInjectable, () => async (filePath: string) => {
|
||||
state.delete(filePath);
|
||||
|
||||
return getSuccess(undefined);
|
||||
});
|
||||
};
|
||||
|
||||
const readFileFor = (state: Map<string, string>) => (filePath: string) => {
|
||||
const fileContent = state.get(filePath);
|
||||
|
||||
if (!fileContent) {
|
||||
const existingFilePaths = [...state.keys()].join('", "');
|
||||
|
||||
const error = new Error(
|
||||
`Tried to access file ${filePath} which does not exist. Existing file paths are: "${existingFilePaths}"`
|
||||
);
|
||||
|
||||
(error as any).code = "ENOENT";
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return Promise.resolve(getSuccess(fileContent));
|
||||
};
|
||||
@ -0,0 +1,40 @@
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { WriteFileOptions } from "fs-extra";
|
||||
import fsInjectable from "../fs/fs.injectable";
|
||||
import getDirnameOfPathInjectable from "../path/get-dirname.injectable";
|
||||
import { AsyncCallSuccess, getSuccess } from "@lensapp/utils";
|
||||
|
||||
export type WriteFile = (
|
||||
filePath: string,
|
||||
content: string | Buffer,
|
||||
opts?: WriteFileOptions
|
||||
) => AsyncCallSuccess<void>;
|
||||
|
||||
export const writeFileInjectionToken = getInjectionToken<WriteFile>({
|
||||
id: "write-file-injection-token",
|
||||
});
|
||||
|
||||
const writeFileInjectable = getInjectable({
|
||||
id: "write-file",
|
||||
|
||||
instantiate: (di): WriteFile => {
|
||||
const { writeFile, ensureDir } = di.inject(fsInjectable);
|
||||
const getDirnameOfPath = di.inject(getDirnameOfPathInjectable);
|
||||
|
||||
return async (filePath, content) => {
|
||||
await ensureDir(getDirnameOfPath(filePath), {
|
||||
mode: 0o755,
|
||||
});
|
||||
|
||||
await writeFile(filePath, content, {
|
||||
encoding: "utf-8",
|
||||
});
|
||||
|
||||
return getSuccess(undefined);
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: writeFileInjectionToken,
|
||||
});
|
||||
|
||||
export default writeFileInjectable;
|
||||
@ -0,0 +1,85 @@
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import { registerFeature } from "@lensapp/feature-core";
|
||||
import { fileSystemFeature } from "../feature";
|
||||
import asyncFn, { AsyncFnMock } from "@async-fn/jest";
|
||||
import fsInjectable from "../fs/fs.injectable";
|
||||
import writeFileInjectable, { WriteFile } from "./write-file.injectable";
|
||||
import { AsyncCallResult, getSuccess } from "@lensapp/utils";
|
||||
|
||||
describe("write-file", () => {
|
||||
let fsWriteFileMock: AsyncFnMock<(filePath: string) => Promise<void>>;
|
||||
let fsEnsureDirMock: AsyncFnMock<(directoryPath: string) => Promise<void>>;
|
||||
let writeFile: WriteFile;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = createContainer("irrelevant");
|
||||
|
||||
registerFeature(di, fileSystemFeature);
|
||||
|
||||
fsWriteFileMock = asyncFn();
|
||||
fsEnsureDirMock = asyncFn();
|
||||
|
||||
const fsStub = {
|
||||
writeFile: fsWriteFileMock,
|
||||
ensureDir: fsEnsureDirMock,
|
||||
};
|
||||
|
||||
di.override(fsInjectable, () => fsStub as any);
|
||||
|
||||
writeFile = di.inject(writeFileInjectable);
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let actualPromise: AsyncCallResult<void>;
|
||||
|
||||
beforeEach(() => {
|
||||
actualPromise = writeFile("./some-directory/some-other-directory/some-file.js", "some-content");
|
||||
});
|
||||
|
||||
it("makes sure that directory exists", () => {
|
||||
expect(fsEnsureDirMock).toHaveBeenCalledWith("./some-directory/some-other-directory", { mode: 493 });
|
||||
});
|
||||
|
||||
it("does not write file yet", () => {
|
||||
expect(fsWriteFileMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("when ensuring existence of directory is resolves with success", () => {
|
||||
beforeEach(async () => {
|
||||
await fsEnsureDirMock.resolve();
|
||||
});
|
||||
|
||||
it("writes file to filesystem", () => {
|
||||
expect(fsWriteFileMock).toHaveBeenCalledWith(
|
||||
"./some-directory/some-other-directory/some-file.js",
|
||||
"some-content",
|
||||
{ encoding: "utf-8" },
|
||||
);
|
||||
});
|
||||
|
||||
it("when writing resolves with success, resolves with success", async () => {
|
||||
await fsWriteFileMock.resolve();
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual(getSuccess(undefined));
|
||||
});
|
||||
|
||||
it("when writing rejects with failure, resolves with failure", () => {
|
||||
const someError = new Error("some-error");
|
||||
|
||||
fsWriteFileMock.reject(someError);
|
||||
|
||||
return expect(actualPromise).rejects.toBe(someError);
|
||||
});
|
||||
});
|
||||
|
||||
it("when ensuring existence of directory rejects, rejects with the original error", () => {
|
||||
const someError = new Error("some-error");
|
||||
|
||||
fsEnsureDirMock.reject(someError);
|
||||
|
||||
return expect(actualPromise).rejects.toBe(someError);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,28 @@
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { JsonValue } from "type-fest";
|
||||
import writeFileInjectable from "../write-file/write-file.injectable";
|
||||
import type { AsyncCallSuccess } from "@lensapp/utils";
|
||||
|
||||
export type WriteJsonFile = (
|
||||
filePath: string,
|
||||
contents: JsonValue
|
||||
) => AsyncCallSuccess<void>;
|
||||
|
||||
export const writeJsonFileInjectionToken = getInjectionToken<WriteJsonFile>({
|
||||
id: "write-json-file-injection-token",
|
||||
});
|
||||
|
||||
const writeJsonFileInjectable = getInjectable({
|
||||
id: "write-json-file",
|
||||
|
||||
instantiate: (di): WriteJsonFile => {
|
||||
const writeFile = di.inject(writeFileInjectable);
|
||||
|
||||
return async (filePath, content) =>
|
||||
writeFile(filePath, JSON.stringify(content, null, 2));
|
||||
},
|
||||
|
||||
injectionToken: writeJsonFileInjectionToken,
|
||||
});
|
||||
|
||||
export default writeJsonFileInjectable;
|
||||
@ -0,0 +1,86 @@
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import { registerFeature } from "@lensapp/feature-core";
|
||||
import { fileSystemFeature } from "../feature";
|
||||
import asyncFn, { AsyncFnMock } from "@async-fn/jest";
|
||||
import fsInjectable from "../fs/fs.injectable";
|
||||
import { AsyncCallResult, getSuccess } from "@lensapp/utils";
|
||||
import type { WriteJsonFile } from "./write-json-file.injectable";
|
||||
import writeJsonFileInjectable from "./write-json-file.injectable";
|
||||
|
||||
describe("write-json-file", () => {
|
||||
let fsWriteFileMock: AsyncFnMock<(filePath: string) => Promise<void>>;
|
||||
let fsEnsureDirMock: AsyncFnMock<(directoryPath: string) => Promise<void>>;
|
||||
let writeJsonFile: WriteJsonFile;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = createContainer("irrelevant");
|
||||
|
||||
registerFeature(di, fileSystemFeature);
|
||||
|
||||
fsWriteFileMock = asyncFn();
|
||||
fsEnsureDirMock = asyncFn();
|
||||
|
||||
const fsStub = {
|
||||
writeFile: fsWriteFileMock,
|
||||
ensureDir: fsEnsureDirMock,
|
||||
};
|
||||
|
||||
di.override(fsInjectable, () => fsStub as any);
|
||||
|
||||
writeJsonFile = di.inject(writeJsonFileInjectable);
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let actualPromise: AsyncCallResult<void>;
|
||||
|
||||
beforeEach(() => {
|
||||
actualPromise = writeJsonFile("./some-directory/some-other-directory/some-file.json", { some: "content" });
|
||||
});
|
||||
|
||||
it("makes sure that directory exists", () => {
|
||||
expect(fsEnsureDirMock).toHaveBeenCalledWith("./some-directory/some-other-directory", { mode: 493 });
|
||||
});
|
||||
|
||||
it("does not write file yet", () => {
|
||||
expect(fsWriteFileMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("when ensuring existence of directory is resolves with success", () => {
|
||||
beforeEach(async () => {
|
||||
await fsEnsureDirMock.resolve();
|
||||
});
|
||||
|
||||
it("writes file to filesystem", () => {
|
||||
expect(fsWriteFileMock).toHaveBeenCalledWith(
|
||||
"./some-directory/some-other-directory/some-file.json",
|
||||
JSON.stringify({ some: "content" }, null, 2),
|
||||
{ encoding: "utf-8" },
|
||||
);
|
||||
});
|
||||
|
||||
it("when writing resolves with success, resolves with success", async () => {
|
||||
await fsWriteFileMock.resolve();
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual(getSuccess(undefined));
|
||||
});
|
||||
|
||||
it("when writing rejects with failure, resolves with failure", () => {
|
||||
const someError = new Error("some-error");
|
||||
|
||||
fsWriteFileMock.reject(someError);
|
||||
|
||||
return expect(actualPromise).rejects.toBe(someError);
|
||||
});
|
||||
});
|
||||
|
||||
it("when ensuring existence of directory rejects, rejects with the original error", () => {
|
||||
const someError = new Error("some-error");
|
||||
|
||||
fsEnsureDirMock.reject(someError);
|
||||
|
||||
return expect(actualPromise).rejects.toBe(someError);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,27 @@
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import writeFileInjectable from "../write-file/write-file.injectable";
|
||||
import type { AsyncCallSuccess } from "@lensapp/utils";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
export type WriteYamlFile = (
|
||||
filePath: string,
|
||||
contents: object
|
||||
) => AsyncCallSuccess<void>;
|
||||
|
||||
export const writeYamlFileInjectionToken = getInjectionToken<WriteYamlFile>({
|
||||
id: "write-yaml-file-injection-token",
|
||||
});
|
||||
|
||||
const writeYamlFileInjectable = getInjectable({
|
||||
id: "write-yaml-file",
|
||||
|
||||
instantiate: (di): WriteYamlFile => {
|
||||
const writeFile = di.inject(writeFileInjectable);
|
||||
|
||||
return async (filePath, content) => writeFile(filePath, yaml.dump(content));
|
||||
},
|
||||
|
||||
injectionToken: writeYamlFileInjectionToken,
|
||||
});
|
||||
|
||||
export default writeYamlFileInjectable;
|
||||
@ -0,0 +1,85 @@
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import { registerFeature } from "@lensapp/feature-core";
|
||||
import { fileSystemFeature } from "../feature";
|
||||
import asyncFn, { AsyncFnMock } from "@async-fn/jest";
|
||||
import fsInjectable from "../fs/fs.injectable";
|
||||
import { AsyncCallResult, getSuccess } from "@lensapp/utils";
|
||||
import writeYamlFileInjectable, { WriteYamlFile } from "./write-yaml-file.injectable";
|
||||
|
||||
describe("write-yaml-file", () => {
|
||||
let fsWriteFileMock: AsyncFnMock<(filePath: string) => Promise<void>>;
|
||||
let fsEnsureDirMock: AsyncFnMock<(directoryPath: string) => Promise<void>>;
|
||||
let writeYamlFile: WriteYamlFile;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = createContainer("irrelevant");
|
||||
|
||||
registerFeature(di, fileSystemFeature);
|
||||
|
||||
fsWriteFileMock = asyncFn();
|
||||
fsEnsureDirMock = asyncFn();
|
||||
|
||||
const fsStub = {
|
||||
writeFile: fsWriteFileMock,
|
||||
ensureDir: fsEnsureDirMock,
|
||||
};
|
||||
|
||||
di.override(fsInjectable, () => fsStub as any);
|
||||
|
||||
writeYamlFile = di.inject(writeYamlFileInjectable);
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let actualPromise: AsyncCallResult<void>;
|
||||
|
||||
beforeEach(() => {
|
||||
actualPromise = writeYamlFile("./some-directory/some-other-directory/some-file.yml", { some: "content" });
|
||||
});
|
||||
|
||||
it("makes sure that directory exists", () => {
|
||||
expect(fsEnsureDirMock).toHaveBeenCalledWith("./some-directory/some-other-directory", { mode: 493 });
|
||||
});
|
||||
|
||||
it("does not write file yet", () => {
|
||||
expect(fsWriteFileMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("when ensuring existence of directory is resolves with success", () => {
|
||||
beforeEach(async () => {
|
||||
await fsEnsureDirMock.resolve();
|
||||
});
|
||||
|
||||
it("writes file to filesystem", () => {
|
||||
expect(fsWriteFileMock).toHaveBeenCalledWith(
|
||||
"./some-directory/some-other-directory/some-file.yml",
|
||||
"some: content\n",
|
||||
{ encoding: "utf-8" },
|
||||
);
|
||||
});
|
||||
|
||||
it("when writing resolves with success, resolves with success", async () => {
|
||||
await fsWriteFileMock.resolve();
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual(getSuccess(undefined));
|
||||
});
|
||||
|
||||
it("when writing rejects with failure, resolves with failure", () => {
|
||||
const someError = new Error("some-error");
|
||||
|
||||
fsWriteFileMock.reject(someError);
|
||||
|
||||
return expect(actualPromise).rejects.toBe(someError);
|
||||
});
|
||||
});
|
||||
|
||||
it("when ensuring existence of directory rejects, rejects with the original error", () => {
|
||||
const someError = new Error("some-error");
|
||||
|
||||
fsEnsureDirMock.reject(someError);
|
||||
|
||||
return expect(actualPromise).rejects.toBe(someError);
|
||||
});
|
||||
});
|
||||
});
|
||||
4
packages/utility-features/file-system/tsconfig.json
Normal file
4
packages/utility-features/file-system/tsconfig.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "@k8slens/typescript/config/base.json",
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
1
packages/utility-features/file-system/webpack.config.js
Normal file
1
packages/utility-features/file-system/webpack.config.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = require("@k8slens/webpack").configForNode;
|
||||
Loading…
Reference in New Issue
Block a user