diff --git a/android/app/build.gradle b/android/app/build.gradle index bb9fedf9..bb6cfa91 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -6,8 +6,8 @@ android { applicationId "com.moimob.drinkable" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 11800 - versionName "1.18.0" + versionCode 11801 + versionName "1.18.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/fastlane/metadata/android/en-US/changelogs/11801.txt b/fastlane/metadata/android/en-US/changelogs/11801.txt new file mode 100644 index 00000000..e69ea36b --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/11801.txt @@ -0,0 +1,2 @@ +• Fixed filter Cocktails by Ingredient to include substitute ingredients +• Fixed issue with showing mocktails \ No newline at end of file diff --git a/jest.config.ts b/jest.config.ts index 9fa15367..034c90fd 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -8,6 +8,7 @@ export default async (): Promise => { '^.+\\.(ts|tsx)$': 'ts-jest' }, testEnvironment: 'jsdom', - moduleDirectories: ['node_modules', 'src'] + moduleDirectories: ['node_modules', 'src'], + setupFiles: ['./tests/jest-setup.ts'] }; }; diff --git a/package-lock.json b/package-lock.json index 4a4cd0e4..bb1776bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,7 @@ "postcss-loader": "7.0.2", "prettier": "^2.7.1", "prettier-plugin-organize-attributes": "^0.0.5", + "reflect-metadata": "^0.1.13", "sass": "1.57.1", "sass-loader": "13.2.0", "style-loader": "3.3.1", @@ -16107,6 +16108,12 @@ "node": ">= 0.10" } }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, "node_modules/regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", @@ -30971,6 +30978,12 @@ "resolve": "^1.1.6" } }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, "regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", diff --git a/package.json b/package.json index 3373938a..c17f1b8d 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "postcss-loader": "7.0.2", "prettier": "^2.7.1", "prettier-plugin-organize-attributes": "^0.0.5", + "reflect-metadata": "^0.1.13", "sass": "1.57.1", "sass-loader": "13.2.0", "style-loader": "3.3.1", @@ -87,4 +88,4 @@ "engines": { "node": ">=10.12.0" } -} \ No newline at end of file +} diff --git a/src/components/dialogs/welcome-dialog.ts b/src/components/dialogs/welcome-dialog.ts index 77bf99c6..f648ab09 100644 --- a/src/components/dialogs/welcome-dialog.ts +++ b/src/components/dialogs/welcome-dialog.ts @@ -6,7 +6,7 @@ import { CocktailService } from 'services/cocktail-service'; @inject(DialogController, LocalStorageService, CocktailService) export class WelcomeDialog { - public showMocktails: boolean; + public showMocktails: boolean = false; public controller: DialogController; public messuarementSystems = [MessuarementSystem.Imperial, MessuarementSystem.Metric]; diff --git a/src/domain/mappers/ingredient-mapper.ts b/src/domain/mappers/ingredient-mapper.ts new file mode 100644 index 00000000..4f986394 --- /dev/null +++ b/src/domain/mappers/ingredient-mapper.ts @@ -0,0 +1,12 @@ +import { Ingredient } from 'domain/entities/ingredient'; + +export class IngredientMapper { + static toIngredientAndReplacementIds(ingredient: Ingredient) { + let ids: string[] = [ingredient.id]; + + if (ingredient.replacementIds !== undefined) { + ids.push(...ingredient.replacementIds); + } + return ids; + } +} diff --git a/src/modules/cocktails/all-cocktails/all-cocktails.ts b/src/modules/cocktails/all-cocktails/all-cocktails.ts index 959d81e5..f175aaf0 100644 --- a/src/modules/cocktails/all-cocktails/all-cocktails.ts +++ b/src/modules/cocktails/all-cocktails/all-cocktails.ts @@ -76,9 +76,11 @@ export class AllCocktails { } if (this._filterDialogModel.ingredientFilter !== null) { - cocktails = cocktails.filter(x => - x.ingredientGroups.some(y => y.ingredientId === this._filterDialogModel.ingredientFilter) + let ingredientIds = this._ingredientService.getIngredientAndReplacementIds( + this._filterDialogModel.ingredientFilter ); + + cocktails = cocktails.filter(x => x.ingredientGroups.some(x => ingredientIds.includes(x.ingredientId))); filterCount++; } diff --git a/src/services/cocktail-service.ts b/src/services/cocktail-service.ts index a43f5d9e..23c845e6 100644 --- a/src/services/cocktail-service.ts +++ b/src/services/cocktail-service.ts @@ -11,7 +11,7 @@ export class CocktailService { private _cocktails: Cocktail[] = getStaticCocktails(); private _createdCocktails: Cocktail[] = []; private _cocktailInformation: CocktailInformation[] = []; - private _tempMocktails: Cocktail[] = []; + private _mocktails: Cocktail[] = []; private _highestId = 0; constructor(private _localStorageService: LocalStorageService, private _ingredientService: IngredientService) { this._createdCocktails = this._localStorageService.getCocktails(); @@ -34,16 +34,13 @@ export class CocktailService { } }); + this._mocktails = this._cocktails.filter(x => x.category === DrinkCategory.Mocktail); + if (this._localStorageService.getSettings().showMocktails !== true) { - this.populateTempMocktails(); + this.hideMocktails(); } } - private populateTempMocktails() { - this._tempMocktails = this._cocktails.filter(x => x.category === DrinkCategory.Mocktail); - this._cocktails = this._cocktails.filter(x => !this._tempMocktails.map(y => y.id).includes(x.id)); - } - public getCocktails() { return [...this._cocktails].sort((a, b) => a.name?.localeCompare(b.name)); } @@ -156,11 +153,10 @@ export class CocktailService { } public updateShowMocktails(value: boolean) { - if (value) { - this._cocktails.push(...this._tempMocktails); - this._tempMocktails = []; + if (value === true) { + this._cocktails.push(...this._mocktails); } else { - this.populateTempMocktails(); + this.hideMocktails(); } } @@ -188,4 +184,7 @@ export class CocktailService { return false; } + private hideMocktails() { + this._cocktails = this._cocktails.filter(x => !this._mocktails.map(y => y.id).includes(x.id)); + } } diff --git a/src/services/ingredient-service.ts b/src/services/ingredient-service.ts index 18665a62..7cd616fa 100644 --- a/src/services/ingredient-service.ts +++ b/src/services/ingredient-service.ts @@ -5,6 +5,7 @@ import { CreatedIngredientModel, Ingredient, ManageIngredientModel } from 'domai import { SpiritType } from 'domain/enums/spirit-type'; import { getStaticIngredients } from 'data/ingredient-data'; import { LocalStorageService } from './local-storage-service'; +import { IngredientMapper } from 'domain/mappers/ingredient-mapper'; @inject(LocalStorageService, I18N) export class IngredientService { @@ -61,7 +62,7 @@ export class IngredientService { } public getIngredientById(id: string): Ingredient { - return this.getIngredients().find(x => x.id === id); + return this._ingredients.find(x => x.id === id); } public getIngredientsBySpiritType(spirit: SpiritType): Ingredient[] { @@ -104,7 +105,7 @@ export class IngredientService { })); } - public async createIngredient(name: string): Promise { + public async createIngredient(name: string) { const ingredient = new Ingredient(); ingredient.name = name; ingredient.id = this.setIngredientId(); @@ -140,6 +141,15 @@ export class IngredientService { this._ingredients = this._ingredients.filter(x => x.id !== id); } + public getIngredientAndReplacementIds(id: string): string[] { + let ingredient = this._ingredients.find(x => x.id === id); + if (ingredient === undefined) { + return []; + } + + return IngredientMapper.toIngredientAndReplacementIds(ingredient); + } + private setIngredientId(): string { this._highestId++; return 'x-' + this._highestId; diff --git a/tests/jest-setup.ts b/tests/jest-setup.ts new file mode 100644 index 00000000..d2c9bc6e --- /dev/null +++ b/tests/jest-setup.ts @@ -0,0 +1 @@ +import 'reflect-metadata'; diff --git a/tests/mappers/ingredient-mapper.test.ts b/tests/mappers/ingredient-mapper.test.ts new file mode 100644 index 00000000..543d40f5 --- /dev/null +++ b/tests/mappers/ingredient-mapper.test.ts @@ -0,0 +1,21 @@ +import { getStaticIngredients } from 'data/ingredient-data'; +import { Ingredient } from 'domain/entities/ingredient'; +import { IngredientMapper } from 'domain/mappers/ingredient-mapper'; + +describe('IngredientMapper', () => { + test('toIngredientAndReplacementIds - No replacementIds', () => { + let ingredient = getStaticIngredients()[0] as Ingredient; + + let result = IngredientMapper.toIngredientAndReplacementIds(ingredient); + + expect(result).toStrictEqual(['1']); + }); + + test('toIngredientAndReplacementIds - With replacementIds', () => { + let ingredient = getStaticIngredients()[18] as Ingredient; + + let result = IngredientMapper.toIngredientAndReplacementIds(ingredient); + + expect(result).toStrictEqual(['19', '38', '92']); + }); +}); diff --git a/tests/services/ingredient-service.test.ts b/tests/services/ingredient-service.test.ts new file mode 100644 index 00000000..47ba9c54 --- /dev/null +++ b/tests/services/ingredient-service.test.ts @@ -0,0 +1,77 @@ +import { IngredientService } from 'services/ingredient-service'; +import { LocalStorageService } from 'services/local-storage-service'; +import { I18N } from 'aurelia-i18n'; + +describe('IngredientService', () => { + let localStorageService: LocalStorageService; + let sut: IngredientService; + + beforeEach(async () => { + localStorageService = new LocalStorageService(); + await localStorageService.initialize(); + + let i18n = new I18N(null, null); + jest.spyOn(i18n, 'tr').mockReturnValue('name'); + sut = new IngredientService(localStorageService, i18n); + }); + + afterEach(() => { + window.localStorage.clear(); + }); + + describe('GET', () => { + test('Get Ingredients - No initial state', () => { + expect(sut.getIngredients().length).toBeGreaterThan(0); + expect(sut.getCreatedIngredients()).toHaveLength(0); + }); + + test('Get Random Ingredients', () => { + expect(sut.getRandomIngredients(0)).toHaveLength(0); + expect(sut.getRandomIngredients(3)).toHaveLength(3); + }); + + test('getIngredientAndReplacementIds - Ingredient not found', () => { + expect(sut.getIngredientAndReplacementIds('error')).toStrictEqual([]); + }); + }); + + describe('Create', () => { + test('Create Ingredient', async () => { + let key = 'CapacitorStorage.ingredients'; + expect(window.localStorage.getItem(key)).toBeNull(); + + let ingredient = await sut.createIngredient('Test'); + expect(ingredient.id).toBe('x-1'); + + expect(window.localStorage.getItem(key)).toBeTruthy(); + expect(sut.getCreatedIngredients()).toStrictEqual([ingredient]); + expect(sut.getIngredients()).toContain(ingredient); + }); + }); + + describe('Update', () => { + test('Update Ingredient', async () => { + let ingredient = await sut.createIngredient('Test'); + + let updatedIngredient = { ...ingredient }; + updatedIngredient.name = 'updated'; + + await sut.updateIngredient(updatedIngredient); + + expect(ingredient.id).toBe('x-1'); + expect(sut.getCreatedIngredients()).toStrictEqual([updatedIngredient]); + expect(sut.getIngredients()).toContain(updatedIngredient); + }); + }); + + describe('Delete', () => { + test('Delete Ingredient', async () => { + let ingredient = await sut.createIngredient('Test'); + + await sut.deleteIngredient(ingredient.id); + + expect(sut.getCreatedIngredients()).toStrictEqual([]); + expect(sut.getIngredientById(ingredient.id)).toBeUndefined(); + }); + }); +});