Skip to content

Commit

Permalink
test: currency conversion (oliverschwendener#1215)
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverschwendener authored Sep 23, 2024
1 parent e8803eb commit 6f5b95f
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 15 deletions.
84 changes: 84 additions & 0 deletions src/main/Extensions/CurrencyConversion/CurrencyConversion.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import type { AssetPathResolver } from "@Core/AssetPathResolver";
import { describe, expect, it, vi } from "vitest";
import { CurrencyConversion } from "./CurrencyConversion";
import type { Rates } from "./Rates";

describe(CurrencyConversion, () => {
describe(CurrencyConversion.prototype.getInstantSearchResultItems, () => {
const testSuccessfulConversion = ({
expectedResult,
userInput,
rates,
}: {
expectedResult: string;
userInput: string;
rates: Rates;
}) => {
const imageFilePath = "/path/to/image";
const getExtensionAssetPathMock = vi.fn().mockReturnValue(imageFilePath);

const assetPathResolver = <AssetPathResolver>{
getExtensionAssetPath: (i, f) => getExtensionAssetPathMock(i, f),
getModuleAssetPath: () => null,
};

const currencyConversion = new CurrencyConversion(null, null, assetPathResolver);

currencyConversion["rates"] = rates;

const actual = currencyConversion.getInstantSearchResultItems(userInput);

expect(actual[0].name).toEqual(expectedResult);
expect(actual[0].image).toEqual({ url: `file://${imageFilePath}` });
expect(getExtensionAssetPathMock).toHaveBeenCalledWith(currencyConversion.id, "currency-conversion.png");
};

it("should return empty array when user input does not contain 4 parts", () => {
const currencyConversion = new CurrencyConversion(null, null, null);

currencyConversion["rates"] = { chf: { usd: 2 } };

expect(currencyConversion.getInstantSearchResultItems("1 CHF to")).toEqual([]);
});

it("should return empty array when first part in user input is not numerical", () => {
const currencyConversion = new CurrencyConversion(null, null, null);

currencyConversion["rates"] = { chf: { usd: 2 } };

expect(currencyConversion.getInstantSearchResultItems("abc CHF to USD")).toEqual([]);
});

it("should return empty array when base currency does not exist", () => {
const currencyConversion = new CurrencyConversion(null, null, null);

currencyConversion["rates"] = { chf: { usd: 2 } };

expect(currencyConversion.getInstantSearchResultItems("1 EUR to USD")).toEqual([]);
});

it("should return empty array when second part of user input is not 'in' or 'to'", () => {
const currencyConversion = new CurrencyConversion(null, null, null);

currencyConversion["rates"] = { chf: { usd: 2 } };

expect(currencyConversion.getInstantSearchResultItems("1 CHF inn USD")).toEqual([]);
expect(currencyConversion.getInstantSearchResultItems("1 CHF t USD")).toEqual([]);
});

it("should return empty array when target currency is not found", () => {
const currencyConversion = new CurrencyConversion(null, null, null);

currencyConversion["rates"] = { chf: { usd: 2 } };

expect(currencyConversion.getInstantSearchResultItems("1 CHF to EUR")).toEqual([]);
});

it("should convert currencies based on the rates when user input matches expected pattern", () => {
const rates: Rates = { chf: { usd: 2, eur: 0.5 } };

testSuccessfulConversion({ expectedResult: "2.00 USD", userInput: "1 CHF to USD", rates });
testSuccessfulConversion({ expectedResult: "0.50 EUR", userInput: "1 CHF in EUR", rates });
});
});
});
28 changes: 13 additions & 15 deletions src/main/Extensions/CurrencyConversion/CurrencyConversion.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { AssetPathResolver } from "@Core/AssetPathResolver";
import type { Extension } from "@Core/Extension";
import type { SettingsManager } from "@Core/SettingsManager";
import { SearchResultItemActionUtility, type SearchResultItem } from "@common/Core";
import { getExtensionSettingKey } from "@common/Core/Extension";
import type { Image } from "@common/Core/Image";
import type { AssetPathResolver } from "@Core/AssetPathResolver";
import type { Extension } from "@Core/Extension";
import type { SettingsManager } from "@Core/SettingsManager";
import type { Net } from "electron";
import { convert } from "./convert";
import type { Rates } from "./Rates";

export class CurrencyConversion implements Extension {
private static readonly translationNamespace = "extension[CurrencyConversion]";
Expand All @@ -26,7 +28,7 @@ export class CurrencyConversion implements Extension {
currencies: ["usd", "chf", "eur"],
};

private readonly rates: Record<string, Record<string, number>>;
private rates: Rates;

public constructor(
private readonly settingsManager: SettingsManager,
Expand All @@ -53,16 +55,16 @@ export class CurrencyConversion implements Extension {
}
}

const conversionResult = this.convert({
value: Number(parts[0]),
base: parts[1],
target: parts[3],
});
const value = Number(parts[0]);
const base = parts[1];
const target = parts[3];

const conversionResult = convert({ value, base, target, rates: this.rates });

return [
{
defaultAction: SearchResultItemActionUtility.createCopyToClipboardAction({
textToCopy: conversionResult.toFixed(2),
textToCopy: conversionResult.result.toFixed(2),
description: "Currency Conversion",
descriptionTranslation: {
key: "copyToClipboard",
Expand All @@ -76,7 +78,7 @@ export class CurrencyConversion implements Extension {
},
id: `currency-conversion:instant-result`,
image: this.getImage(),
name: `${conversionResult.toFixed(2)} ${parts[3].toUpperCase()}`,
name: `${conversionResult.result.toFixed(2)} ${parts[3].toUpperCase()}`,
},
];
}
Expand Down Expand Up @@ -123,10 +125,6 @@ export class CurrencyConversion implements Extension {
return [getExtensionSettingKey(this.id, "currencies")];
}

private convert({ value, base, target }: { value: number; base: string; target: string }): number {
return value * this.rates[base.toLowerCase()][target.toLowerCase()];
}

private async setRates(): Promise<void> {
const currencies = this.settingsManager.getValue(
getExtensionSettingKey(this.id, "currencies"),
Expand Down
1 change: 1 addition & 0 deletions src/main/Extensions/CurrencyConversion/Rates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Rates = Record<string, Record<string, number>>;
33 changes: 33 additions & 0 deletions src/main/Extensions/CurrencyConversion/convert.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { describe, expect, it } from "vitest";
import { convert } from "./convert";
import type { Rates } from "./Rates";

describe(convert, () => {
it("should convert the base currency to the target according to the rates", () => {
const rates: Rates = {
chf: {
usd: 2,
eur: 0.5,
},
usd: {
chf: 0.5,
eur: 0.25,
},
};

expect(convert({ base: "CHF", rates, target: "USD", value: 1 })).toEqual({ result: 2, target: "USD" });
expect(convert({ base: "CHF", rates, target: "EUR", value: 10 })).toEqual({ result: 5, target: "EUR" });
});

it("should throw an error if the base currency is not found", () => {
expect(() => convert({ base: "CHF", rates: { usd: { chf: 0.5 } }, target: "USD", value: 1 })).toThrowError(
"Base currency not found",
);
});

it("should throw an error if the target currency is not found", () => {
expect(() => convert({ base: "CHF", rates: { chf: { eur: 0.5 } }, target: "USD", value: 1 })).toThrowError(
"Target currency not found",
);
});
});
26 changes: 26 additions & 0 deletions src/main/Extensions/CurrencyConversion/convert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { Rates } from "./Rates";

export const convert = ({
value,
base,
target,
rates,
}: {
value: number;
base: string;
target: string;
rates: Rates;
}): { result: number; target: string } => {
if (!Object.keys(rates).includes(base.toLowerCase())) {
throw new Error("Base currency not found");
}

if (!Object.keys(rates[base.toLowerCase()]).includes(target.toLowerCase())) {
throw new Error("Target currency not found");
}

return {
result: value * rates[base.toLowerCase()][target.toLowerCase()],
target,
};
};

0 comments on commit 6f5b95f

Please sign in to comment.