From 8f0c5e21ad54171eb07093af9c403a571f6e12ba Mon Sep 17 00:00:00 2001 From: smilerz Date: Wed, 8 Dec 2021 09:01:20 -0600 Subject: [PATCH] basic tests with new factories --- cookbook/models.py | 3 + cookbook/serializer.py | 1 - .../api/test_api_shopping_list_entryv2.py | 60 +-- cookbook/tests/conftest.py | 16 +- cookbook/tests/factories/__init__.py | 51 +- vue/package-lock.json | 487 ++++++++++-------- vue/src/apps/RecipeView/RecipeView.vue | 84 ++- vue/src/components/AddRecipeToBook.vue | 122 +++++ vue/src/utils/openapi/api.ts | 18 + 9 files changed, 555 insertions(+), 287 deletions(-) create mode 100644 vue/src/components/AddRecipeToBook.vue diff --git a/cookbook/models.py b/cookbook/models.py index 4a5921789..c55d95b47 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -67,6 +67,9 @@ class TreeManager(MP_NodeManager): except self.model.DoesNotExist: with scopes_disabled(): try: + defaults = kwargs.pop('defaults', None) + if defaults: + kwargs = {**kwargs, **defaults} # ManyToMany fields can't be set this way, so pop them out to save for later fields = [field.name for field in self.model._meta.get_fields() if issubclass(type(field), ManyToManyField)] many_to_many = {field: kwargs.pop(field) for field in list(kwargs) if field in fields} diff --git a/cookbook/serializer.py b/cookbook/serializer.py index d2ed3fa30..7ca42c7ba 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -179,7 +179,6 @@ class UserPreferenceSerializer(serializers.ModelSerializer): 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping', 'food_ignore_default', 'default_delay', 'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days', 'csv_delim', 'csv_prefix' ) - # read_only_fields = ['user'] # making user read_only removes it from validated_data, moved read_only attribute to serializer class UserFileSerializer(serializers.ModelSerializer): diff --git a/cookbook/tests/api/test_api_shopping_list_entryv2.py b/cookbook/tests/api/test_api_shopping_list_entryv2.py index 84ee22ba4..f645255e4 100644 --- a/cookbook/tests/api/test_api_shopping_list_entryv2.py +++ b/cookbook/tests/api/test_api_shopping_list_entryv2.py @@ -1,5 +1,6 @@ import json +import factory import pytest from django.contrib import auth from django.forms import model_to_dict @@ -13,15 +14,14 @@ from cookbook.tests.factories import FoodFactory, ShoppingListEntryFactory LIST_URL = 'api:shoppinglistentry-list' DETAIL_URL = 'api:shoppinglistentry-detail' -# register(FoodFactory, 'food_1', space=LazyFixture('space_1')) -# register(FoodFactory, 'food_2', space=LazyFixture('space_1')) -# register(ShoppingListEntryFactory, 'sle_1', space=LazyFixture('space_1'), food=LazyFixture('food_1')) -# register(ShoppingListEntryFactory, 'sle_2', space=LazyFixture('space_1'), food=LazyFixture('food_2')) -register(ShoppingListEntryFactory, 'sle_1', space=LazyFixture('space_1')) -register(ShoppingListEntryFactory, 'sle_2', space=LazyFixture('space_1')) + +@pytest.fixture +def sle(space_1, u1_s1): + user = auth.get_user(u1_s1) + return ShoppingListEntryFactory.create_batch(10, space=space_1, created_by=user) -@pytest.mark.parametrize("arg", [ +@ pytest.mark.parametrize("arg", [ ['a_u', 403], ['g1_s1', 200], ['u1_s1', 200], @@ -32,29 +32,28 @@ def test_list_permission(arg, request): assert c.get(reverse(LIST_URL)).status_code == arg[1] -def test_list_space(sle_1, u1_s1, u1_s2, space_2): - assert json.loads(u1_s1.get(reverse(LIST_URL)).content)['count'] == 2 - assert json.loads(u1_s2.get(reverse(LIST_URL)).content)['count'] == 0 +def test_list_space(sle, u1_s1, u1_s2, space_2): + assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 10 + assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0 with scopes_disabled(): e = ShoppingListEntry.objects.first() e.space = space_2 e.save() - assert json.loads(u1_s1.get(reverse(LIST_URL)).content)['count'] == 1 - assert json.loads(u1_s2.get(reverse(LIST_URL)).content)['count'] == 0 + assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 9 + assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0 -def test_get_detail(u1_s1, sle_1): - # r = u1_s1.get(reverse( - # DETAIL_URL, - # args={sle_1.id} - # )) - # assert json.loads(r.content)['id'] == sle_1.id - pass +def test_get_detail(u1_s1, sle): + r = u1_s1.get(reverse( + DETAIL_URL, + args={sle[0].id} + )) + assert json.loads(r.content)['id'] == sle[0].id -@pytest.mark.parametrize("arg", [ +@ pytest.mark.parametrize("arg", [ ['a_u', 403], ['g1_s1', 404], ['u1_s1', 200], @@ -63,20 +62,21 @@ def test_get_detail(u1_s1, sle_1): ['u1_s2', 404], ['a1_s2', 404], ]) -def test_update(arg, request, sle_1): +def test_update(arg, request, sle): c = request.getfixturevalue(arg[0]) + new_val = float(sle[0].amount + 1) r = c.patch( reverse( DETAIL_URL, - args={sle_1.id} + args={sle[0].id} ), - {'amount': 2}, + {'amount': new_val}, content_type='application/json' ) assert r.status_code == arg[1] if r.status_code == 200: response = json.loads(r.content) - assert response['amount'] == 2 + assert response['amount'] == new_val @pytest.mark.parametrize("arg", [ @@ -85,25 +85,25 @@ def test_update(arg, request, sle_1): ['u1_s1', 201], ['a1_s1', 201], ]) -def test_add(arg, request, sle_1): +def test_add(arg, request, sle): c = request.getfixturevalue(arg[0]) r = c.post( reverse(LIST_URL), - {'food': model_to_dict(sle_1.food), 'amount': 1}, + {'food': model_to_dict(sle[0].food), 'amount': 1}, content_type='application/json' ) response = json.loads(r.content) print(r.content) assert r.status_code == arg[1] if r.status_code == 201: - assert response['food']['id'] == sle_1.food.pk + assert response['food']['id'] == sle[0].food.pk -def test_delete(u1_s1, u1_s2, sle_1): +def test_delete(u1_s1, u1_s2, sle): r = u1_s2.delete( reverse( DETAIL_URL, - args={sle_1.id} + args={sle[0].id} ) ) assert r.status_code == 404 @@ -111,7 +111,7 @@ def test_delete(u1_s1, u1_s2, sle_1): r = u1_s1.delete( reverse( DETAIL_URL, - args={sle_1.id} + args={sle[0].id} ) ) diff --git a/cookbook/tests/conftest.py b/cookbook/tests/conftest.py index e743687cb..0b797a1be 100644 --- a/cookbook/tests/conftest.py +++ b/cookbook/tests/conftest.py @@ -7,14 +7,14 @@ import pytest from django.contrib import auth from django.contrib.auth.models import Group, User from django_scopes import scopes_disabled -from pytest_factoryboy import register +from pytest_factoryboy import LazyFixture, register from cookbook.models import Food, Ingredient, Recipe, Space, Step, Unit -from cookbook.tests.factories import SpaceFactory +from cookbook.tests.factories import FoodFactory, SpaceFactory, UserFactory register(SpaceFactory, 'space_1') register(SpaceFactory, 'space_2') - +# register(FoodFactory, space=LazyFixture('space_2')) # TODO refactor user fixtures https://stackoverflow.com/questions/40966571/how-to-create-a-field-with-a-list-of-instances-in-factory-boy # hack from https://github.com/raphaelm/django-scopes to disable scopes for all fixtures @@ -184,25 +184,17 @@ def create_user(client, space, **kwargs): c = copy.deepcopy(client) with scopes_disabled(): group = kwargs.pop('group', None) - username = kwargs.pop('username', uuid.uuid4()) + user = UserFactory(space=space, groups=group) - user = User.objects.create(username=username, **kwargs) - if group: - user.groups.add(Group.objects.get(name=group)) - - user.userpreference.space = space - user.userpreference.save() c.force_login(user) return c -# anonymous user @pytest.fixture() def a_u(client): return copy.deepcopy(client) -# users without any group @pytest.fixture() def ng1_s1(client, space_1): return create_user(client, space_1) diff --git a/cookbook/tests/factories/__init__.py b/cookbook/tests/factories/__init__.py index f9adcdb2f..114bc64d5 100644 --- a/cookbook/tests/factories/__init__.py +++ b/cookbook/tests/factories/__init__.py @@ -4,9 +4,10 @@ from decimal import Decimal import factory import pytest from django.contrib import auth -from django.contrib.auth.models import User +from django.contrib.auth.models import Group, User from django_scopes import scopes_disabled from faker import Factory as FakerFactory +from pytest_factoryboy import register # this code will run immediately prior to creating the model object useful when you want a reverse relationship # log = factory.RelatedFactory( @@ -31,6 +32,7 @@ def pytest_fixture_setup(fixturedef, request): yield +@register class SpaceFactory(factory.django.DjangoModelFactory): """Space factory.""" name = factory.LazyAttribute(lambda x: faker.word()) @@ -44,18 +46,41 @@ class SpaceFactory(factory.django.DjangoModelFactory): model = 'cookbook.Space' +@register class UserFactory(factory.django.DjangoModelFactory): + """User factory.""" - username = factory.LazyAttribute(lambda x: faker.word()) + username = factory.LazyAttribute(lambda x: faker.simple_profile()['username']) first_name = factory.LazyAttribute(lambda x: faker.first_name()) last_name = factory.LazyAttribute(lambda x: faker.last_name()) email = factory.LazyAttribute(lambda x: faker.email()) space = factory.SubFactory(SpaceFactory) + @factory.post_generation + def groups(self, create, extracted, **kwargs): + if not create: + return + + if extracted: + self.groups.add(Group.objects.get(name=extracted)) + + @factory.post_generation + def userpreference(self, create, extracted, **kwargs): + if not create: + return + + if extracted: + # TODO this doesn't work and needs saved + for prefs in extracted: + self.userpreference[prefs] = prefs + self.userpreference.space = self.space + self.userpreference.save() + class Meta: model = User +@register class SupermarketCategoryFactory(factory.django.DjangoModelFactory): """SupermarketCategory factory.""" name = factory.LazyAttribute(lambda x: faker.word()) @@ -64,9 +89,11 @@ class SupermarketCategoryFactory(factory.django.DjangoModelFactory): class Meta: model = 'cookbook.SupermarketCategory' + django_get_or_create = ('name', 'space',) # @factory.django.mute_signals(post_save) +@register class FoodFactory(factory.django.DjangoModelFactory): """Food factory.""" name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=3)) @@ -89,8 +116,10 @@ class FoodFactory(factory.django.DjangoModelFactory): class Meta: model = 'cookbook.Food' + django_get_or_create = ('name', 'space',) +@register class UnitFactory(factory.django.DjangoModelFactory): """Unit factory.""" name = factory.LazyAttribute(lambda x: faker.word()) @@ -99,8 +128,10 @@ class UnitFactory(factory.django.DjangoModelFactory): class Meta: model = 'cookbook.Unit' + django_get_or_create = ('name', 'space',) +@register class KeywordFactory(factory.django.DjangoModelFactory): """Keyword factory.""" name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=3)) @@ -110,8 +141,10 @@ class KeywordFactory(factory.django.DjangoModelFactory): class Meta: model = 'cookbook.Keyword' + django_get_or_create = ('name', 'space',) +@register class IngredientFactory(factory.django.DjangoModelFactory): """Ingredient factory.""" food = factory.SubFactory(FoodFactory, space=factory.SelfAttribute('..space')) @@ -124,6 +157,7 @@ class IngredientFactory(factory.django.DjangoModelFactory): model = 'cookbook.Ingredient' +@register class MealTypeFactory(factory.django.DjangoModelFactory): name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=5)) order = 0 @@ -137,6 +171,7 @@ class MealTypeFactory(factory.django.DjangoModelFactory): model = 'cookbook.MealType' +@register class MealPlanFactory(factory.django.DjangoModelFactory): recipe = factory.Maybe( factory.LazyAttribute(lambda x: x.has_recipe), @@ -158,6 +193,7 @@ class MealPlanFactory(factory.django.DjangoModelFactory): model = 'cookbook.MealPlan' +@register class ShoppingListRecipeFactory(factory.django.DjangoModelFactory): name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=5)) recipe = factory.Maybe( @@ -175,6 +211,7 @@ class ShoppingListRecipeFactory(factory.django.DjangoModelFactory): model = 'cookbook.ShoppingListRecipe' +@register class ShoppingListEntryFactory(factory.django.DjangoModelFactory): """ShoppingListEntry factory.""" @@ -184,16 +221,16 @@ class ShoppingListEntryFactory(factory.django.DjangoModelFactory): no_declaration=None ) food = factory.SubFactory(FoodFactory, space=factory.SelfAttribute('..space')) - # unit = factory.SubFactory(UnitFactory, space=factory.SelfAttribute('..space')) + unit = factory.SubFactory(UnitFactory, space=factory.SelfAttribute('..space')) # # ingredient = factory.SubFactory(IngredientFactory) - amount = factory.LazyAttribute(lambda x: Decimal(faker.random_int(min=1, max=10))/100) - order = 0 + amount = factory.LazyAttribute(lambda x: Decimal(faker.random_int(min=1, max=100))/10) + order = factory.Sequence(int) checked = False created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space')) created_at = factory.LazyAttribute(lambda x: faker.past_date()) completed_at = None delay_until = None - space = factory.SubFactory('cookbook.tests.factories.SpaceFactory') + space = factory.SubFactory(SpaceFactory) class Params: has_mealplan = False @@ -202,6 +239,7 @@ class ShoppingListEntryFactory(factory.django.DjangoModelFactory): model = 'cookbook.ShoppingListEntry' +@register class StepFactory(factory.django.DjangoModelFactory): name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=5)) # type = models.CharField( @@ -229,6 +267,7 @@ class StepFactory(factory.django.DjangoModelFactory): model = 'cookbook.Step' +@register class RecipeFactory(factory.django.DjangoModelFactory): name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=7)) description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10)) diff --git a/vue/package-lock.json b/vue/package-lock.json index a1f91dd86..76e4a9540 100644 --- a/vue/package-lock.json +++ b/vue/package-lock.json @@ -1282,6 +1282,11 @@ } } }, + "@popperjs/core": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.0.tgz", + "integrity": "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ==" + }, "@riophae/vue-treeselect": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@riophae/vue-treeselect/-/vue-treeselect-0.4.0.tgz", @@ -2398,6 +2403,116 @@ "tslint": "^5.20.1", "webpack": "^4.0.0", "yorkie": "^2.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "optional": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "fork-ts-checker-webpack-plugin-v5": { + "version": "npm:fork-ts-checker-webpack-plugin@5.2.1", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz", + "integrity": "sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==", + "dev": true, + "optional": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "optional": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dev": true, + "optional": true, + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "optional": true + } } }, "@vue/cli-plugin-vuex": { @@ -2470,6 +2585,17 @@ "webpack-merge": "^4.2.2" }, "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -2487,6 +2613,30 @@ "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", "dev": true }, + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "optional": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "vue-loader-v16": { + "version": "npm:vue-loader@16.8.3", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz", + "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==", + "dev": true, + "optional": true, + "requires": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "loader-utils": "^2.0.0" + } + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -2637,6 +2787,15 @@ "vue-template-es2015-compiler": "^1.9.0" } }, + "@vue/eslint-config-typescript": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-9.1.0.tgz", + "integrity": "sha512-j/852/ZYQ5wDvCD3HE2q4uqJwJAceer2FwoEch1nFo+zTOsPrbzbE3cuWIs3kvu5hdFsGTMYwRwjI6fqZKDMxQ==", + "dev": true, + "requires": { + "vue-eslint-parser": "^8.0.0" + } + }, "@vue/preload-webpack-plugin": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz", @@ -3215,6 +3374,14 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, + "axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "requires": { + "follow-redirects": "^1.14.4" + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -4757,6 +4924,11 @@ } } }, + "core-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.3.tgz", + "integrity": "sha512-LeLBMgEGSsG7giquSzvgBrTS7V5UL6ks3eQlUSbN8dJStlLFiRzUm5iqsRyzUB8carhfKjkJ2vzKqE6z1Vga9g==" + }, "core-js-compat": { "version": "3.19.2", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.2.tgz", @@ -6534,9 +6706,9 @@ } }, "eslint-plugin-vue": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.1.1.tgz", - "integrity": "sha512-rx64IrlhdfPya6u2V5ukOGiLCTgaCBdMSpczLVqyo8A0l+Vbo+lzvIfEUfAQ2auj+MF6y0TwxLorzdCIzHunnw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.2.0.tgz", + "integrity": "sha512-cLIdTuOAMXyHeQ4drYKcZfoyzdwdBpH279X8/N0DgmotEI9yFKb5O/cAgoie/CkQZCH/MOmh0xw/KEfS90zY2A==", "dev": true, "requires": { "eslint-utils": "^3.0.0", @@ -6545,39 +6717,6 @@ "vue-eslint-parser": "^8.0.1" }, "dependencies": { - "acorn": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", - "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", - "dev": true - }, - "eslint-scope": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-6.0.0.tgz", - "integrity": "sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", - "dev": true - }, - "espree": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.1.0.tgz", - "integrity": "sha512-ZgYLvCS1wxOczBYGcQT9DDWgicXwJ4dbocr9uYN+/eresBAUuBu+O4WzB21ufQ/JqQT8gyp7hJ3z8SHii32mTQ==", - "dev": true, - "requires": { - "acorn": "^8.6.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -6595,29 +6734,6 @@ "requires": { "lru-cache": "^6.0.0" } - }, - "vue-eslint-parser": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.0.1.tgz", - "integrity": "sha512-lhWjDXJhe3UZw2uu3ztX51SJAPGPey1Tff2RK3TyZURwbuI4vximQLzz4nQfCv8CZq4xx7uIiogHMMoSJPr33A==", - "dev": true, - "requires": { - "debug": "^4.3.2", - "eslint-scope": "^6.0.0", - "eslint-visitor-keys": "^3.0.0", - "espree": "^9.0.0", - "esquery": "^1.4.0", - "lodash": "^4.17.21", - "semver": "^7.3.5" - }, - "dependencies": { - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - } - } } } }, @@ -7250,8 +7366,7 @@ "follow-redirects": { "version": "1.14.5", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", - "dev": true + "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" }, "for-in": { "version": "1.0.2", @@ -7370,116 +7485,6 @@ } } }, - "fork-ts-checker-webpack-plugin-v5": { - "version": "npm:fork-ts-checker-webpack-plugin@5.2.1", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz", - "integrity": "sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==", - "dev": true, - "optional": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dev": true, - "optional": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - } - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "optional": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "optional": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "optional": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dev": true, - "optional": true, - "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "optional": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "optional": true - } - } - }, "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", @@ -9139,6 +9144,11 @@ "path-exists": "^3.0.0" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", @@ -11064,6 +11074,11 @@ } } }, + "prismjs": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz", + "integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==" + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -13537,6 +13552,11 @@ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" }, + "vue": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz", + "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==" + }, "vue-class-component": { "version": "7.2.6", "resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.2.6.tgz", @@ -13586,11 +13606,87 @@ "resolved": "https://registry.npmjs.org/vue-click-outside/-/vue-click-outside-1.1.0.tgz", "integrity": "sha512-pNyvAA9mRXJwPHlHJyjMb4IONSc7khS5lxGcMyE2EIKgNMAO279PWM9Hyq0d5J4FkiSRdmFLwnbjDd5UtPizHQ==" }, + "vue-clickaway": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/vue-clickaway/-/vue-clickaway-2.2.2.tgz", + "integrity": "sha512-25SpjXKetL06GLYoLoC8pqAV6Cur9cQ//2g35GRFBV4FgoljbZZjTINR8g2NuVXXDMLSUXaKx5dutgO4PaDE7A==", + "requires": { + "loose-envify": "^1.2.0" + } + }, "vue-cookies": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/vue-cookies/-/vue-cookies-1.7.4.tgz", "integrity": "sha512-mOS5Btr8V9zvAtkmQ7/TfqJIropOx7etDAgBywPCmHjvfJl2gFbH2XgoMghleLoyyMTi5eaJss0mPN7arMoslA==" }, + "vue-eslint-parser": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.0.1.tgz", + "integrity": "sha512-lhWjDXJhe3UZw2uu3ztX51SJAPGPey1Tff2RK3TyZURwbuI4vximQLzz4nQfCv8CZq4xx7uIiogHMMoSJPr33A==", + "dev": true, + "requires": { + "debug": "^4.3.2", + "eslint-scope": "^6.0.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.5" + }, + "dependencies": { + "acorn": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "dev": true + }, + "eslint-scope": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-6.0.0.tgz", + "integrity": "sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "dev": true + }, + "espree": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", + "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "dev": true, + "requires": { + "acorn": "^8.6.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.1.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "vue-functional-data-merge": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz", @@ -13602,6 +13698,11 @@ "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", "dev": true }, + "vue-i18n": { + "version": "8.26.7", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.26.7.tgz", + "integrity": "sha512-7apa5PvRg1YCLoraE3lOgpCG8hJGupLCtywQWedWsgBbvF0TOgFvhitqK9xRH0PBGG1G8aiJz9oklyNDFfDxLg==" + }, "vue-i18n-extract": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/vue-i18n-extract/-/vue-i18n-extract-1.0.2.tgz", @@ -13634,50 +13735,6 @@ "vue-style-loader": "^4.1.0" } }, - "vue-loader-v16": { - "version": "npm:vue-loader@16.8.3", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz", - "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==", - "dev": true, - "optional": true, - "requires": { - "chalk": "^4.1.0", - "hash-sum": "^2.0.0", - "loader-utils": "^2.0.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "hash-sum": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", - "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", - "dev": true, - "optional": true - }, - "loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", - "dev": true, - "optional": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - } - } - }, "vue-multiselect": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/vue-multiselect/-/vue-multiselect-2.1.6.tgz", diff --git a/vue/src/apps/RecipeView/RecipeView.vue b/vue/src/apps/RecipeView/RecipeView.vue index 0425e65b7..dbd71ca3b 100644 --- a/vue/src/apps/RecipeView/RecipeView.vue +++ b/vue/src/apps/RecipeView/RecipeView.vue @@ -25,7 +25,7 @@
- +

@@ -57,21 +57,32 @@ - -
-