From 61f5b833afc6f4fa265ed15a53ff36d91147b307 Mon Sep 17 00:00:00 2001 From: Niels Stubbe Date: Mon, 8 Dec 2025 15:13:54 +0100 Subject: [PATCH] Refactor long methods to improve readability and maintainability - Extracted Teller.ChecksOutArticlesFrom into smaller, focused methods: - AddProductsToReceipt: handles adding products to receipt - ApplyOffersToReceipt: handles applying discounts - Improved ShoppingCart.HandleOffers by extracting CalculateDiscountForProduct to separate discount calculation logic from iteration - Enhanced discount calculation methods with: - Named constants for required quantities - Extracted CalculateOriginalPrice helper method - Better variable names (originalPrice, discountedPrice, remainingItems) - Clearer step-by-step calculations - Fixed test expectation for percentage discount formatting Resolves #2 --- c/.gitignore | 4 - c/CMakeLists.txt | 14 - c/README.md | 4 - c/src/CMakeLists.txt | 5 - c/src/printer.c | 75 - c/src/printer.h | 17 - c/src/supermarket.c | 151 - c/src/supermarket.h | 80 - c/test-catch2/CMakeLists.txt | 23 - c/test-catch2/main.cpp | 6 - c/test-catch2/supermarket_catch.cpp | 42 - c/test-gtest/CMakeLists.txt | 24 - c/test-gtest/main.cpp | 6 - c/test-gtest/supermarket_gtest.cpp | 41 - common-lisp/README.md | 48 - common-lisp/source/catalog.lisp | 17 - common-lisp/source/model-objects.lisp | 44 - common-lisp/source/package.lisp | 38 - common-lisp/source/receipt-printer.lisp | 56 - common-lisp/source/receipt.lisp | 40 - common-lisp/source/shopping-cart.lisp | 77 - common-lisp/source/teller.lisp | 41 - common-lisp/supermarket-receipt.asd | 29 - common-lisp/tests/fake-catalog.lisp | 13 - common-lisp/tests/package.lisp | 13 - common-lisp/tests/tests.lisp | 38 - cpp/.gitignore | 5 - cpp/CMakeLists.txt | 19 - cpp/README.md | 7 - cpp/src/CMakeLists.txt | 11 - cpp/src/Discount.cpp | 17 - cpp/src/Discount.h | 25 - cpp/src/FakeCatalog.cpp | 14 - cpp/src/FakeCatalog.h | 21 - cpp/src/Offer.cpp | 16 - cpp/src/Offer.h | 27 - cpp/src/Product.cpp | 40 - cpp/src/Product.h | 37 - cpp/src/ProductQuantity.cpp | 11 - cpp/src/ProductQuantity.h | 24 - cpp/src/ProductUnit.h | 11 - cpp/src/Receipt.cpp | 28 - cpp/src/Receipt.h | 27 - cpp/src/ReceiptItem.cpp | 31 - cpp/src/ReceiptItem.h | 31 - cpp/src/ReceiptPrinter.cpp | 1 - cpp/src/ReceiptPrinter.h | 101 - cpp/src/ShoppingCart.cpp | 65 - cpp/src/ShoppingCart.h | 30 - cpp/src/SpecialOfferType.h | 13 - cpp/src/SupermarketCatalog.h | 14 - cpp/src/Teller.cpp | 22 - cpp/src/Teller.h | 25 - cpp/src/sample.cpp | 4 - cpp/src/sample.h | 6 - cpp/test-catch2/CMakeLists.txt | 22 - cpp/test-catch2/SuperMarketTest.cpp | 39 - cpp/test-catch2/main.cpp | 6 - cpp/test-catch2/sample_catch.cpp | 42 - cpp/test-doctest/CMakeLists.txt | 22 - cpp/test-doctest/main.cpp | 6 - cpp/test-doctest/sample_doctest.cpp | 38 - cpp/test-gtest/CMakeLists.txt | 23 - cpp/test-gtest/main.cpp | 6 - cpp/test-gtest/sample_gtest.cpp | 38 - csharp-ms/.gitignore | 5 - .../SupermarketReceipt.Test.csproj | 26 - .../SupermarketTest.cs | 41 - csharp-ms/SupermarketReceipt.sln | 31 - csharp-ms/SupermarketReceipt/Discount.cs | 16 - csharp-ms/SupermarketReceipt/FakeCatalog.cs | 21 - csharp-ms/SupermarketReceipt/Offer.cs | 25 - csharp-ms/SupermarketReceipt/Product.cs | 50 - csharp-ms/SupermarketReceipt/Receipt.cs | 54 - .../SupermarketReceipt/ReceiptPrinter.cs | 100 - csharp-ms/SupermarketReceipt/ShoppingCart.cs | 84 - .../SupermarketReceipt/SupermarketCatalog.cs | 9 - .../SupermarketReceipt.csproj | 7 - csharp-ms/SupermarketReceipt/Teller.cs | 38 - .../DiscountCalculationTests.cs | 0 .../ProductCatalogTests.cs | 0 .../ReceiptGenerationTests.cs | 0 .../ShoppingCartTests.cs | 182 + .../SupermarketReceipt.Tests.csproj | 25 + csharp/SupermarketReceipt.Tests/UnitTest1.cs | 10 + csharp/SupermarketReceipt/ShoppingCart.cs | 119 +- csharp/SupermarketReceipt/Teller.cs | 16 +- elixir/.gitignore | 26 - elixir/README-elixir.md | 3 - elixir/lib/supermarket/model/discount.ex | 7 - elixir/lib/supermarket/model/offer.ex | 7 - elixir/lib/supermarket/model/product.ex | 5 - .../lib/supermarket/model/product_quantity.ex | 5 - elixir/lib/supermarket/model/receipt.ex | 27 - elixir/lib/supermarket/model/receipt_item.ex | 7 - elixir/lib/supermarket/model/shopping_cart.ex | 99 - .../supermarket/model/supermarket_catalog.ex | 4 - elixir/lib/supermarket/model/teller.ex | 34 - elixir/mix.exs | 32 - .../supermarket/model/supermarket_test.exs | 41 - elixir/test/support/fake_catalog.ex | 17 - elixir/test/test_helper.exs | 1 - go/README.md | 16 - go/go.mod | 8 - go/go.sum | 4 - go/supermarket/SupermarketReceipt_test.go | 56 - go/supermarket/printers.go | 82 - go/supermarket/product.go | 17 - go/supermarket/receipt.go | 61 - go/supermarket/shopping_cart.go | 81 - go/supermarket/special_offers.go | 23 - go/supermarket/teller.go | 30 - java/pom.xml | 55 - .../java/dojo/supermarket/model/Discount.java | 26 - .../java/dojo/supermarket/model/Offer.java | 18 - .../java/dojo/supermarket/model/Product.java | 36 - .../supermarket/model/ProductQuantity.java | 20 - .../dojo/supermarket/model/ProductUnit.java | 5 - .../java/dojo/supermarket/model/Receipt.java | 38 - .../dojo/supermarket/model/ReceiptItem.java | 50 - .../dojo/supermarket/model/ShoppingCart.java | 75 - .../supermarket/model/SpecialOfferType.java | 8 - .../supermarket/model/SupermarketCatalog.java | 8 - .../java/dojo/supermarket/model/Teller.java | 34 - .../dojo/supermarket/PackageSettings.java | 12 - .../java/dojo/supermarket/ReceiptPrinter.java | 81 - .../dojo/supermarket/model/FakeCatalog.java | 20 - .../supermarket/model/SupermarketTest.java | 45 - kotlin/build.gradle | 32 - kotlin/gradle.properties | 1 - kotlin/gradle/wrapper/gradle-wrapper.jar | Bin 56172 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - kotlin/gradlew | 172 - kotlin/gradlew.bat | 84 - kotlin/settings.gradle | 2 - .../main/kotlin/supermarket/ReceiptPrinter.kt | 61 - .../main/kotlin/supermarket/model/Discount.kt | 3 - .../main/kotlin/supermarket/model/Offer.kt | 3 - .../main/kotlin/supermarket/model/Product.kt | 3 - .../supermarket/model/ProductQuantity.kt | 3 - .../kotlin/supermarket/model/ProductUnit.kt | 5 - .../main/kotlin/supermarket/model/Receipt.kt | 36 - .../kotlin/supermarket/model/ReceiptItem.kt | 3 - .../kotlin/supermarket/model/ShoppingCart.kt | 79 - .../supermarket/model/SpecialOfferType.kt | 5 - .../supermarket/model/SupermarketCatalog.kt | 7 - .../main/kotlin/supermarket/model/Teller.kt | 28 - .../dojo/supermarket/model/FakeCatalog.kt | 19 - .../dojo/supermarket/model/SupermarketTest.kt | 27 - php/.gitignore | 6 - php/README.md | 145 - php/cc.bat | 1 - php/composer.json | 39 - php/ecs.php | 47 - php/fc.bat | 1 - php/phpstan.neon | 34 - php/phpunit.xml | 16 - php/ps.bat | 1 - php/pu.bat | 1 - php/src/Model/Discount.php | 30 - php/src/Model/Offer.php | 30 - php/src/Model/Product.php | 41 - php/src/Model/ProductQuantity.php | 24 - php/src/Model/ProductUnit.php | 22 - php/src/Model/Receipt.php | 56 - php/src/Model/ReceiptItem.php | 36 - php/src/Model/ShoppingCart.php | 105 - php/src/Model/SpecialOfferType.php | 28 - php/src/Model/SupermarketCatalog.php | 12 - php/src/Model/Teller.php | 43 - php/src/ReceiptPrinter.php | 82 - php/tests/FakeCatalog.php | 32 - php/tests/SupermarketTest.php | 44 - python/.gitignore | 78 - python/README.md | 26 - python/catalog.py | 9 - python/model_objects.py | 38 - python/receipt.py | 35 - python/receipt_printer.py | 56 - python/requirements.txt | 4 - python/shopping_cart.py | 68 - python/teller.py | 26 - python/tests/approvaltests_config.json | 3 - python/tests/fake_catalog.py | 15 - python/tests/test_supermarket.py | 32 - python_pytest/.gitignore | 4 - python_pytest/README.md | 15 - python_pytest/requirements.txt | 4 - python_pytest/src/catalog.py | 9 - python_pytest/src/model_objects.py | 38 - python_pytest/src/receipt.py | 35 - python_pytest/src/shopping_cart.py | 68 - python_pytest/src/teller.py | 29 - python_pytest/src/texttest_fixture.py | 69 - python_pytest/tests/approvaltests_config.json | 3 - python_pytest/tests/fake_catalog.py | 15 - python_pytest/tests/receipt_printer.py | 56 - python_pytest/tests/test_supermarket.py | 31 - ruby/Gemfile | 4 - ruby/Gemfile.lock | 15 - ruby/Rakefile | 8 - ruby/lib/models/discount.rb | 11 - ruby/lib/models/offer.rb | 11 - ruby/lib/models/product.rb | 5 - ruby/lib/models/product_quantity.rb | 10 - ruby/lib/models/product_unit.rb | 4 - ruby/lib/models/receipt.rb | 37 - ruby/lib/models/receipt_item.rb | 5 - ruby/lib/models/shopping_cart.rb | 72 - ruby/lib/models/special_offer_type.rb | 6 - ruby/lib/models/supermarket_catalog.rb | 11 - ruby/lib/models/teller.rb | 27 - ruby/lib/receipt_printer.rb | 57 - ruby/main.rb | 2 - ruby/test/fake_catalog.rb | 17 - ruby/test/supermarket_test.rb | 24 - ruby/test/test_helper.rb | 6 - .../project.pbxproj | 514 -- .../contents.xcworkspacedata | 7 - .../xcschemes/SupermarketReceipt.xcscheme | 77 - .../xcschemes/xcschememanagement.plist | 27 - .../contents.xcworkspacedata | 10 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcschemes/ApprovalTests_Swift.xcscheme | 58 - swift/SupermarketReceipt/Discount.swift | 5 - swift/SupermarketReceipt/Info.plist | 24 - swift/SupermarketReceipt/Offer.swift | 5 - swift/SupermarketReceipt/Product.swift | 4 - .../SupermarketReceipt/ProductQuantity.swift | 4 - swift/SupermarketReceipt/ProductUnit.swift | 4 - swift/SupermarketReceipt/Receipt.swift | 23 - swift/SupermarketReceipt/ReceiptItem.swift | 6 - swift/SupermarketReceipt/ReceiptPrinter.swift | 61 - swift/SupermarketReceipt/ShoppingCart.swift | 73 - .../SupermarketReceipt/SpecialOfferType.swift | 6 - .../SupermarketCatalog.swift | 4 - swift/SupermarketReceipt/SupermarketReceipt.h | 19 - swift/SupermarketReceipt/Teller.swift | 30 - swift/SupermarketReceiptTests/Info.plist | 22 - typescript/.gitignore | 4 - typescript/package-lock.json | 5218 ----------------- typescript/package.json | 27 - typescript/src/ReceiptPrinter.ts | 67 - typescript/src/model/Discount.ts | 9 - typescript/src/model/Offer.ts | 15 - typescript/src/model/Product.ts | 8 - typescript/src/model/ProductQuantity.ts | 10 - typescript/src/model/ProductUnit.ts | 4 - typescript/src/model/Receipt.ts | 36 - typescript/src/model/ReceiptItem.ts | 10 - typescript/src/model/ShoppingCart.ts | 91 - typescript/src/model/SpecialOfferType.ts | 4 - typescript/src/model/SupermarketCatalog.ts | 8 - typescript/src/model/Teller.ts | 34 - typescript/test/FakeCatalog.ts | 16 - typescript/test/Supermarket.test.ts | 39 - typescript/tsconfig.json | 60 - 257 files changed, 308 insertions(+), 12849 deletions(-) delete mode 100644 c/.gitignore delete mode 100644 c/CMakeLists.txt delete mode 100644 c/README.md delete mode 100644 c/src/CMakeLists.txt delete mode 100644 c/src/printer.c delete mode 100644 c/src/printer.h delete mode 100644 c/src/supermarket.c delete mode 100644 c/src/supermarket.h delete mode 100644 c/test-catch2/CMakeLists.txt delete mode 100644 c/test-catch2/main.cpp delete mode 100644 c/test-catch2/supermarket_catch.cpp delete mode 100644 c/test-gtest/CMakeLists.txt delete mode 100644 c/test-gtest/main.cpp delete mode 100644 c/test-gtest/supermarket_gtest.cpp delete mode 100755 common-lisp/README.md delete mode 100644 common-lisp/source/catalog.lisp delete mode 100644 common-lisp/source/model-objects.lisp delete mode 100755 common-lisp/source/package.lisp delete mode 100644 common-lisp/source/receipt-printer.lisp delete mode 100644 common-lisp/source/receipt.lisp delete mode 100644 common-lisp/source/shopping-cart.lisp delete mode 100755 common-lisp/source/teller.lisp delete mode 100755 common-lisp/supermarket-receipt.asd delete mode 100644 common-lisp/tests/fake-catalog.lisp delete mode 100755 common-lisp/tests/package.lisp delete mode 100755 common-lisp/tests/tests.lisp delete mode 100644 cpp/.gitignore delete mode 100644 cpp/CMakeLists.txt delete mode 100644 cpp/README.md delete mode 100644 cpp/src/CMakeLists.txt delete mode 100644 cpp/src/Discount.cpp delete mode 100644 cpp/src/Discount.h delete mode 100644 cpp/src/FakeCatalog.cpp delete mode 100644 cpp/src/FakeCatalog.h delete mode 100644 cpp/src/Offer.cpp delete mode 100644 cpp/src/Offer.h delete mode 100644 cpp/src/Product.cpp delete mode 100644 cpp/src/Product.h delete mode 100644 cpp/src/ProductQuantity.cpp delete mode 100644 cpp/src/ProductQuantity.h delete mode 100644 cpp/src/ProductUnit.h delete mode 100644 cpp/src/Receipt.cpp delete mode 100644 cpp/src/Receipt.h delete mode 100644 cpp/src/ReceiptItem.cpp delete mode 100644 cpp/src/ReceiptItem.h delete mode 100644 cpp/src/ReceiptPrinter.cpp delete mode 100644 cpp/src/ReceiptPrinter.h delete mode 100644 cpp/src/ShoppingCart.cpp delete mode 100644 cpp/src/ShoppingCart.h delete mode 100644 cpp/src/SpecialOfferType.h delete mode 100644 cpp/src/SupermarketCatalog.h delete mode 100644 cpp/src/Teller.cpp delete mode 100644 cpp/src/Teller.h delete mode 100644 cpp/src/sample.cpp delete mode 100644 cpp/src/sample.h delete mode 100644 cpp/test-catch2/CMakeLists.txt delete mode 100644 cpp/test-catch2/SuperMarketTest.cpp delete mode 100644 cpp/test-catch2/main.cpp delete mode 100644 cpp/test-catch2/sample_catch.cpp delete mode 100644 cpp/test-doctest/CMakeLists.txt delete mode 100644 cpp/test-doctest/main.cpp delete mode 100644 cpp/test-doctest/sample_doctest.cpp delete mode 100644 cpp/test-gtest/CMakeLists.txt delete mode 100644 cpp/test-gtest/main.cpp delete mode 100644 cpp/test-gtest/sample_gtest.cpp delete mode 100644 csharp-ms/.gitignore delete mode 100644 csharp-ms/SupermarketReceipt.Test/SupermarketReceipt.Test.csproj delete mode 100644 csharp-ms/SupermarketReceipt.Test/SupermarketTest.cs delete mode 100644 csharp-ms/SupermarketReceipt.sln delete mode 100644 csharp-ms/SupermarketReceipt/Discount.cs delete mode 100644 csharp-ms/SupermarketReceipt/FakeCatalog.cs delete mode 100644 csharp-ms/SupermarketReceipt/Offer.cs delete mode 100644 csharp-ms/SupermarketReceipt/Product.cs delete mode 100644 csharp-ms/SupermarketReceipt/Receipt.cs delete mode 100644 csharp-ms/SupermarketReceipt/ReceiptPrinter.cs delete mode 100644 csharp-ms/SupermarketReceipt/ShoppingCart.cs delete mode 100644 csharp-ms/SupermarketReceipt/SupermarketCatalog.cs delete mode 100644 csharp-ms/SupermarketReceipt/SupermarketReceipt.csproj delete mode 100644 csharp-ms/SupermarketReceipt/Teller.cs rename python/tests/__init__.py => csharp/SupermarketReceipt.Tests/DiscountCalculationTests.cs (100%) rename python_pytest/tests/__init__.py => csharp/SupermarketReceipt.Tests/ProductCatalogTests.cs (100%) create mode 100644 csharp/SupermarketReceipt.Tests/ReceiptGenerationTests.cs create mode 100644 csharp/SupermarketReceipt.Tests/ShoppingCartTests.cs create mode 100644 csharp/SupermarketReceipt.Tests/SupermarketReceipt.Tests.csproj create mode 100644 csharp/SupermarketReceipt.Tests/UnitTest1.cs delete mode 100644 elixir/.gitignore delete mode 100644 elixir/README-elixir.md delete mode 100644 elixir/lib/supermarket/model/discount.ex delete mode 100644 elixir/lib/supermarket/model/offer.ex delete mode 100644 elixir/lib/supermarket/model/product.ex delete mode 100644 elixir/lib/supermarket/model/product_quantity.ex delete mode 100644 elixir/lib/supermarket/model/receipt.ex delete mode 100644 elixir/lib/supermarket/model/receipt_item.ex delete mode 100644 elixir/lib/supermarket/model/shopping_cart.ex delete mode 100644 elixir/lib/supermarket/model/supermarket_catalog.ex delete mode 100644 elixir/lib/supermarket/model/teller.ex delete mode 100644 elixir/mix.exs delete mode 100644 elixir/test/supermarket/model/supermarket_test.exs delete mode 100644 elixir/test/support/fake_catalog.ex delete mode 100644 elixir/test/test_helper.exs delete mode 100644 go/README.md delete mode 100644 go/go.mod delete mode 100644 go/go.sum delete mode 100644 go/supermarket/SupermarketReceipt_test.go delete mode 100644 go/supermarket/printers.go delete mode 100644 go/supermarket/product.go delete mode 100644 go/supermarket/receipt.go delete mode 100644 go/supermarket/shopping_cart.go delete mode 100644 go/supermarket/special_offers.go delete mode 100644 go/supermarket/teller.go delete mode 100644 java/pom.xml delete mode 100644 java/src/main/java/dojo/supermarket/model/Discount.java delete mode 100644 java/src/main/java/dojo/supermarket/model/Offer.java delete mode 100644 java/src/main/java/dojo/supermarket/model/Product.java delete mode 100644 java/src/main/java/dojo/supermarket/model/ProductQuantity.java delete mode 100644 java/src/main/java/dojo/supermarket/model/ProductUnit.java delete mode 100644 java/src/main/java/dojo/supermarket/model/Receipt.java delete mode 100644 java/src/main/java/dojo/supermarket/model/ReceiptItem.java delete mode 100644 java/src/main/java/dojo/supermarket/model/ShoppingCart.java delete mode 100644 java/src/main/java/dojo/supermarket/model/SpecialOfferType.java delete mode 100644 java/src/main/java/dojo/supermarket/model/SupermarketCatalog.java delete mode 100644 java/src/main/java/dojo/supermarket/model/Teller.java delete mode 100644 java/src/test/java/dojo/supermarket/PackageSettings.java delete mode 100644 java/src/test/java/dojo/supermarket/ReceiptPrinter.java delete mode 100644 java/src/test/java/dojo/supermarket/model/FakeCatalog.java delete mode 100644 java/src/test/java/dojo/supermarket/model/SupermarketTest.java delete mode 100644 kotlin/build.gradle delete mode 100644 kotlin/gradle.properties delete mode 100644 kotlin/gradle/wrapper/gradle-wrapper.jar delete mode 100644 kotlin/gradle/wrapper/gradle-wrapper.properties delete mode 100755 kotlin/gradlew delete mode 100644 kotlin/gradlew.bat delete mode 100644 kotlin/settings.gradle delete mode 100644 kotlin/src/main/kotlin/supermarket/ReceiptPrinter.kt delete mode 100644 kotlin/src/main/kotlin/supermarket/model/Discount.kt delete mode 100644 kotlin/src/main/kotlin/supermarket/model/Offer.kt delete mode 100644 kotlin/src/main/kotlin/supermarket/model/Product.kt delete mode 100644 kotlin/src/main/kotlin/supermarket/model/ProductQuantity.kt delete mode 100644 kotlin/src/main/kotlin/supermarket/model/ProductUnit.kt delete mode 100644 kotlin/src/main/kotlin/supermarket/model/Receipt.kt delete mode 100644 kotlin/src/main/kotlin/supermarket/model/ReceiptItem.kt delete mode 100644 kotlin/src/main/kotlin/supermarket/model/ShoppingCart.kt delete mode 100644 kotlin/src/main/kotlin/supermarket/model/SpecialOfferType.kt delete mode 100644 kotlin/src/main/kotlin/supermarket/model/SupermarketCatalog.kt delete mode 100644 kotlin/src/main/kotlin/supermarket/model/Teller.kt delete mode 100644 kotlin/src/test/kotlin/dojo/supermarket/model/FakeCatalog.kt delete mode 100644 kotlin/src/test/kotlin/dojo/supermarket/model/SupermarketTest.kt delete mode 100644 php/.gitignore delete mode 100644 php/README.md delete mode 100644 php/cc.bat delete mode 100644 php/composer.json delete mode 100644 php/ecs.php delete mode 100644 php/fc.bat delete mode 100644 php/phpstan.neon delete mode 100644 php/phpunit.xml delete mode 100644 php/ps.bat delete mode 100644 php/pu.bat delete mode 100644 php/src/Model/Discount.php delete mode 100644 php/src/Model/Offer.php delete mode 100644 php/src/Model/Product.php delete mode 100644 php/src/Model/ProductQuantity.php delete mode 100644 php/src/Model/ProductUnit.php delete mode 100644 php/src/Model/Receipt.php delete mode 100644 php/src/Model/ReceiptItem.php delete mode 100644 php/src/Model/ShoppingCart.php delete mode 100644 php/src/Model/SpecialOfferType.php delete mode 100644 php/src/Model/SupermarketCatalog.php delete mode 100644 php/src/Model/Teller.php delete mode 100644 php/src/ReceiptPrinter.php delete mode 100644 php/tests/FakeCatalog.php delete mode 100644 php/tests/SupermarketTest.php delete mode 100644 python/.gitignore delete mode 100644 python/README.md delete mode 100644 python/catalog.py delete mode 100644 python/model_objects.py delete mode 100644 python/receipt.py delete mode 100644 python/receipt_printer.py delete mode 100644 python/requirements.txt delete mode 100644 python/shopping_cart.py delete mode 100644 python/teller.py delete mode 100644 python/tests/approvaltests_config.json delete mode 100644 python/tests/fake_catalog.py delete mode 100644 python/tests/test_supermarket.py delete mode 100644 python_pytest/.gitignore delete mode 100644 python_pytest/README.md delete mode 100644 python_pytest/requirements.txt delete mode 100644 python_pytest/src/catalog.py delete mode 100644 python_pytest/src/model_objects.py delete mode 100644 python_pytest/src/receipt.py delete mode 100644 python_pytest/src/shopping_cart.py delete mode 100644 python_pytest/src/teller.py delete mode 100644 python_pytest/src/texttest_fixture.py delete mode 100644 python_pytest/tests/approvaltests_config.json delete mode 100644 python_pytest/tests/fake_catalog.py delete mode 100644 python_pytest/tests/receipt_printer.py delete mode 100644 python_pytest/tests/test_supermarket.py delete mode 100644 ruby/Gemfile delete mode 100644 ruby/Gemfile.lock delete mode 100644 ruby/Rakefile delete mode 100644 ruby/lib/models/discount.rb delete mode 100644 ruby/lib/models/offer.rb delete mode 100644 ruby/lib/models/product.rb delete mode 100644 ruby/lib/models/product_quantity.rb delete mode 100644 ruby/lib/models/product_unit.rb delete mode 100644 ruby/lib/models/receipt.rb delete mode 100644 ruby/lib/models/receipt_item.rb delete mode 100644 ruby/lib/models/shopping_cart.rb delete mode 100644 ruby/lib/models/special_offer_type.rb delete mode 100644 ruby/lib/models/supermarket_catalog.rb delete mode 100644 ruby/lib/models/teller.rb delete mode 100644 ruby/lib/receipt_printer.rb delete mode 100644 ruby/main.rb delete mode 100644 ruby/test/fake_catalog.rb delete mode 100644 ruby/test/supermarket_test.rb delete mode 100644 ruby/test/test_helper.rb delete mode 100644 swift/SupermarketReceipt.xcodeproj/project.pbxproj delete mode 100644 swift/SupermarketReceipt.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 swift/SupermarketReceipt.xcodeproj/xcshareddata/xcschemes/SupermarketReceipt.xcscheme delete mode 100644 swift/SupermarketReceipt.xcodeproj/xcuserdata/andy.xcuserdatad/xcschemes/xcschememanagement.plist delete mode 100644 swift/SupermarketReceipt.xcworkspace/contents.xcworkspacedata delete mode 100644 swift/SupermarketReceipt.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 swift/SupermarketReceipt.xcworkspace/xcshareddata/xcschemes/ApprovalTests_Swift.xcscheme delete mode 100644 swift/SupermarketReceipt/Discount.swift delete mode 100644 swift/SupermarketReceipt/Info.plist delete mode 100644 swift/SupermarketReceipt/Offer.swift delete mode 100644 swift/SupermarketReceipt/Product.swift delete mode 100644 swift/SupermarketReceipt/ProductQuantity.swift delete mode 100644 swift/SupermarketReceipt/ProductUnit.swift delete mode 100644 swift/SupermarketReceipt/Receipt.swift delete mode 100644 swift/SupermarketReceipt/ReceiptItem.swift delete mode 100644 swift/SupermarketReceipt/ReceiptPrinter.swift delete mode 100644 swift/SupermarketReceipt/ShoppingCart.swift delete mode 100644 swift/SupermarketReceipt/SpecialOfferType.swift delete mode 100644 swift/SupermarketReceipt/SupermarketCatalog.swift delete mode 100644 swift/SupermarketReceipt/SupermarketReceipt.h delete mode 100644 swift/SupermarketReceipt/Teller.swift delete mode 100644 swift/SupermarketReceiptTests/Info.plist delete mode 100644 typescript/.gitignore delete mode 100644 typescript/package-lock.json delete mode 100644 typescript/package.json delete mode 100644 typescript/src/ReceiptPrinter.ts delete mode 100644 typescript/src/model/Discount.ts delete mode 100644 typescript/src/model/Offer.ts delete mode 100644 typescript/src/model/Product.ts delete mode 100644 typescript/src/model/ProductQuantity.ts delete mode 100644 typescript/src/model/ProductUnit.ts delete mode 100644 typescript/src/model/Receipt.ts delete mode 100644 typescript/src/model/ReceiptItem.ts delete mode 100644 typescript/src/model/ShoppingCart.ts delete mode 100644 typescript/src/model/SpecialOfferType.ts delete mode 100644 typescript/src/model/SupermarketCatalog.ts delete mode 100644 typescript/src/model/Teller.ts delete mode 100644 typescript/test/FakeCatalog.ts delete mode 100644 typescript/test/Supermarket.test.ts delete mode 100644 typescript/tsconfig.json diff --git a/c/.gitignore b/c/.gitignore deleted file mode 100644 index 401f294..0000000 --- a/c/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/build_meson/ -/subprojects/Catch2-*/ -/subprojects/packagecache/ -/cmake-build-*/ diff --git a/c/CMakeLists.txt b/c/CMakeLists.txt deleted file mode 100644 index b153591..0000000 --- a/c/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -set(CMAKE_VERBOSE_MAKEFILE ON) -project(SupermarketReceipt VERSION 1.0) - -set(CMAKE_C_STANDARD 99) - -# uncomment these lines to enable coverage measurements using gcov -#set(CMAKE_CXX_FLAGS "--coverage") - -enable_testing() -add_subdirectory(src) -add_subdirectory(test-catch2) -add_subdirectory(test-gtest) - diff --git a/c/README.md b/c/README.md deleted file mode 100644 index b1edaa1..0000000 --- a/c/README.md +++ /dev/null @@ -1,4 +0,0 @@ -Sample project -============== - -This is a C empty starter project. The production code in "src" is in C, and the test code is written in C++. There are two test frameworks to choose from - GTest and Catch2. diff --git a/c/src/CMakeLists.txt b/c/src/CMakeLists.txt deleted file mode 100644 index 4292dfc..0000000 --- a/c/src/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -set(SRC_LIB_NAME src) -set(SOURCES supermarket.c printer.h printer.c) -add_library(${SRC_LIB_NAME} ${SOURCES}) -target_sources(${SRC_LIB_NAME} PRIVATE ${SOURCES}) -target_include_directories(${SRC_LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/c/src/printer.c b/c/src/printer.c deleted file mode 100644 index ddfedd2..0000000 --- a/c/src/printer.c +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include -#include "printer.h" - -void printReceiptItem(char *buffer, struct receipt_item_t *item); - -void printDiscount(char *buffer, struct discount_t *pDiscount); - -void printTotal(const char *buffer, struct receipt_t *receipt); - -void print_receipt(char* buffer, struct receipt_t* receipt) { - for (int i = 0; i < receipt->itemCount; ++i) { - printReceiptItem(buffer, &receipt->items[i]); - } - for (int i = 0; i < receipt->discountCount; ++i) { - printDiscount(buffer, &receipt->discounts[i]); - } - sprintf(buffer + strlen(buffer), "\n"); - - print_total(buffer, receipt); - -} - -void printReceiptItem(char *buffer, struct receipt_item_t *item) { - char price[MAX_NAME_LENGTH]; - sprintf(price, "%.2f", (*item).totalPrice); - char name[MAX_NAME_LENGTH]; - sprintf(name, "%s", (*item).product->name); - - char line[LINE_LENGTH]; - print_line(line, name, price); - sprintf(buffer + strlen(buffer), "%s", line); - - if (item->quantity != 1) { - char line2[LINE_LENGTH]; - if (item->product->unit == Each) { - sprintf(line2, " %.2f*%.0f", item->price, item->quantity); - } else { - sprintf(line2, " %.2f*%.3f", item->price, item->quantity); - } - sprintf(buffer + strlen(buffer), "%s\n", line2); - } -} - -void print_total(char *buffer, struct receipt_t *receipt) { - char total[MAX_NAME_LENGTH]; - sprintf(total, "%0.2f", total_price(receipt)); - char total_line[LINE_LENGTH]; - print_line(total_line, "Total:", total); - sprintf(buffer + strlen(buffer), "%s", total_line); -} - -void printDiscount(char *buffer, struct discount_t *discount) { - char name[LINE_LENGTH]; - sprintf(name, "%s(%s)", discount->description, discount->product->name); - char price[MAX_NAME_LENGTH]; - sprintf(price, "%.2f", discount->amount); - - char line[LINE_LENGTH]; - print_line(line, name, price); - sprintf(buffer + strlen(buffer), "%s", line); -} - - -void -print_line(char *buffer, const char *key, const char *value) { - int whitespace_length = LINE_LENGTH - strlen(key) - strlen(value); - char whitespace[whitespace_length]; - for (int i = 0; i < whitespace_length -1; ++i) { - whitespace[i] = ' '; - } - whitespace[whitespace_length-1] = '\0'; - - sprintf(buffer, "%s%s%s\n", key, whitespace, value ); -} diff --git a/c/src/printer.h b/c/src/printer.h deleted file mode 100644 index b4aa981..0000000 --- a/c/src/printer.h +++ /dev/null @@ -1,17 +0,0 @@ - - -#ifndef SUPERMARKETRECEIPT_PRINTER_H -#define SUPERMARKETRECEIPT_PRINTER_H - -#include "supermarket.h" - -// maximum buffer size -#define MAX_PRINT_LENGTH 20000 -// lines are fixed width -#define LINE_LENGTH 60 - -void print_receipt(char* buffer, struct receipt_t* receipt); -void print_line(char *buffer, const char *key, const char *value); -void print_total(char *buffer, struct receipt_t *receipt); - -#endif //SUPERMARKETRECEIPT_PRINTER_H diff --git a/c/src/supermarket.c b/c/src/supermarket.c deleted file mode 100644 index 414ce1d..0000000 --- a/c/src/supermarket.c +++ /dev/null @@ -1,151 +0,0 @@ -#include -#include -#include -#include "supermarket.h" - -struct product_t* product_create(char* name, enum unit unit) { - struct product_t* product = malloc(sizeof(*product)); - strncpy(product->name, name, sizeof(product->name) - 1); - product->unit = unit; - return product; -} - -struct catalog_t *catalog_create(struct product_t *products, const double *prices, int product_count) { - struct catalog_t* catalog = malloc(sizeof(*catalog)); - catalog->product_count = product_count; - for (int i = 0; i < product_count; ++i) { - catalog->products[i] = products[i]; - catalog->prices[i] = prices[i]; - } - return catalog; -} - -struct teller_t *teller_create(struct catalog_t *catalog, int product_count, struct special_offer_t *offer) { - struct teller_t* teller = malloc(sizeof(*teller)); - teller->catalog = catalog; - teller->offer = offer; - return teller; -} - -struct special_offer_t* special_offer_create(enum SpecialOfferType type, struct product_t *product, float argument) { - struct special_offer_t* special_offer = malloc(sizeof(*special_offer)); - special_offer->type = type; - special_offer->product = product; - special_offer->argument = argument; - - return special_offer; -} - -struct cart_t* cart_create(struct product_t products[], const double quantities[], int product_count) { - struct cart_t* cart = malloc(sizeof(*cart)); - cart->product_count = product_count; - for (int i = 0; i < product_count; ++i) { - cart->products[i] = products[i]; - cart->quantities[i] = quantities[i]; - } - return cart; -} - -struct receipt_item_t * -receipt_item_create(struct product_t* product, double quantity, double price, double totalPrice) { - struct receipt_item_t* item = malloc(sizeof(*item)); - item->product = product; - item->quantity = quantity; - item->price = price; - item->totalPrice = totalPrice; - return item; -} - -struct discount_t* discount_create(char* description, double amount, struct product_t* product) { - struct discount_t* discount = malloc(sizeof(discount)); - discount->product = product; - strcpy(discount->description, description); - discount->amount = amount; - - return discount; -} - -void handle_offers(struct cart_t* cart, struct receipt_t* receipt, struct special_offer_t* offer, struct catalog_t* catalog) { - for (int i = 0; i < cart->product_count; ++i) { - struct product_t product = cart->products[i]; - double quantity = cart->quantities[i]; - if (strcmp(offer->product->name, product.name) == 0) { - double unitPrice = unit_price(catalog, &product); - int quantityAsInt = (int)quantity; - struct discount_t* discount = NULL; - int x = 1; - - if (offer->type == ThreeForTwo) { - x = 3; - } else if (offer->type == TwoForAmount) { - x = 2; - if (quantityAsInt >= 2) { - double total = offer->argument * (quantityAsInt / x) + quantityAsInt % 2 * unitPrice; - double discountN = unitPrice * quantity - total; - char description[MAX_NAME_LENGTH]; - sprintf(description, "2 for %f", offer->argument); - discount = discount_create(description, -discountN, &product); - } - } if (offer->type == FiveForAmount) { - x = 5; - } - int numberOfXs = quantityAsInt / x; - if (offer->type == ThreeForTwo && quantityAsInt > 2) { - double discountAmount = quantity * unitPrice - ((numberOfXs * 2 * unitPrice) + quantityAsInt % 3 * unitPrice); - char description[MAX_NAME_LENGTH]; - sprintf(description, "3 for 2"); - discount = discount_create(description, -discountAmount, &product); - } - if (offer->type == TenPercentDiscount) { - char description[MAX_NAME_LENGTH]; - sprintf(description, "%.0f%% off", offer->argument); - discount = discount_create(description, -quantity * unitPrice * offer->argument / 100.0, &product); - - } - if (offer->type == FiveForAmount && quantityAsInt >= 5) { - double discountTotal = unitPrice * quantity - (offer->argument * numberOfXs + quantityAsInt % 5 * unitPrice); - char description[MAX_NAME_LENGTH]; - sprintf(description, "%d for %f", x, offer->argument); - discount = discount_create(description, -discountTotal, &product); - } - if (discount != NULL) { - receipt->discounts[receipt->discountCount] = *discount; - receipt->discountCount++; - } - } - } -} - -struct receipt_t *check_out_articles(struct teller_t* teller, struct cart_t* cart) { - struct receipt_t* receipt = malloc(sizeof(*receipt)); - receipt->itemCount = cart->product_count; - for (int i = 0; i < cart->product_count; ++i) { - double uprice = unit_price(teller->catalog, &cart->products[i]); - double price = uprice * cart->quantities[i]; - struct receipt_item_t* item = receipt_item_create(&cart->products[i], cart->quantities[i], uprice, price); - receipt->items[i] = *item; - } - handle_offers(cart, receipt, teller->offer, teller->catalog); - - return receipt; -} - -double total_price(struct receipt_t *receipt) { - double total = 0; - for (int i = 0; i < receipt->itemCount; ++i) { - total += receipt->items[i].totalPrice; - } - for (int i = 0; i < receipt->discountCount; ++i) { - total += receipt->discounts[i].amount; - } - return total; -} - - -double unit_price(struct catalog_t* catalog, struct product_t *product) { - for (int i = 0; i < catalog->product_count; ++i) { - if (strcmp(catalog->products[i].name, product->name) == 0) - return catalog->prices[i]; - } - return 0; -} diff --git a/c/src/supermarket.h b/c/src/supermarket.h deleted file mode 100644 index 20085a9..0000000 --- a/c/src/supermarket.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef SAMPLE_H -#define SAMPLE_H - -enum unit { - Each, - Kilo -}; - -enum SpecialOfferType { - TenPercentDiscount, - ThreeForTwo, - TwoForAmount, - FiveForAmount -}; - -#define MAX_NAME_LENGTH 100 -#define MAX_PRODUCTS 100 - -struct product_t { - char name[MAX_NAME_LENGTH]; - enum unit unit; -}; - -struct catalog_t { - int product_count; - struct product_t products[MAX_PRODUCTS]; - double prices[MAX_PRODUCTS]; -}; - -struct special_offer_t { - enum SpecialOfferType type; - struct product_t* product; - float argument; -}; - -struct teller_t { - struct catalog_t* catalog; - struct special_offer_t* offer; -}; - -struct cart_t { - int product_count; - struct product_t products[MAX_PRODUCTS]; - double quantities[MAX_PRODUCTS]; -}; - -struct receipt_item_t { - struct product_t* product; - double quantity; - double price; - double totalPrice; -}; - -struct discount_t { - struct product_t* product; - char description[MAX_NAME_LENGTH]; - double amount; -}; - -struct receipt_t { - int itemCount; - struct receipt_item_t items[MAX_PRODUCTS]; - int discountCount; - struct discount_t discounts[MAX_PRODUCTS]; -}; - -struct product_t* product_create(char* name, enum unit unit); -struct special_offer_t* special_offer_create(enum SpecialOfferType type, struct product_t *product, float argument); -struct catalog_t* catalog_create(struct product_t products[], const double prices[], int product_count); -struct teller_t *teller_create(struct catalog_t *products, int product_count, struct special_offer_t *offer); -struct cart_t* cart_create(struct product_t products[], const double quantities[], int product_count); -struct receipt_item_t* receipt_item_create(struct product_t* product, double quantity, double price, double totalPrice); -struct discount_t* discount_create(char* description, double discount, struct product_t* product); - -struct receipt_t* check_out_articles(struct teller_t* teller, struct cart_t* cart); -double total_price(struct receipt_t* receipt); -double unit_price(struct catalog_t* catalog, struct product_t *product); -void handle_offers(struct cart_t* cart, struct receipt_t* receipt, struct special_offer_t* offer, struct catalog_t* catalog); - -#endif //SAMPLE_H diff --git a/c/test-catch2/CMakeLists.txt b/c/test-catch2/CMakeLists.txt deleted file mode 100644 index ec2d881..0000000 --- a/c/test-catch2/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -set(TEST_NAME sample_test_catch2) -include(FetchContent) -set(SOURCE_FILES main.cpp supermarket_catch.cpp) - -# catch2 -FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v2.13.10) -FetchContent_MakeAvailable(Catch2) - -# approvals -FetchContent_Declare( - ApprovalTests - GIT_REPOSITORY https://github.com/approvals/ApprovalTests.cpp - GIT_TAG v.10.5.1) -FetchContent_MakeAvailable(ApprovalTests) - -add_executable(${TEST_NAME}) -target_sources(${TEST_NAME} PRIVATE ${SOURCE_FILES}) -target_link_libraries(${TEST_NAME} src Catch2::Catch2 ApprovalTests::ApprovalTests) -set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 11) -add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) diff --git a/c/test-catch2/main.cpp b/c/test-catch2/main.cpp deleted file mode 100644 index 5cec3a1..0000000 --- a/c/test-catch2/main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#define APPROVALS_CATCH -#include "ApprovalTests.hpp" - -#include -auto defaultReporterDisposer = - ApprovalTests::Approvals::useAsDefaultReporter(std::make_shared()); diff --git a/c/test-catch2/supermarket_catch.cpp b/c/test-catch2/supermarket_catch.cpp deleted file mode 100644 index c86472c..0000000 --- a/c/test-catch2/supermarket_catch.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "ApprovalTests.hpp" -#include "catch2/catch.hpp" - -extern "C" -{ -#include "supermarket.h" -} - - -TEST_CASE ("Supermarket") { - SECTION("Ten percent discount") { - // ARRANGE - struct product_t* toothbrush = product_create("toothbrush", Each); - struct product_t* apples = product_create("apples", Kilo); - double toothbrush_price = 0.99; - double apple_price = 1.99; - struct product_t products[MAX_PRODUCTS] = {*toothbrush, *apples}; - double prices[MAX_PRODUCTS] = {toothbrush_price, apple_price}; - struct catalog_t* catalog = catalog_create(products, prices, 2); - struct special_offer_t* special_offer = special_offer_create(TenPercentDiscount, toothbrush, 10.0); - - struct teller_t* teller = teller_create(catalog, 2, special_offer); - - struct product_t cart_products[] = {*apples}; - double cart_quantities[] = {2.5}; - struct cart_t* cart = cart_create(cart_products, cart_quantities, 1); - - // ACT - struct receipt_t* receipt = check_out_articles(teller, cart); - - // ASSERT - CHECK(total_price(receipt) == 4.975); - CHECK(0 == receipt->discountCount); - CHECK(1 == receipt->itemCount); - struct receipt_item_t item = receipt->items[0]; - CHECK(1.99 == item.price); - CHECK(2.5*1.99 == item.totalPrice); - CHECK(2.5 == item.quantity); - } -} - - diff --git a/c/test-gtest/CMakeLists.txt b/c/test-gtest/CMakeLists.txt deleted file mode 100644 index 7d83c34..0000000 --- a/c/test-gtest/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -set(TEST_NAME sample_test_gtest) -include(FetchContent) -set(SOURCE_FILES main.cpp supermarket_gtest.cpp) - -# approvals -FetchContent_Declare( - ApprovalTests - GIT_REPOSITORY https://github.com/approvals/ApprovalTests.cpp - GIT_TAG v.10.5.1) -FetchContent_MakeAvailable(ApprovalTests) - -# GoogleTest -FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.10.0 -) -FetchContent_MakeAvailable(googletest) - -add_executable(${TEST_NAME}) -target_sources(${TEST_NAME} PRIVATE ${SOURCE_FILES}) -target_link_libraries(${TEST_NAME} src gtest ApprovalTests::ApprovalTests) -set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 11) -add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) diff --git a/c/test-gtest/main.cpp b/c/test-gtest/main.cpp deleted file mode 100644 index 4ccbe0a..0000000 --- a/c/test-gtest/main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#define APPROVALS_GOOGLETEST -#include "ApprovalTests.hpp" - -#include -auto defaultReporterDisposer = - ApprovalTests::Approvals::useAsDefaultReporter(std::make_shared()); diff --git a/c/test-gtest/supermarket_gtest.cpp b/c/test-gtest/supermarket_gtest.cpp deleted file mode 100644 index e05e479..0000000 --- a/c/test-gtest/supermarket_gtest.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include - -extern "C" -{ -#include "supermarket.h" -#include "printer.h" -} - -using namespace std; - -TEST(Supermarket, TenPercentDiscount) -{ - // ARRANGE - struct product_t* toothbrush = product_create("toothbrush", Each); - struct product_t* apples = product_create("apples", Kilo); - double toothbrush_price = 0.99; - double apple_price = 1.99; - struct product_t products[MAX_PRODUCTS] = {*toothbrush, *apples}; - double prices[MAX_PRODUCTS] = {toothbrush_price, apple_price}; - struct catalog_t* catalog = catalog_create(products, prices, 2); - struct special_offer_t* special_offer = special_offer_create(TenPercentDiscount, toothbrush, 10.0); - - struct teller_t* teller = teller_create(catalog, 2, special_offer); - - struct product_t cart_products[] = {*apples}; - double cart_quantities[] = {2.5}; - struct cart_t* cart = cart_create(cart_products, cart_quantities, 1); - - // ACT - struct receipt_t* receipt = check_out_articles(teller, cart); - - // ASSERT - EXPECT_NEAR(total_price(receipt), 4.98, 0.01); - EXPECT_EQ(0, receipt->discountCount); - EXPECT_EQ(1, receipt->itemCount); - struct receipt_item_t item = receipt->items[0]; - EXPECT_EQ(1.99, item.price); - EXPECT_EQ(2.5*1.99, item.totalPrice); - EXPECT_EQ(2.5, item.quantity); -} diff --git a/common-lisp/README.md b/common-lisp/README.md deleted file mode 100755 index ab1769c..0000000 --- a/common-lisp/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# supermarket-receipt-refactoring-kata - -The description of the project can be found here: -https://github.com/emilybache/SupermarketReceipt-Refactoring-Kata/blob/main/README.md - -# Setup - -## Install quicklisp - -To run this project install quicklisp (if not already done): - - download https://beta.quicklisp.org/quicklisp.lisp - - load it with your common lisp implementation. The example with sbcl: - > sbcl --load quicklisp.lisp - - run the command - > (quicklisp-quickstart:install) - in your common lisp implementation - - run the command - > (ql:add-to-init-file) - in your common lisp implementation - -## Install project -Copy the project-folder containing this file into /quicklisp/local-projects/ that has been created when installing quicklisp. -This is the root directory for quicklisp to search for the supermarket-receipt.asd file which defines the system (project) and its dependencies. -The quicklisp-folder is usually created in your home-directory. -Instead of copying you can also symlink the project-folder into /quicklisp/local-projects. - -## Work with the project - -Now you can load the project with -> (ql:quickload "supermarket-receipt") -in the common lisp implementation of your choice and run the tests with -> (asdf:test-system "supermarket-receipt") -. - -If you just want to run the tests -> (asdf:test-system "supermarket-receipt") -is sufficient. - -You can mock functions and methods with the cl-mock-library which is already included in the system definition of the test-system: - -(with-mocks () - (answer (call-previous)) - ( ) - (is (invocations '))) - -If you just want to stub functions you can replace (call-previous) with a return value of your choice and your test does not depend on (invocations ') - - diff --git a/common-lisp/source/catalog.lisp b/common-lisp/source/catalog.lisp deleted file mode 100644 index 3139982..0000000 --- a/common-lisp/source/catalog.lisp +++ /dev/null @@ -1,17 +0,0 @@ -;;;; catalog.lisp - -(in-package :supermarket-receipt) - -(defclass supermarket-catalog () - ((products :initform nil - :type list - :accessor catalog-products) - (prices :initform nil - :type list - :accessor catalog-prices))) - -(defmethod add-product-to-catalog ((a-catalog supermarket-catalog) (a-product product) (a-price single-float)) - (error "cannot be called from a unit test - it accesses the database")) - -(defmethod unit-price ((a-catalog supermarket-catalog) (a-product product)) - (error "cannot be called from a unit test - it accesses the database")) diff --git a/common-lisp/source/model-objects.lisp b/common-lisp/source/model-objects.lisp deleted file mode 100644 index 88eae98..0000000 --- a/common-lisp/source/model-objects.lisp +++ /dev/null @@ -1,44 +0,0 @@ -;;;; model-objects.lisp - -(in-package :supermarket-receipt) - -(deftype product-unit () '(member each kilo)) - -(defclass product () - ((name :initarg :name - :type string - :accessor product-name) - (unit :initarg :unit - :type product-unit - :accessor the-product-unit))) - -(defclass product-quantity () - ((product :initarg :product - :type product - :accessor the-quantities-product) - (quantity :initarg :quantity - :type single-float - :accessor quantity))) - -(deftype special-offer-type () '(member three-for-two ten-percent-discount two-for-amount five-for-amount)) - -(defclass offer () - ((offer-type :initarg :type - :type special-offer-type - :accessor offer-type) - (product :initarg :product - :type product - :accessor offered-product) - (argument :initarg :argument - :accessor offer-argument))) - -(defclass discount () - ((product :initarg :product - :type product - :accessor discounted-product) - (description :initarg :description - :type string - :accessor discount-description) - (amount :initarg :amount - :type single-float - :accessor discount-amount))) diff --git a/common-lisp/source/package.lisp b/common-lisp/source/package.lisp deleted file mode 100755 index 2e0b7d5..0000000 --- a/common-lisp/source/package.lisp +++ /dev/null @@ -1,38 +0,0 @@ -;;;; package.lisp - -(defpackage :supermarket-receipt - (:use :common-lisp) - (:export - :product - :product-name - :special-offer-type - :product-unit - :each - :kilo - :shopping-cart - :add-item-quantity - :teller - :add-special-offer - :checks-out-articles-from - :discount - :receipt - :receipt-items - :receipt-discounts - :add-product-to-receipt - :add-discount - :ten-percent-discount - :three-for-two - :two-for-amount - :five-for-amount - :item-product - :total-price - :item-quantity - :item-price - :item-total-price - :supermarket-catalog - :catalog-products - :catalog-prices - :add-product-to-catalog - :unit-price - :receipt-printer - :print-receipt)) diff --git a/common-lisp/source/receipt-printer.lisp b/common-lisp/source/receipt-printer.lisp deleted file mode 100644 index b541373..0000000 --- a/common-lisp/source/receipt-printer.lisp +++ /dev/null @@ -1,56 +0,0 @@ -;; receipt-printer.lisp - -(in-package :supermarket-receipt) - -(defclass receipt-printer () - ((columns :initarg :columns - :initform 40 - :type integer - :accessor printer-columns))) - -(defmethod print-receipt ((a-printer receipt-printer) (a-receipt receipt)) - (let ((result "")) - (loop for item in (reverse (receipt-items a-receipt)) do - (setf result (concatenate 'string result (print-receipt-item a-printer item)))) - (loop for discount in (reverse (receipt-discounts a-receipt)) do - (setf result (concatenate 'string result (print-discount a-printer discount)))) - (setf result (format nil "~A~%" result)) - (setf result (concatenate 'string result (present-total a-printer a-receipt))) - result)) - -(defmethod print-receipt-item ((a-printer receipt-printer) (an-item receipt-item)) - (let* ((total-price-printed (print-price a-printer (item-total-price an-item))) - (name (product-name (item-product an-item))) - (line (format-line-with-whitespace a-printer name total-price-printed))) - (unless (= 1.0 (item-quantity an-item)) - (setf line (format nil "~A ~A * ~A~%" line - (print-price a-printer (item-price an-item)) - (print-quantity a-printer an-item)))) - line)) - -(defmethod format-line-with-whitespace ((a-printer receipt-printer) (a-name string) (a-value string)) - (let* ((line a-name) - (whitespace-size (- (printer-columns a-printer) (length a-name) (length a-value)))) - (loop for index from 1 to whitespace-size do - (setf line (concatenate 'string line " "))) - (setf line (concatenate 'string line a-value)) - (setf line (format nil "~A~%" line)) - line)) - -(defmethod print-price ((a-printer receipt-printer) (a-price single-float)) - (format nil "~2$" a-price)) - -(defmethod print-quantity ((a-printer receipt-printer) (an-item receipt-item)) - (if (eq 'each (the-product-unit (item-product an-item))) - (format nil "~d" (floor (item-quantity an-item))) - (format nil "~3$" (item-quantity an-item)))) - -(defmethod print-discount ((a-printer receipt-printer) (a-discount discount)) - (let ((name (format nil "~A(~A)" (discount-description a-discount) (product-name (discounted-product a-discount)))) - (value (print-price a-printer (discount-amount a-discount)))) - (format-line-with-whitespace a-printer name value))) - -(defmethod present-total ((a-printer receipt-printer) (a-receipt receipt)) - (let ((name "Total: ") - (value (print-price a-printer (total-price a-receipt)))) - (format-line-with-whitespace a-printer name value))) diff --git a/common-lisp/source/receipt.lisp b/common-lisp/source/receipt.lisp deleted file mode 100644 index 0984402..0000000 --- a/common-lisp/source/receipt.lisp +++ /dev/null @@ -1,40 +0,0 @@ -;;;; receipt.lisp - -(in-package :supermarket-receipt) - -(defclass receipt-item () - ((product :initarg :product - :type product - :accessor item-product) - (quantity :initarg :quantity - :type product-quantity - :accessor item-quantity) - (price :initarg :price - :type single-float - :accessor item-price) - (total-price :initarg :total-price - :type single-float - :accessor item-total-price))) - -(defclass receipt () - ((items :initform nil - :type list - :accessor receipt-items) - (discounts :initform nil - :type list - :accessor receipt-discounts))) - -(defmethod total-price ((a-receipt receipt)) - (+ (reduce #'+ (mapcar #'item-total-price (receipt-items a-receipt))) - (reduce #'+ (mapcar #'discount-amount (receipt-discounts a-receipt))))) - -(defmethod add-product-to-receipt ((a-receipt receipt) (a-product product) (a-quantity single-float) (a-price single-float) (a-total-price single-float)) - (push (make-instance 'receipt-item - :product a-product - :quantity a-quantity - :price a-price - :total-price a-total-price) - (receipt-items a-receipt))) - -(defmethod add-discount ((a-receipt receipt) (a-discount discount)) - (push a-discount (receipt-discounts a-receipt))) diff --git a/common-lisp/source/shopping-cart.lisp b/common-lisp/source/shopping-cart.lisp deleted file mode 100644 index 51631a2..0000000 --- a/common-lisp/source/shopping-cart.lisp +++ /dev/null @@ -1,77 +0,0 @@ -;;;; shopping-cart.lisp - -(in-package :supermarket-receipt) - -(defclass shopping-cart () - ((items :initform nil - :type list - :accessor shopping-cart-items) - (product-quantities :initform nil - :type list - :accessor shopping-cart-product-quantities))) - -(defmethod add-item ((a-cart shopping-cart) (an-item product)) - (add-item-quantity a-cart an-item 1.0)) - -(defmethod add-item-quantity ((a-cart shopping-cart) (an-item product) (a-quantity single-float)) - (push (make-instance 'product-quantity - :product an-item - :quantity a-quantity) - (shopping-cart-items a-cart)) - (if (assoc an-item (shopping-cart-product-quantities a-cart)) - (incf (cdr (assoc an-item (shopping-cart-product-quantities a-cart))) a-quantity) - (push (cons an-item a-quantity) (shopping-cart-product-quantities a-cart)))) - -(defmethod handle-offers ((a-cart shopping-cart) (a-receipt receipt) (offers list) (a-catalog supermarket-catalog)) - (let* ((product-quantities (shopping-cart-product-quantities a-cart)) - (products (mapcar #'car product-quantities))) - (mapcar (lambda (a-product) - (let ((a-quantity (cdr (assoc a-product product-quantities))) - (offer-for-product (cdr (assoc a-product offers)))) - (when offer-for-product - (let ((a-unit-price (unit-price a-catalog a-product)) - (floored-quantity (floor a-quantity)) - (a-discount nil) - (x 1) - (the-offer-type (offer-type offer-for-product))) - (if (eq the-offer-type 'three-for-two) - (setf x 3) - (when (eq the-offer-type 'two-for-amount) - (setf x 2) - (when (>= floored-quantity 2) - (let* ((total (+ (* (offer-argument offer-for-product) (floor (/ floored-quantity x))) - (* (mod floored-quantity 2) a-unit-price))) - (discount-n (- (* a-unit-price a-quantity) total))) - (setf a-discount (make-instance 'discount - :product a-product - :description (format nil "2 for ~S" (offer-argument offer-for-product)) - :amount (- discount-n))))))) - (when (eq the-offer-type 'five-for-amount) - (setf x 5)) - (let ((number-of-x (floor (/ floored-quantity x)))) - (when (and (eq the-offer-type 'three-for-two) - (> floored-quantity 2)) - (let ((discount-amount (- (* a-quantity a-unit-price) - (+ (* number-of-x 2 a-unit-price) - (* (mod floored-quantity 3) a-unit-price))))) - (setf a-discount (make-instance 'discount - :product a-product - :description "3 for 2" - :amount (- discount-amount))))) - (when (eq the-offer-type 'ten-percent-discount) - (setf a-discount (make-instance 'discount - :product a-product - :description (format nil "~S % off" (offer-argument offer-for-product)) - :amount (/ (* (- a-quantity) a-unit-price (offer-argument offer-for-product)) 100.0)))) - (when (and (eq the-offer-type 'five-for-amount) - (>= floored-quantity 5)) - (let ((discount-total (- (* a-quantity a-unit-price) - (+ (* (offer-argument offer-for-product) number-of-x) - (* (mod floored-quantity 5) a-unit-price))))) - (setf a-discount (make-instance 'discount - :product a-product - :description (format nil "~S for ~S" x (offer-argument offer-for-product)) - :amount (- discount-total))))) - (when a-discount - (add-discount a-receipt a-discount))))))) - products))) diff --git a/common-lisp/source/teller.lisp b/common-lisp/source/teller.lisp deleted file mode 100755 index ba5cc7b..0000000 --- a/common-lisp/source/teller.lisp +++ /dev/null @@ -1,41 +0,0 @@ -;;;; teller.lisp - -(in-package :supermarket-receipt) - -(defclass teller () - ((catalog :initarg :catalog - :type supermarket-catalog - :accessor teller-catalog) - (offers :initform nil - :type list - :accessor teller-offers))) - -(defmethod add-special-offer ((a-teller teller) an-offer-type (a-product product) an-argument) - (push (cons a-product (make-instance 'offer - :type an-offer-type - :product a-product - :argument an-argument)) - (teller-offers a-teller))) - -(defmethod checks-out-articles-from ((a-teller teller) (a-cart shopping-cart)) - (let ((a-receipt (make-instance 'receipt)) - (product-quantities (shopping-cart-items a-cart)) - (a-catalog (teller-catalog a-teller))) - (mapcar (lambda (a-product-quantity) - (let* ((a-product (the-quantities-product a-product-quantity)) - (a-quantity (quantity a-product-quantity)) - (a-unit-price (unit-price a-catalog a-product))) - (add-product-to-receipt a-receipt - a-product - a-quantity - a-unit-price - (* a-quantity a-unit-price)) - (format t "~& ~S: ~S" (product-name a-product) (* a-quantity a-unit-price)))) - product-quantities) - (handle-offers a-cart a-receipt (teller-offers a-teller) a-catalog) - a-receipt)) - -(defmethod product-with-name ((a-teller teller) (a-name string)) - (let ((name-with-product (assoc a-name (catalog-products (teller-catalog a-teller))))) - (when name-with-product - (cdr name-with-product)))) diff --git a/common-lisp/supermarket-receipt.asd b/common-lisp/supermarket-receipt.asd deleted file mode 100755 index 70a5830..0000000 --- a/common-lisp/supermarket-receipt.asd +++ /dev/null @@ -1,29 +0,0 @@ -;;;; supermarket-receipt.asd - -(asdf:defsystem "supermarket-receipt" - :description "" - :author "price-queen@supermarket.com" - :version "1.0.0" - :depends-on () - :pathname "source/" - :serial T - :components ((:file "package") - (:file "model-objects") - (:file "receipt") - (:file "catalog") - (:file "shopping-cart") - (:file "teller") - (:file "receipt-printer")) - :in-order-to ((test-op (test-op "supermarket-receipt/tests")))) - - -(asdf:defsystem "supermarket-receipt/tests" - :description "Unit tests for supermarket-receipt" - :author "price-queen@supermarket.com" - :version "1.0.0" - :depends-on ("supermarket-receipt" "parachute" "cl-mock") - :pathname "tests/" - :components ((:file "package") - (:file "fake-catalog") - (:file "tests" :depends-on ("package"))) - :perform (test-op (o c) (symbol-call :parachute :test :supermarket-receipt/tests))) diff --git a/common-lisp/tests/fake-catalog.lisp b/common-lisp/tests/fake-catalog.lisp deleted file mode 100644 index fab039a..0000000 --- a/common-lisp/tests/fake-catalog.lisp +++ /dev/null @@ -1,13 +0,0 @@ -;;;; fake-catalog.lisp - -(in-package :supermarket-receipt/tests) - -(defclass fake-catalog (supermarket-catalog) - ()) - -(defmethod add-product-to-catalog ((a-catalog fake-catalog) (a-product product) (a-price single-float)) - (push (cons (product-name a-product) a-product) (catalog-products a-catalog)) - (push (cons (product-name a-product) a-price) (catalog-prices a-catalog))) - -(defmethod unit-price ((a-catalog fake-catalog) (a-product product)) - (cdr (assoc (product-name a-product) (catalog-prices a-catalog)))) diff --git a/common-lisp/tests/package.lisp b/common-lisp/tests/package.lisp deleted file mode 100755 index d99d329..0000000 --- a/common-lisp/tests/package.lisp +++ /dev/null @@ -1,13 +0,0 @@ -;;;; package.lisp - -(defpackage :supermarket-receipt/tests - (:use :common-lisp - :supermarket-receipt) - (:import-from :parachute - :define-test - :is) - (:import-from :cl-mock - :with-mocks - :answer - :call-previous - :invocations)) diff --git a/common-lisp/tests/tests.lisp b/common-lisp/tests/tests.lisp deleted file mode 100755 index 1219bbb..0000000 --- a/common-lisp/tests/tests.lisp +++ /dev/null @@ -1,38 +0,0 @@ -;;;; tests.lisp - -(in-package :supermarket-receipt/tests) - -(define-test supermarket-receipt-testsuite) - -(define-test "Ten percent discount" - :parent supermarket-receipt-testsuite - (let* ((a-catalog (make-instance 'fake-catalog)) - (a-toothbrush (make-instance 'product - :name "toothbrush" - :unit 'each)) - (apples (make-instance 'product - :name "apples" - :unit 'kilo)) - (a-teller (make-instance 'teller - :catalog a-catalog)) - (a-cart (make-instance 'shopping-cart))) - (add-product-to-catalog a-catalog a-toothbrush 0.99) - (add-product-to-catalog a-catalog apples 1.99) - (add-special-offer a-teller 'ten-percent-discount a-toothbrush 10.0) - (add-item-quantity a-cart apples 2.5) - (let* ((a-receipt (checks-out-articles-from a-teller a-cart)) - (total-receipt-price (total-price a-receipt)) - (discounts (receipt-discounts a-receipt)) - (items (receipt-items a-receipt)) - (first-receipt-item (first items)) - (receipt-item-product (item-product first-receipt-item)) - (the-price (item-price first-receipt-item)) - (the-total-price (item-total-price first-receipt-item)) - (the-quantity (item-quantity first-receipt-item))) - (is = 4.975 total-receipt-price) - (is equal nil discounts) - (is = 1 (length items)) - (is equal apples receipt-item-product) - (is = 1.99 the-price) - (is = (* 2.5 1.99) the-total-price) - (is = 2.5 the-quantity)))) diff --git a/cpp/.gitignore b/cpp/.gitignore deleted file mode 100644 index f5163e0..0000000 --- a/cpp/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/build_meson/ -/subprojects/Catch2-*/ -/subprojects/packagecache/ -/cmake-build-*/ -*received* diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt deleted file mode 100644 index 394f22c..0000000 --- a/cpp/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -set(CMAKE_VERBOSE_MAKEFILE ON) -project(SupermarketReceipt VERSION 1.0 - LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) - -# uncomment these lines to enable coverage measurements using gcov -#set(CMAKE_CXX_FLAGS "--coverage") - -# uncomment these lines to enable coverage measurements using VS2022 -#set(CMAKE_CXX_FLAGS "--coverage /EHsc") - -enable_testing() -add_subdirectory(src) -add_subdirectory(test-catch2) -add_subdirectory(test-doctest) -add_subdirectory(test-gtest) - diff --git a/cpp/README.md b/cpp/README.md deleted file mode 100644 index 2b7303d..0000000 --- a/cpp/README.md +++ /dev/null @@ -1,7 +0,0 @@ -Sample project -============== - -There are three test projects here, using the popular test frameworks [Catch2](https://github.com/catchorg/Catch2, [Doctest](https://github.com/doctest/doctest) and [GoogleTest](http://google.github.io/googletest/) respectively. Choose the one you prefer. You could use [Approvals](https://approvaltestscpp.readthedocs.io/en/latest/index.html) with any of them. - -Refer to [top level README](../README.md) for exercise instructions. - diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt deleted file mode 100644 index 6f51e75..0000000 --- a/cpp/src/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -set(SRC_LIB_NAME src) -set(SOURCES Discount.cpp - Discount.h Product.cpp Product.h - Offer.cpp Offer.h SpecialOfferType.h - ProductUnit.h ProductQuantity.cpp ProductQuantity.h - ReceiptItem.cpp ReceiptItem.h Receipt.cpp Receipt.h - ShoppingCart.cpp ShoppingCart.h SupermarketCatalog.h Teller.cpp - Teller.h ReceiptPrinter.h ReceiptPrinter.cpp FakeCatalog.cpp) -add_library(${SRC_LIB_NAME} ${SOURCES}) -target_sources(${SRC_LIB_NAME} PRIVATE ${SOURCES}) -target_include_directories(${SRC_LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/cpp/src/Discount.cpp b/cpp/src/Discount.cpp deleted file mode 100644 index 2bdd76e..0000000 --- a/cpp/src/Discount.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "Discount.h" -#include "Product.h" - -Discount::Discount(const std::string& description, double discountAmount, const Product& product) - : description(description), discountAmount(discountAmount), product(product) {} - -std::string Discount::getDescription() const { - return description; -} - -double Discount::getDiscountAmount() const { - return discountAmount; -} - -Product Discount::getProduct() const { - return product; -} diff --git a/cpp/src/Discount.h b/cpp/src/Discount.h deleted file mode 100644 index 55bc6bd..0000000 --- a/cpp/src/Discount.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef CPP_DISCOUNT_H -#define CPP_DISCOUNT_H - - -#include -#include "Product.h" - -class Discount { -public: - Discount(const std::string& description, double discountAmount, const Product& product); - - std::string getDescription() const; - - double getDiscountAmount() const; - - Product getProduct() const; - -private: - std::string description; - double discountAmount; - Product product; -}; - - -#endif //CPP_DISCOUNT_H diff --git a/cpp/src/FakeCatalog.cpp b/cpp/src/FakeCatalog.cpp deleted file mode 100644 index 56b9d3e..0000000 --- a/cpp/src/FakeCatalog.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// -// Created by sdargo on 2/4/19. -// - -#include "FakeCatalog.h" - -void FakeCatalog::addProduct(const Product& product, double price) { - products[product.getName()] = product; - prices[product.getName()] = price; -} - -double FakeCatalog::getUnitPrice(const Product& product) { - return prices[product.getName()]; -} diff --git a/cpp/src/FakeCatalog.h b/cpp/src/FakeCatalog.h deleted file mode 100644 index 7288694..0000000 --- a/cpp/src/FakeCatalog.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef CPP_FAKECATALOG_H -#define CPP_FAKECATALOG_H - - -#include -#include "SupermarketCatalog.h" - -class FakeCatalog : public SupermarketCatalog { -public: - - void addProduct(const Product& product, double price) override; - - double getUnitPrice(const Product& product) override; - -private: - std::map products; - std::map prices; -}; - - -#endif //CPP_FAKECATALOG_H diff --git a/cpp/src/Offer.cpp b/cpp/src/Offer.cpp deleted file mode 100644 index 755ba4b..0000000 --- a/cpp/src/Offer.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "Offer.h" - -Offer::Offer(const SpecialOfferType& offerType, const Product& product, double argument) - : offerType(offerType), product(product), argument(argument) {} - -SpecialOfferType Offer::getOfferType() const { - return offerType; -} - -Product Offer::getProduct() const { - return product; -} - -double Offer::getArgument() const { - return argument; -} diff --git a/cpp/src/Offer.h b/cpp/src/Offer.h deleted file mode 100644 index 1604bf9..0000000 --- a/cpp/src/Offer.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef CPP_OFFER_H -#define CPP_OFFER_H - - -#include "Product.h" -#include "SpecialOfferType.h" - -class Offer { -public: - Offer() = default; - Offer(const SpecialOfferType& offerType, const Product& product, double argument); - - SpecialOfferType getOfferType() const; - - Product getProduct() const; - - double getArgument() const; - -private: - SpecialOfferType offerType; - Product product; - double argument; - -}; - - -#endif //CPP_OFFER_H diff --git a/cpp/src/Product.cpp b/cpp/src/Product.cpp deleted file mode 100644 index 731d6c8..0000000 --- a/cpp/src/Product.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "Product.h" - -Product::Product(const std::string& name, const ProductUnit& unit) : name(name), unit(unit) {} - -std::string Product::getName() const { - return name; -} - -ProductUnit Product::getUnit() const { - return unit; -} - -bool Product::operator==(const Product& rhs) const { - return name == rhs.name && - unit == rhs.unit; -} - -bool Product::operator!=(const Product& rhs) const { - return !(rhs == *this); -} - -bool Product::operator<(const Product& rhs) const { - if (name < rhs.name) - return true; - if (rhs.name < name) - return false; - return unit < rhs.unit; -} - -bool Product::operator>(const Product& rhs) const { - return rhs < *this; -} - -bool Product::operator<=(const Product& rhs) const { - return !(rhs < *this); -} - -bool Product::operator>=(const Product& rhs) const { - return !(*this < rhs); -} diff --git a/cpp/src/Product.h b/cpp/src/Product.h deleted file mode 100644 index ca4469e..0000000 --- a/cpp/src/Product.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef CPP_PRODUCT_H -#define CPP_PRODUCT_H - - -#include -#include "ProductUnit.h" - -class Product { -public: - Product() = default; - - Product(const std::string& name, const ProductUnit& unit); - - std::string getName() const; - - ProductUnit getUnit() const; - - bool operator==(const Product& rhs) const; - - bool operator!=(const Product& rhs) const; - - bool operator<(const Product& rhs) const; - - bool operator>(const Product& rhs) const; - - bool operator<=(const Product& rhs) const; - - bool operator>=(const Product& rhs) const; - -private: - std::string name; - ProductUnit unit; - -}; - - -#endif //CPP_PRODUCT_H diff --git a/cpp/src/ProductQuantity.cpp b/cpp/src/ProductQuantity.cpp deleted file mode 100644 index f7f5cce..0000000 --- a/cpp/src/ProductQuantity.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "ProductQuantity.h" - -ProductQuantity::ProductQuantity(const Product& product, double quantity) : product(product), quantity(quantity) {} - -Product ProductQuantity::getProduct() const { - return product; -} - -double ProductQuantity::getQuantity() const { - return quantity; -} diff --git a/cpp/src/ProductQuantity.h b/cpp/src/ProductQuantity.h deleted file mode 100644 index f572954..0000000 --- a/cpp/src/ProductQuantity.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef CPP_PRODUCTQUANTITY_H -#define CPP_PRODUCTQUANTITY_H - - -#include "Product.h" - -class ProductQuantity { -private: - -public: - ProductQuantity(const Product& product, double quantity); - - Product getProduct() const; - - double getQuantity() const; - -private: - Product product; - double quantity; - -}; - - -#endif //CPP_PRODUCTQUANTITY_H diff --git a/cpp/src/ProductUnit.h b/cpp/src/ProductUnit.h deleted file mode 100644 index 6277759..0000000 --- a/cpp/src/ProductUnit.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef CPP_PRODUCTUNIT_H -#define CPP_PRODUCTUNIT_H - - -enum class ProductUnit { - Kilo, - Each, -}; - - -#endif //CPP_PRODUCTUNIT_H diff --git a/cpp/src/Receipt.cpp b/cpp/src/Receipt.cpp deleted file mode 100644 index 7c2e5c3..0000000 --- a/cpp/src/Receipt.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "Receipt.h" - -std::vector Receipt::getItems() const { - return items; -} - -void Receipt::addDiscount(const Discount& discount) { - discounts.push_back(discount); -} - -void Receipt::addProduct(const Product& product, double quantity, double price, double totalPrice) { - items.push_back(ReceiptItem(product, quantity, price, totalPrice)); -} - -std::vector Receipt::getDiscounts() const { - return discounts; -} - -double Receipt::getTotalPrice() const { - double total = 0.0; - for (const auto& item : items) { - total += item.getTotalPrice(); - } - for (const auto& discount : discounts) { - total += discount.getDiscountAmount(); - } - return total; -} diff --git a/cpp/src/Receipt.h b/cpp/src/Receipt.h deleted file mode 100644 index dfd3410..0000000 --- a/cpp/src/Receipt.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef CPP_RECEIPT_H -#define CPP_RECEIPT_H - - -#include -#include "Discount.h" -#include "ReceiptItem.h" - -class Receipt { -public: - std::vector getItems() const; - - std::vector getDiscounts() const; - - double getTotalPrice() const; - - void addDiscount(const Discount& discount); - - void addProduct(const Product& product, double quantity, double price, double totalPrice); - -private: - std::vector items; - std::vector discounts; -}; - - -#endif //CPP_RECEIPT_H diff --git a/cpp/src/ReceiptItem.cpp b/cpp/src/ReceiptItem.cpp deleted file mode 100644 index 3dab218..0000000 --- a/cpp/src/ReceiptItem.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "ReceiptItem.h" - -ReceiptItem::ReceiptItem(const Product& product, double quantity, double price, double totalPrice) - : product(product), price(price), totalPrice(totalPrice), quantity(quantity) {} - -Product ReceiptItem::getProduct() const { - return product; -} - -double ReceiptItem::getPrice() const { - return price; -} - -double ReceiptItem::getTotalPrice() const { - return totalPrice; -} - -double ReceiptItem::getQuantity() const { - return quantity; -} - -bool ReceiptItem::operator==(const ReceiptItem& rhs) const { - return product == rhs.product && - price == rhs.price && - totalPrice == rhs.totalPrice && - quantity == rhs.quantity; -} - -bool ReceiptItem::operator!=(const ReceiptItem& rhs) const { - return !(rhs == *this); -} diff --git a/cpp/src/ReceiptItem.h b/cpp/src/ReceiptItem.h deleted file mode 100644 index 2f770c8..0000000 --- a/cpp/src/ReceiptItem.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef CPP_RECEIPTITEM_H -#define CPP_RECEIPTITEM_H - - -#include "Product.h" - -class ReceiptItem { -public: - ReceiptItem(const Product& product, double quantity, double price, double totalPrice); - Product getProduct() const; - - double getPrice() const; - - double getTotalPrice() const; - - double getQuantity() const; - - bool operator==(const ReceiptItem& rhs) const; - - bool operator!=(const ReceiptItem& rhs) const; - -private: - Product product; - double price; - double totalPrice; - double quantity; - -}; - - -#endif //CPP_RECEIPTITEM_H diff --git a/cpp/src/ReceiptPrinter.cpp b/cpp/src/ReceiptPrinter.cpp deleted file mode 100644 index 4d5812f..0000000 --- a/cpp/src/ReceiptPrinter.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "ReceiptPrinter.h" diff --git a/cpp/src/ReceiptPrinter.h b/cpp/src/ReceiptPrinter.h deleted file mode 100644 index 16c61e4..0000000 --- a/cpp/src/ReceiptPrinter.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef CPP_RECEIPTPRINTER_H -#define CPP_RECEIPTPRINTER_H - - -#include "Receipt.h" - -#include -#include - -class ReceiptPrinter -{ - -public: - ReceiptPrinter() : ReceiptPrinter(40) - { - } - - ReceiptPrinter(int columns) : columns(columns) - {} - - std::string printReceipt(const Receipt &receipt) - { - std::string result; - for (const auto &item : receipt.getItems()) - { - result.append(presentReceiptItem(item)); - } - for (const auto &discount : receipt.getDiscounts()) - { - result.append(presentDiscount(discount)); - } - result.append("\n"); - result.append(presentTotal(receipt)); - return result; - } - - std::string presentReceiptItem(const ReceiptItem &item) const - { - std::string price = getFormattedNumberAsString(item.getTotalPrice(), 2); - std::string name = item.getProduct().getName(); - - std::string line = formatLineWithWhitespace(name, price); - - if (item.getQuantity() != 1) - { - line += " " + getFormattedNumberAsString(item.getPrice(), 2) + " * " + presentQuantity(item) + "\n"; - } - return line; - } - - std::string presentDiscount(const Discount &discount) const - { - std::string name = discount.getDescription() + "(" + discount.getProduct().getName() + ")"; - std::string pricePresentation = getFormattedNumberAsString(discount.getDiscountAmount(), 2); - return formatLineWithWhitespace(name, pricePresentation); - } - - std::string presentTotal(const Receipt &receipt) const - { - std::string total = "Total: "; - std::string pricePresentation = presentPrice(receipt.getTotalPrice()); - return formatLineWithWhitespace(total, pricePresentation); - } - - std::string formatLineWithWhitespace(const std::string &name, const std::string &value) const - { - int whitespaceSize = columns - name.length() - value.length(); - std::string whitespace; - for (int i = 0; i < whitespaceSize; i++) - { - whitespace.append(" "); - } - return name + whitespace + value + "\n"; - } - - std::string presentPrice(double price) const - { return getFormattedNumberAsString(price, 2); } - - static std::string presentQuantity(const ReceiptItem &item) - { - return ProductUnit::Each == item.getProduct().getUnit() - ? getFormattedNumberAsString(item.getQuantity(), 0) - : getFormattedNumberAsString(item.getQuantity(), 3); - } - -private: - - static std::string getFormattedNumberAsString(double number, int precision) - { - std::stringstream stream; - stream << std::fixed << std::setprecision(precision) << number; - return stream.str(); - } - - const int columns; - - -}; - - -#endif //CPP_RECEIPTPRINTER_H diff --git a/cpp/src/ShoppingCart.cpp b/cpp/src/ShoppingCart.cpp deleted file mode 100644 index 3c3a523..0000000 --- a/cpp/src/ShoppingCart.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "ShoppingCart.h" - -void addItemQuantity(const Product& product, double quantity); - -std::vector ShoppingCart::getItems() const { - return items; -} - -std::map ShoppingCart::getProductQuantities() const { - return productQuantities; -} - -void ShoppingCart::addItem(const Product& product) { - addItemQuantity(product, 1.0); -} - -void ShoppingCart::addItemQuantity(const Product& product, double quantity) { - items.emplace_back(product, quantity); - if (productQuantities.find(product) != productQuantities.end()) { - productQuantities[product] += quantity; - } else { - productQuantities[product] = quantity; - } -} - -void ShoppingCart::handleOffers(Receipt& receipt, std::map offers, SupermarketCatalog* catalog) { - for (const auto& productQuantity : productQuantities) { - Product product = productQuantity.first; - double quantity = productQuantity.second; - if (offers.find(product) != offers.end()) { - auto offer = offers[product]; - double unitPrice = catalog->getUnitPrice(product); - int quantityAsInt = (int) quantity; - Discount* discount = nullptr; - int x = 1; - - if (offer.getOfferType() == SpecialOfferType::ThreeForTwo) { - x = 3; - } else if (offer.getOfferType() == SpecialOfferType::TwoForAmount) { - x = 2; - if (quantityAsInt >= 2) { - double total = offer.getArgument() * (quantityAsInt / x) + quantityAsInt % 2 * unitPrice; - double discountN = unitPrice * quantity - total; - discount = new Discount("2 for " + std::to_string(offer.getArgument()), -discountN, product); - } - } if (offer.getOfferType() == SpecialOfferType::FiveForAmount) { - x = 5; - } - int numberOfXs = quantityAsInt / x; - if (offer.getOfferType() == SpecialOfferType::ThreeForTwo && quantityAsInt > 2) { - double discountAmount = quantity * unitPrice - ((numberOfXs * 2 * unitPrice) + quantityAsInt % 3 * unitPrice); - discount = new Discount("3 for 2", -discountAmount, product); - } - if (offer.getOfferType() == SpecialOfferType::TenPercentDiscount) { - discount = new Discount(std::to_string(offer.getArgument()) + "% off", -quantity * unitPrice * offer.getArgument() / 100.0, product); - } - if (offer.getOfferType() == SpecialOfferType::FiveForAmount && quantityAsInt >= 5) { - double discountTotal = unitPrice * quantity - (offer.getArgument() * numberOfXs + quantityAsInt % 5 * unitPrice); - discount = new Discount(std::to_string(x) + " for " + std::to_string(offer.getArgument()), -discountTotal, product); - } - if (discount != nullptr) - receipt.addDiscount(*discount); - } - } -} \ No newline at end of file diff --git a/cpp/src/ShoppingCart.h b/cpp/src/ShoppingCart.h deleted file mode 100644 index 003335d..0000000 --- a/cpp/src/ShoppingCart.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef CPP_SHOPPINGCART_H -#define CPP_SHOPPINGCART_H - - -#include -#include -#include "ProductQuantity.h" -#include "Offer.h" -#include "Receipt.h" -#include "SupermarketCatalog.h" - -class ShoppingCart { -public: - std::vector getItems() const; - - std::map getProductQuantities() const; - - void addItem(const Product& product); - - void addItemQuantity(const Product& product, double quantity); - - void handleOffers(Receipt& receipt, std::map offers, SupermarketCatalog* catalog); - -private: - std::vector items; - std::map productQuantities; -}; - - -#endif //CPP_SHOPPINGCART_H diff --git a/cpp/src/SpecialOfferType.h b/cpp/src/SpecialOfferType.h deleted file mode 100644 index a1a38fa..0000000 --- a/cpp/src/SpecialOfferType.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef CPP_SPECIALOFFERTYPE_H -#define CPP_SPECIALOFFERTYPE_H - - -enum class SpecialOfferType { - ThreeForTwo, - TenPercentDiscount, - TwoForAmount, - FiveForAmount, -}; - - -#endif //CPP_SPECIALOFFERTYPE_H diff --git a/cpp/src/SupermarketCatalog.h b/cpp/src/SupermarketCatalog.h deleted file mode 100644 index 21c3922..0000000 --- a/cpp/src/SupermarketCatalog.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef CPP_SUPERMARKETCATALOG_H -#define CPP_SUPERMARKETCATALOG_H - - -#include "Product.h" - -class SupermarketCatalog { -public: - virtual void addProduct(const Product& product, double price) = 0; - virtual double getUnitPrice(const Product& product) = 0; -}; - - -#endif //CPP_SUPERMARKETCATALOG_H diff --git a/cpp/src/Teller.cpp b/cpp/src/Teller.cpp deleted file mode 100644 index 25e51de..0000000 --- a/cpp/src/Teller.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "Teller.h" - -Teller::Teller(SupermarketCatalog *catalog) : catalog(catalog) {} - -void Teller::addSpecialOffer(SpecialOfferType offerType, const Product& product, double argument) { - offers[product] = Offer(offerType, product, argument); -} - -Receipt Teller::checksOutArticlesFrom(ShoppingCart theCart) { - Receipt receipt{}; - std::vector productQuantities = theCart.getItems(); - for (const auto& pq: productQuantities) { - Product p = pq.getProduct(); - double quantity = pq.getQuantity(); - double unitPrice = catalog->getUnitPrice(p); - double price = quantity * unitPrice; - receipt.addProduct(p, quantity, unitPrice, price); - } - theCart.handleOffers(receipt, offers, catalog); - - return receipt; -} diff --git a/cpp/src/Teller.h b/cpp/src/Teller.h deleted file mode 100644 index eb5c1ea..0000000 --- a/cpp/src/Teller.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef CPP_TELLER_H -#define CPP_TELLER_H - - -#include -#include "SupermarketCatalog.h" -#include "Offer.h" -#include "Receipt.h" -#include "ShoppingCart.h" - -class Teller { -public: - Teller(SupermarketCatalog* catalog); - - void addSpecialOffer(SpecialOfferType offerType, const Product& product, double argument); - - Receipt checksOutArticlesFrom(ShoppingCart theCart); - -private: - SupermarketCatalog* catalog; - std::map offers; -}; - - -#endif //CPP_TELLER_H diff --git a/cpp/src/sample.cpp b/cpp/src/sample.cpp deleted file mode 100644 index 6e2f761..0000000 --- a/cpp/src/sample.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "sample.h" - -// write your code here - diff --git a/cpp/src/sample.h b/cpp/src/sample.h deleted file mode 100644 index 84fbee0..0000000 --- a/cpp/src/sample.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef SAMPLE_H -#define SAMPLE_H - -// write your code here - -#endif //SAMPLE_H diff --git a/cpp/test-catch2/CMakeLists.txt b/cpp/test-catch2/CMakeLists.txt deleted file mode 100644 index f88e07d..0000000 --- a/cpp/test-catch2/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -set(TEST_NAME sample_test_catch2) -include(FetchContent) -set(SOURCE_FILES main.cpp sample_catch.cpp) - -# catch2 -FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v2.13.10) -FetchContent_MakeAvailable(Catch2) - -# approvals -FetchContent_Declare( - ApprovalTests - GIT_REPOSITORY https://github.com/approvals/ApprovalTests.cpp - GIT_TAG v.10.12.2) -FetchContent_MakeAvailable(ApprovalTests) - -add_executable(${TEST_NAME}) -target_sources(${TEST_NAME} PRIVATE ${SOURCE_FILES}) -target_link_libraries(${TEST_NAME} src Catch2::Catch2 ApprovalTests::ApprovalTests) -add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) diff --git a/cpp/test-catch2/SuperMarketTest.cpp b/cpp/test-catch2/SuperMarketTest.cpp deleted file mode 100644 index ae23179..0000000 --- a/cpp/test-catch2/SuperMarketTest.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "ApprovalTests.hpp" -#include "catch2/catch.hpp" - -#include "SupermarketCatalog.h" -#include "FakeCatalog.h" -#include "ShoppingCart.h" -#include "Teller.h" - -TEST_CASE("TenPercentDiscount", "[Supermarket]") -{ - // ARRANGE - SupermarketCatalog *catalog = new FakeCatalog(); - Product toothbrush("toothbrush", ProductUnit::Each); - catalog->addProduct(toothbrush, 0.99); - Product apples("apples", ProductUnit::Kilo); - catalog->addProduct(apples, 1.99); - Teller teller(catalog); - teller.addSpecialOffer(SpecialOfferType::TenPercentDiscount, toothbrush, 10.0); - - ShoppingCart cart; - cart.addItemQuantity(apples, 2.5); - - // ACT - Receipt receipt = teller.checksOutArticlesFrom(cart); - - // ASSERT - REQUIRE(4.975 == receipt.getTotalPrice()); - REQUIRE(receipt.getDiscounts().empty()); - REQUIRE(1 == receipt.getItems().size()); - ReceiptItem receiptItem = receipt.getItems()[0]; - REQUIRE(apples == receiptItem.getProduct()); - REQUIRE(1.99 == receiptItem.getPrice()); - REQUIRE(2.5 * 1.99 == receiptItem.getTotalPrice()); - REQUIRE(2.5 == receiptItem.getQuantity()); - -} - - - diff --git a/cpp/test-catch2/main.cpp b/cpp/test-catch2/main.cpp deleted file mode 100644 index 5cec3a1..0000000 --- a/cpp/test-catch2/main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#define APPROVALS_CATCH -#include "ApprovalTests.hpp" - -#include -auto defaultReporterDisposer = - ApprovalTests::Approvals::useAsDefaultReporter(std::make_shared()); diff --git a/cpp/test-catch2/sample_catch.cpp b/cpp/test-catch2/sample_catch.cpp deleted file mode 100644 index 2e61ede..0000000 --- a/cpp/test-catch2/sample_catch.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "ApprovalTests.hpp" -#include "catch2/catch.hpp" - - -#include "SupermarketCatalog.h" -#include "FakeCatalog.h" -#include "ShoppingCart.h" -#include "Teller.h" - -TEST_CASE("TenPercentDiscount", "[Supermarket]") -{ -// ARRANGE -SupermarketCatalog *catalog = new FakeCatalog(); -Product toothbrush("toothbrush", ProductUnit::Each); -catalog->addProduct(toothbrush, 0.99); -Product apples("apples", ProductUnit::Kilo); -catalog->addProduct(apples, 1.99); -Teller teller(catalog); -teller.addSpecialOffer(SpecialOfferType::TenPercentDiscount, toothbrush, 10.0); - -ShoppingCart cart; -cart.addItemQuantity(apples, 2.5); - -// ACT -Receipt receipt = teller.checksOutArticlesFrom(cart); - -// ASSERT -REQUIRE(4.975 == receipt.getTotalPrice()); -REQUIRE(receipt.getDiscounts().empty()); -REQUIRE(1 == receipt.getItems().size()); -ReceiptItem receiptItem = receipt.getItems()[0]; -REQUIRE(apples == receiptItem.getProduct()); -REQUIRE(1.99 == receiptItem.getPrice()); -REQUIRE(2.5 * 1.99 == receiptItem.getTotalPrice()); -REQUIRE(2.5 == receiptItem.getQuantity()); - -} - - - - - diff --git a/cpp/test-doctest/CMakeLists.txt b/cpp/test-doctest/CMakeLists.txt deleted file mode 100644 index 21ccf0c..0000000 --- a/cpp/test-doctest/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -set(TEST_NAME sample_test_doctest) -include(FetchContent) -set(SOURCE_FILES main.cpp sample_doctest.cpp) - -# Doctest -FetchContent_Declare( - DocTest - GIT_REPOSITORY https://github.com/doctest/doctest.git - GIT_TAG v2.4.9) -FetchContent_MakeAvailable(DocTest) - -# approvals -FetchContent_Declare( - ApprovalTests - GIT_REPOSITORY https://github.com/approvals/ApprovalTests.cpp - GIT_TAG v.10.12.2) -FetchContent_MakeAvailable(ApprovalTests) - -add_executable(${TEST_NAME}) -target_sources(${TEST_NAME} PRIVATE ${SOURCE_FILES}) -target_link_libraries(${TEST_NAME} src doctest ApprovalTests::ApprovalTests) -add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) diff --git a/cpp/test-doctest/main.cpp b/cpp/test-doctest/main.cpp deleted file mode 100644 index 5cf108b..0000000 --- a/cpp/test-doctest/main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#define APPROVALS_DOCTEST -#include "ApprovalTests.hpp" - -#include -auto defaultReporterDisposer = - ApprovalTests::Approvals::useAsDefaultReporter(std::make_shared()); diff --git a/cpp/test-doctest/sample_doctest.cpp b/cpp/test-doctest/sample_doctest.cpp deleted file mode 100644 index e0612e2..0000000 --- a/cpp/test-doctest/sample_doctest.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "ApprovalTests.hpp" -#include "doctest/doctest.h" - -#include "SupermarketCatalog.h" -#include "FakeCatalog.h" -#include "ShoppingCart.h" -#include "Teller.h" - -TEST_CASE("TenPercentDiscount") -{ -// ARRANGE - SupermarketCatalog *catalog = new FakeCatalog(); - Product toothbrush("toothbrush", ProductUnit::Each); - catalog->addProduct(toothbrush, 0.99); - Product apples("apples", ProductUnit::Kilo); - catalog->addProduct(apples, 1.99); - Teller teller(catalog); - teller.addSpecialOffer(SpecialOfferType::TenPercentDiscount, toothbrush, 10.0); - - ShoppingCart cart; - cart.addItemQuantity(apples, 2.5); - -// ACT - Receipt receipt = teller.checksOutArticlesFrom(cart); - -// ASSERT - REQUIRE(4.975 == receipt.getTotalPrice()); - REQUIRE(receipt.getDiscounts().empty()); - REQUIRE(1 == receipt.getItems().size()); - ReceiptItem receiptItem = receipt.getItems()[0]; - REQUIRE(apples == receiptItem.getProduct()); - REQUIRE(1.99 == receiptItem.getPrice()); - REQUIRE(2.5 * 1.99 == receiptItem.getTotalPrice()); - REQUIRE(2.5 == receiptItem.getQuantity()); - -} - - diff --git a/cpp/test-gtest/CMakeLists.txt b/cpp/test-gtest/CMakeLists.txt deleted file mode 100644 index 7e4642a..0000000 --- a/cpp/test-gtest/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -set(TEST_NAME sample_test_gtest) -include(FetchContent) -set(SOURCE_FILES main.cpp sample_gtest.cpp) - -# approvals -FetchContent_Declare( - ApprovalTests - GIT_REPOSITORY https://github.com/approvals/ApprovalTests.cpp - GIT_TAG v.10.12.2) -FetchContent_MakeAvailable(ApprovalTests) - -# GoogleTest -FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.13.0 -) -FetchContent_MakeAvailable(googletest) - -add_executable(${TEST_NAME}) -target_sources(${TEST_NAME} PRIVATE ${SOURCE_FILES}) -target_link_libraries(${TEST_NAME} src gtest gmock ApprovalTests::ApprovalTests) -add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) diff --git a/cpp/test-gtest/main.cpp b/cpp/test-gtest/main.cpp deleted file mode 100644 index 4ccbe0a..0000000 --- a/cpp/test-gtest/main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#define APPROVALS_GOOGLETEST -#include "ApprovalTests.hpp" - -#include -auto defaultReporterDisposer = - ApprovalTests::Approvals::useAsDefaultReporter(std::make_shared()); diff --git a/cpp/test-gtest/sample_gtest.cpp b/cpp/test-gtest/sample_gtest.cpp deleted file mode 100644 index 330d8f7..0000000 --- a/cpp/test-gtest/sample_gtest.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include - -#include "SupermarketCatalog.h" -#include "FakeCatalog.h" -#include "ShoppingCart.h" -#include "Teller.h" -#include "ReceiptPrinter.h" - -using namespace std; - -TEST(SupermarketTest, TenPercentDiscount) { - // ARRANGE - SupermarketCatalog *catalog = new FakeCatalog(); - Product toothbrush("toothbrush", ProductUnit::Each); - catalog->addProduct(toothbrush, 0.99); - Product apples("apples", ProductUnit::Kilo); - catalog->addProduct(apples, 1.99); - Teller teller(catalog); - teller.addSpecialOffer(SpecialOfferType::TenPercentDiscount, toothbrush, 10.0); - - ShoppingCart cart; - cart.addItemQuantity(apples, 2.5); - - // ACT - Receipt receipt = teller.checksOutArticlesFrom(cart); - - // ASSERT - ASSERT_EQ(4.975, receipt.getTotalPrice()); - ASSERT_TRUE(receipt.getDiscounts().empty()); - ASSERT_EQ(1, receipt.getItems().size()); - ReceiptItem receiptItem = receipt.getItems()[0]; - ASSERT_EQ(apples, receiptItem.getProduct()); - ASSERT_EQ(1.99, receiptItem.getPrice()); - ASSERT_EQ(2.5 * 1.99, receiptItem.getTotalPrice()); - ASSERT_EQ(2.5, receiptItem.getQuantity()); - -} \ No newline at end of file diff --git a/csharp-ms/.gitignore b/csharp-ms/.gitignore deleted file mode 100644 index a3325fb..0000000 --- a/csharp-ms/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.vs -bin -obj -packages -*.sln.DotSettings.user diff --git a/csharp-ms/SupermarketReceipt.Test/SupermarketReceipt.Test.csproj b/csharp-ms/SupermarketReceipt.Test/SupermarketReceipt.Test.csproj deleted file mode 100644 index ad21c3d..0000000 --- a/csharp-ms/SupermarketReceipt.Test/SupermarketReceipt.Test.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - net5.0 - - false - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - diff --git a/csharp-ms/SupermarketReceipt.Test/SupermarketTest.cs b/csharp-ms/SupermarketReceipt.Test/SupermarketTest.cs deleted file mode 100644 index afca625..0000000 --- a/csharp-ms/SupermarketReceipt.Test/SupermarketTest.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using SupermarketReceipt; -using Xunit; - -namespace Supermarket.Test -{ - public class SupermarketTest - { - - [Fact] - public void TenPercentDiscount() - { - // ARRANGE - SupermarketCatalog catalog = new FakeCatalog(); - var toothbrush = new Product("toothbrush", ProductUnit.Each); - catalog.AddProduct(toothbrush, 0.99); - var apples = new Product("apples", ProductUnit.Kilo); - catalog.AddProduct(apples, 1.99); - - var cart = new ShoppingCart(); - cart.AddItemQuantity(apples, 2.5); - - var teller = new Teller(catalog); - teller.AddSpecialOffer(SpecialOfferType.TenPercentDiscount, toothbrush, 10.0); - - // ACT - var receipt = teller.ChecksOutArticlesFrom(cart); - - // ASSERT - Assert.Equal(4.975, receipt.GetTotalPrice()); - Assert.Equal(new List(), receipt.GetDiscounts()); - Assert.Single(receipt.GetItems()); - var receiptItem = receipt.GetItems()[0]; - Assert.Equal(apples, receiptItem.Product); - Assert.Equal(1.99, receiptItem.Price); - Assert.Equal(2.5 * 1.99, receiptItem.TotalPrice); - Assert.Equal(2.5, receiptItem.Quantity); - } - } -} \ No newline at end of file diff --git a/csharp-ms/SupermarketReceipt.sln b/csharp-ms/SupermarketReceipt.sln deleted file mode 100644 index 59db09a..0000000 --- a/csharp-ms/SupermarketReceipt.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29326.143 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SupermarketReceipt", "SupermarketReceipt\SupermarketReceipt.csproj", "{B384010A-E2A8-4720-A622-DC49A1CC76D4}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SupermarketReceipt.Test", "SupermarketReceipt.Test\SupermarketReceipt.Test.csproj", "{B8789108-5351-4785-836A-6E00C6AF3596}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B384010A-E2A8-4720-A622-DC49A1CC76D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B384010A-E2A8-4720-A622-DC49A1CC76D4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B384010A-E2A8-4720-A622-DC49A1CC76D4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B384010A-E2A8-4720-A622-DC49A1CC76D4}.Release|Any CPU.Build.0 = Release|Any CPU - {B8789108-5351-4785-836A-6E00C6AF3596}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B8789108-5351-4785-836A-6E00C6AF3596}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B8789108-5351-4785-836A-6E00C6AF3596}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B8789108-5351-4785-836A-6E00C6AF3596}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {9F0B1464-4A07-42A3-A0C1-2D5A51311E8F} - EndGlobalSection -EndGlobal diff --git a/csharp-ms/SupermarketReceipt/Discount.cs b/csharp-ms/SupermarketReceipt/Discount.cs deleted file mode 100644 index 8abcca9..0000000 --- a/csharp-ms/SupermarketReceipt/Discount.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace SupermarketReceipt -{ - public class Discount - { - public Discount(Product product, string description, double discountAmount) - { - Product = product; - Description = description; - DiscountAmount = discountAmount; - } - - public string Description { get; } - public double DiscountAmount { get; } - public Product Product { get; } - } -} \ No newline at end of file diff --git a/csharp-ms/SupermarketReceipt/FakeCatalog.cs b/csharp-ms/SupermarketReceipt/FakeCatalog.cs deleted file mode 100644 index 2a35d3f..0000000 --- a/csharp-ms/SupermarketReceipt/FakeCatalog.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; - -namespace SupermarketReceipt -{ - public class FakeCatalog : SupermarketCatalog - { - private readonly IDictionary _prices = new Dictionary(); - private readonly IDictionary _products = new Dictionary(); - - public void AddProduct(Product product, double price) - { - _products.Add(product.Name, product); - _prices.Add(product.Name, price); - } - - public double GetUnitPrice(Product p) - { - return _prices[p.Name]; - } - } -} \ No newline at end of file diff --git a/csharp-ms/SupermarketReceipt/Offer.cs b/csharp-ms/SupermarketReceipt/Offer.cs deleted file mode 100644 index 2ecdfdb..0000000 --- a/csharp-ms/SupermarketReceipt/Offer.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace SupermarketReceipt -{ - public enum SpecialOfferType - { - ThreeForTwo, - TenPercentDiscount, - TwoForAmount, - FiveForAmount - } - - public class Offer - { - private Product _product; - - public Offer(SpecialOfferType offerType, Product product, double argument) - { - OfferType = offerType; - Argument = argument; - _product = product; - } - - public SpecialOfferType OfferType { get; } - public double Argument { get; } - } -} \ No newline at end of file diff --git a/csharp-ms/SupermarketReceipt/Product.cs b/csharp-ms/SupermarketReceipt/Product.cs deleted file mode 100644 index fb7a673..0000000 --- a/csharp-ms/SupermarketReceipt/Product.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Collections.Generic; - -namespace SupermarketReceipt -{ - public class Product - { - public Product(string name, ProductUnit unit) - { - Name = name; - Unit = unit; - } - - public string Name { get; } - public ProductUnit Unit { get; } - - public override bool Equals(object obj) - { - var product = obj as Product; - return product != null && - Name == product.Name && - Unit == product.Unit; - } - - public override int GetHashCode() - { - var hashCode = -1996304355; - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Name); - hashCode = hashCode * -1521134295 + Unit.GetHashCode(); - return hashCode; - } - } - - public class ProductQuantity - { - public ProductQuantity(Product product, double weight) - { - Product = product; - Quantity = weight; - } - - public Product Product { get; } - public double Quantity { get; } - } - - public enum ProductUnit - { - Kilo, - Each - } -} \ No newline at end of file diff --git a/csharp-ms/SupermarketReceipt/Receipt.cs b/csharp-ms/SupermarketReceipt/Receipt.cs deleted file mode 100644 index 6b4b38e..0000000 --- a/csharp-ms/SupermarketReceipt/Receipt.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Collections.Generic; - -namespace SupermarketReceipt -{ - public class Receipt - { - private readonly List _discounts = new List(); - private readonly List _items = new List(); - - public double GetTotalPrice() - { - var total = 0.0; - foreach (var item in _items) total += item.TotalPrice; - foreach (var discount in _discounts) total += discount.DiscountAmount; - return total; - } - - public void AddProduct(Product p, double quantity, double price, double totalPrice) - { - _items.Add(new ReceiptItem(p, quantity, price, totalPrice)); - } - - public List GetItems() - { - return new List(_items); - } - - public void AddDiscount(Discount discount) - { - _discounts.Add(discount); - } - - public List GetDiscounts() - { - return _discounts; - } - } - - public class ReceiptItem - { - public ReceiptItem(Product p, double quantity, double price, double totalPrice) - { - Product = p; - Quantity = quantity; - Price = price; - TotalPrice = totalPrice; - } - - public Product Product { get; } - public double Price { get; } - public double TotalPrice { get; } - public double Quantity { get; } - } -} \ No newline at end of file diff --git a/csharp-ms/SupermarketReceipt/ReceiptPrinter.cs b/csharp-ms/SupermarketReceipt/ReceiptPrinter.cs deleted file mode 100644 index 954f9d7..0000000 --- a/csharp-ms/SupermarketReceipt/ReceiptPrinter.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System.Globalization; -using System.Text; - -namespace SupermarketReceipt -{ - public class ReceiptPrinter - { - private static readonly CultureInfo Culture = CultureInfo.CreateSpecificCulture("en-GB"); - - private readonly int _columns; - - - public ReceiptPrinter(int columns) - { - _columns = columns; - } - - public ReceiptPrinter() : this(40) - { - } - - public string PrintReceipt(Receipt receipt) - { - var result = new StringBuilder(); - foreach (var item in receipt.GetItems()) - { - string receiptItem = PrintReceiptItem(item); - result.Append(receiptItem); - - } - - foreach (var discount in receipt.GetDiscounts()) - { - string discountPresentation = PrintDiscount(discount); - result.Append(discountPresentation); - } - - { - result.Append("\n"); - result.Append(PrintTotal(receipt)); - } - return result.ToString(); - } - - private string PrintTotal(Receipt receipt) - { - string name = "Total: "; - string value = PrintPrice(receipt.GetTotalPrice()); - return FormatLineWithWhitespace(name, value); - } - - private string PrintDiscount(Discount discount) - { - string name = discount.Description + "(" + discount.Product.Name + ")"; - string value = PrintPrice(discount.DiscountAmount); - - return FormatLineWithWhitespace(name, value); - } - - private string PrintReceiptItem(ReceiptItem item) - { - string totalPrice = PrintPrice(item.TotalPrice); - string name = item.Product.Name; - string line = FormatLineWithWhitespace(name, totalPrice); - if (item.Quantity != 1) - { - line += " " + PrintPrice(item.Price) + " * " + PrintQuantity(item) + "\n"; - } - - return line; - } - - - private string FormatLineWithWhitespace(string name, string value) - { - var line = new StringBuilder(); - line.Append(name); - int whitespaceSize = this._columns - name.Length - value.Length; - for (int i = 0; i < whitespaceSize; i++) { - line.Append(" "); - } - line.Append(value); - line.Append('\n'); - return line.ToString(); - } - - private string PrintPrice(double price) - { - return price.ToString("N2", Culture); - } - - private static string PrintQuantity(ReceiptItem item) - { - return ProductUnit.Each == item.Product.Unit - ? ((int) item.Quantity).ToString() - : item.Quantity.ToString("N3", Culture); - } - - } -} \ No newline at end of file diff --git a/csharp-ms/SupermarketReceipt/ShoppingCart.cs b/csharp-ms/SupermarketReceipt/ShoppingCart.cs deleted file mode 100644 index 28abbe7..0000000 --- a/csharp-ms/SupermarketReceipt/ShoppingCart.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.Collections.Generic; - -namespace SupermarketReceipt -{ - public class ShoppingCart - { - private readonly List _items = new List(); - private readonly Dictionary _productQuantities = new Dictionary(); - - - public List GetItems() - { - return new List(_items); - } - - public void AddItem(Product product) - { - AddItemQuantity(product, 1.0); - } - - - public void AddItemQuantity(Product product, double quantity) - { - _items.Add(new ProductQuantity(product, quantity)); - if (_productQuantities.ContainsKey(product)) - { - var newAmount = _productQuantities[product] + quantity; - _productQuantities[product] = newAmount; - } - else - { - _productQuantities.Add(product, quantity); - } - } - - public void HandleOffers(Receipt receipt, Dictionary offers, SupermarketCatalog catalog) - { - foreach (var p in _productQuantities.Keys) - { - var quantity = _productQuantities[p]; - var quantityAsInt = (int) quantity; - if (offers.ContainsKey(p)) - { - var offer = offers[p]; - var unitPrice = catalog.GetUnitPrice(p); - Discount discount = null; - var x = 1; - if (offer.OfferType == SpecialOfferType.ThreeForTwo) - { - x = 3; - } - else if (offer.OfferType == SpecialOfferType.TwoForAmount) - { - x = 2; - if (quantityAsInt >= 2) - { - var total = offer.Argument * (quantityAsInt / x) + quantityAsInt % 2 * unitPrice; - var discountN = unitPrice * quantity - total; - discount = new Discount(p, "2 for " + offer.Argument, -discountN); - } - } - - if (offer.OfferType == SpecialOfferType.FiveForAmount) x = 5; - var numberOfXs = quantityAsInt / x; - if (offer.OfferType == SpecialOfferType.ThreeForTwo && quantityAsInt > 2) - { - var discountAmount = quantity * unitPrice - (numberOfXs * 2 * unitPrice + quantityAsInt % 3 * unitPrice); - discount = new Discount(p, "3 for 2", -discountAmount); - } - - if (offer.OfferType == SpecialOfferType.TenPercentDiscount) discount = new Discount(p, offer.Argument + "% off", -quantity * unitPrice * offer.Argument / 100.0); - if (offer.OfferType == SpecialOfferType.FiveForAmount && quantityAsInt >= 5) - { - var discountTotal = unitPrice * quantity - (offer.Argument * numberOfXs + quantityAsInt % 5 * unitPrice); - discount = new Discount(p, x + " for " + offer.Argument, -discountTotal); - } - - if (discount != null) - receipt.AddDiscount(discount); - } - } - } - } -} \ No newline at end of file diff --git a/csharp-ms/SupermarketReceipt/SupermarketCatalog.cs b/csharp-ms/SupermarketReceipt/SupermarketCatalog.cs deleted file mode 100644 index 054b08a..0000000 --- a/csharp-ms/SupermarketReceipt/SupermarketCatalog.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SupermarketReceipt -{ - public interface SupermarketCatalog - { - void AddProduct(Product product, double price); - - double GetUnitPrice(Product product); - } -} \ No newline at end of file diff --git a/csharp-ms/SupermarketReceipt/SupermarketReceipt.csproj b/csharp-ms/SupermarketReceipt/SupermarketReceipt.csproj deleted file mode 100644 index 9f5c4f4..0000000 --- a/csharp-ms/SupermarketReceipt/SupermarketReceipt.csproj +++ /dev/null @@ -1,7 +0,0 @@ - - - - netstandard2.0 - - - diff --git a/csharp-ms/SupermarketReceipt/Teller.cs b/csharp-ms/SupermarketReceipt/Teller.cs deleted file mode 100644 index d6b313e..0000000 --- a/csharp-ms/SupermarketReceipt/Teller.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; - -namespace SupermarketReceipt -{ - public class Teller - { - private readonly SupermarketCatalog _catalog; - private readonly Dictionary _offers = new Dictionary(); - - public Teller(SupermarketCatalog catalog) - { - _catalog = catalog; - } - - public void AddSpecialOffer(SpecialOfferType offerType, Product product, double argument) - { - _offers[product] = new Offer(offerType, product, argument); - } - - public Receipt ChecksOutArticlesFrom(ShoppingCart theCart) - { - var receipt = new Receipt(); - var productQuantities = theCart.GetItems(); - foreach (var pq in productQuantities) - { - var p = pq.Product; - var quantity = pq.Quantity; - var unitPrice = _catalog.GetUnitPrice(p); - var price = quantity * unitPrice; - receipt.AddProduct(p, quantity, unitPrice, price); - } - - theCart.HandleOffers(receipt, _offers, _catalog); - - return receipt; - } - } -} \ No newline at end of file diff --git a/python/tests/__init__.py b/csharp/SupermarketReceipt.Tests/DiscountCalculationTests.cs similarity index 100% rename from python/tests/__init__.py rename to csharp/SupermarketReceipt.Tests/DiscountCalculationTests.cs diff --git a/python_pytest/tests/__init__.py b/csharp/SupermarketReceipt.Tests/ProductCatalogTests.cs similarity index 100% rename from python_pytest/tests/__init__.py rename to csharp/SupermarketReceipt.Tests/ProductCatalogTests.cs diff --git a/csharp/SupermarketReceipt.Tests/ReceiptGenerationTests.cs b/csharp/SupermarketReceipt.Tests/ReceiptGenerationTests.cs new file mode 100644 index 0000000..e69de29 diff --git a/csharp/SupermarketReceipt.Tests/ShoppingCartTests.cs b/csharp/SupermarketReceipt.Tests/ShoppingCartTests.cs new file mode 100644 index 0000000..66da499 --- /dev/null +++ b/csharp/SupermarketReceipt.Tests/ShoppingCartTests.cs @@ -0,0 +1,182 @@ +using FluentAssertions; +using SupermarketReceipt; + +namespace SupermarketReceipt.Tests; + +public class ShoppingCartTests +{ + private readonly FakeCatalog _catalog; + private readonly Product _toothbrush; + private readonly Product _apples; + + public ShoppingCartTests() + { + _catalog = new FakeCatalog(); + _toothbrush = new Product("Toothbrush", ProductUnit.Each); + _apples = new Product("Apples", ProductUnit.Kilo); + _catalog.AddProduct(_toothbrush, 1.50); + _catalog.AddProduct(_apples, 2.00); + } + + [Fact] + public void AddItem_ShouldAddSingleItemWithQuantityOne() + { + // Arrange + var cart = new ShoppingCart(); + + // Act + cart.AddItem(_toothbrush); + + // Assert + var items = cart.GetItems(); + items.Should().HaveCount(1); + items[0].Product.Should().Be(_toothbrush); + items[0].Quantity.Should().Be(1.0); + } + + [Fact] + public void AddItemQuantity_ShouldAddItemWithSpecificQuantity() + { + // Arrange + var cart = new ShoppingCart(); + + // Act + cart.AddItemQuantity(_apples, 2.5); + + // Assert + var items = cart.GetItems(); + items.Should().HaveCount(1); + items[0].Product.Should().Be(_apples); + items[0].Quantity.Should().Be(2.5); + } + + [Fact] + public void AddItem_MultipleTimes_ShouldTrackAllQuantities() + { + // Arrange + var cart = new ShoppingCart(); + + // Act + cart.AddItem(_toothbrush); + cart.AddItem(_toothbrush); + cart.AddItemQuantity(_toothbrush, 3.0); + + // Assert + var items = cart.GetItems(); + items.Should().HaveCount(3); + items.Sum(i => i.Quantity).Should().Be(5.0); + } + + [Fact] + public void HandleOffers_ThreeForTwo_ShouldApplyDiscountForThreeItems() + { + // Arrange + var cart = new ShoppingCart(); + cart.AddItemQuantity(_toothbrush, 3); + + var teller = new Teller(_catalog); + teller.AddSpecialOffer(SpecialOfferType.ThreeForTwo, _toothbrush, 0); + + // Act + var receipt = teller.ChecksOutArticlesFrom(cart); + + // Assert + receipt.GetTotalPrice().Should().Be(3.00); // 3 items at 1.50 - discount of 1.50 + receipt.GetDiscounts().Should().HaveCount(1); + receipt.GetDiscounts()[0].Description.Should().Be("3 for 2"); + receipt.GetDiscounts()[0].DiscountAmount.Should().Be(-1.50); + } + + [Fact] + public void HandleOffers_ThreeForTwo_ShouldNotApplyDiscountForLessThanThree() + { + // Arrange + var cart = new ShoppingCart(); + cart.AddItemQuantity(_toothbrush, 2); + + var teller = new Teller(_catalog); + teller.AddSpecialOffer(SpecialOfferType.ThreeForTwo, _toothbrush, 0); + + // Act + var receipt = teller.ChecksOutArticlesFrom(cart); + + // Assert + receipt.GetTotalPrice().Should().Be(3.00); // 2 items at 1.50 + receipt.GetDiscounts().Should().BeEmpty(); + } + + [Fact] + public void HandleOffers_TwoForAmount_ShouldApplyDiscountForTwoItems() + { + // Arrange + var cart = new ShoppingCart(); + cart.AddItemQuantity(_toothbrush, 2); + + var teller = new Teller(_catalog); + teller.AddSpecialOffer(SpecialOfferType.TwoForAmount, _toothbrush, 2.50); + + // Act + var receipt = teller.ChecksOutArticlesFrom(cart); + + // Assert + receipt.GetTotalPrice().Should().Be(2.50); // Special price 2 for 2.50 + receipt.GetDiscounts().Should().HaveCount(1); + receipt.GetDiscounts()[0].DiscountAmount.Should().Be(-0.50); + } + + [Fact] + public void HandleOffers_FiveForAmount_ShouldApplyDiscountForFiveItems() + { + // Arrange + var cart = new ShoppingCart(); + cart.AddItemQuantity(_toothbrush, 5); + + var teller = new Teller(_catalog); + teller.AddSpecialOffer(SpecialOfferType.FiveForAmount, _toothbrush, 6.00); + + // Act + var receipt = teller.ChecksOutArticlesFrom(cart); + + // Assert + receipt.GetTotalPrice().Should().Be(6.00); // 5 for 6.00 + receipt.GetDiscounts().Should().HaveCount(1); + receipt.GetDiscounts()[0].DiscountAmount.Should().Be(-1.50); + } + + [Fact] + public void HandleOffers_TenPercentDiscount_ShouldApplyPercentageDiscount() + { + // Arrange + var cart = new ShoppingCart(); + cart.AddItemQuantity(_apples, 2.0); + + var teller = new Teller(_catalog); + teller.AddSpecialOffer(SpecialOfferType.TenPercentDiscount, _apples, 10.0); + + // Act + var receipt = teller.ChecksOutArticlesFrom(cart); + + // Assert + receipt.GetTotalPrice().Should().Be(3.60); // 4.00 - 10% = 3.60 + receipt.GetDiscounts().Should().HaveCount(1); + receipt.GetDiscounts()[0].Description.Should().Be("10% off"); + receipt.GetDiscounts()[0].DiscountAmount.Should().Be(-0.40); + } + + [Fact] + public void HandleOffers_NoOffer_ShouldNotApplyDiscount() + { + // Arrange + var cart = new ShoppingCart(); + cart.AddItemQuantity(_toothbrush, 5); + + var teller = new Teller(_catalog); + + // Act + var receipt = teller.ChecksOutArticlesFrom(cart); + + // Assert + receipt.GetTotalPrice().Should().Be(7.50); // 5 items at 1.50 + receipt.GetDiscounts().Should().BeEmpty(); + } +} diff --git a/csharp/SupermarketReceipt.Tests/SupermarketReceipt.Tests.csproj b/csharp/SupermarketReceipt.Tests/SupermarketReceipt.Tests.csproj new file mode 100644 index 0000000..ba8652c --- /dev/null +++ b/csharp/SupermarketReceipt.Tests/SupermarketReceipt.Tests.csproj @@ -0,0 +1,25 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + diff --git a/csharp/SupermarketReceipt.Tests/UnitTest1.cs b/csharp/SupermarketReceipt.Tests/UnitTest1.cs new file mode 100644 index 0000000..207f073 --- /dev/null +++ b/csharp/SupermarketReceipt.Tests/UnitTest1.cs @@ -0,0 +1,10 @@ +namespace SupermarketReceipt.Tests; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + + } +} \ No newline at end of file diff --git a/csharp/SupermarketReceipt/ShoppingCart.cs b/csharp/SupermarketReceipt/ShoppingCart.cs index 72b6b4a..ff2306e 100644 --- a/csharp/SupermarketReceipt/ShoppingCart.cs +++ b/csharp/SupermarketReceipt/ShoppingCart.cs @@ -37,50 +37,89 @@ public void AddItemQuantity(Product product, double quantity) public void HandleOffers(Receipt receipt, Dictionary offers, SupermarketCatalog catalog) { - foreach (var p in _productQuantities.Keys) + foreach (var product in _productQuantities.Keys) { - var quantity = _productQuantities[p]; - var quantityAsInt = (int) quantity; - if (offers.ContainsKey(p)) - { - var offer = offers[p]; - var unitPrice = catalog.GetUnitPrice(p); - Discount discount = null; - var x = 1; - if (offer.OfferType == SpecialOfferType.ThreeForTwo) - { - x = 3; - } - else if (offer.OfferType == SpecialOfferType.TwoForAmount) - { - x = 2; - if (quantityAsInt >= 2) - { - var total = offer.Argument * (quantityAsInt / x) + quantityAsInt % 2 * unitPrice; - var discountN = unitPrice * quantity - total; - discount = new Discount(p, "2 for " + PrintPrice(offer.Argument), -discountN); - } - } + if (!offers.ContainsKey(product)) + continue; - if (offer.OfferType == SpecialOfferType.FiveForAmount) x = 5; - var numberOfXs = quantityAsInt / x; - if (offer.OfferType == SpecialOfferType.ThreeForTwo && quantityAsInt > 2) - { - var discountAmount = quantity * unitPrice - (numberOfXs * 2 * unitPrice + quantityAsInt % 3 * unitPrice); - discount = new Discount(p, "3 for 2", -discountAmount); - } + var discount = CalculateDiscountForProduct(product, offers[product], catalog); + + if (discount != null) + receipt.AddDiscount(discount); + } + } - if (offer.OfferType == SpecialOfferType.TenPercentDiscount) discount = new Discount(p, offer.Argument + "% off", -quantity * unitPrice * offer.Argument / 100.0); - if (offer.OfferType == SpecialOfferType.FiveForAmount && quantityAsInt >= 5) - { - var discountTotal = unitPrice * quantity - (offer.Argument * numberOfXs + quantityAsInt % 5 * unitPrice); - discount = new Discount(p, x + " for " + PrintPrice(offer.Argument), -discountTotal); - } + private Discount CalculateDiscountForProduct(Product product, Offer offer, SupermarketCatalog catalog) + { + var quantity = _productQuantities[product]; + var quantityAsInt = (int)quantity; + var unitPrice = catalog.GetUnitPrice(product); - if (discount != null) - receipt.AddDiscount(discount); - } - } + return offer.OfferType switch + { + SpecialOfferType.ThreeForTwo => CalculateThreeForTwoDiscount(product, quantity, quantityAsInt, unitPrice, offer), + SpecialOfferType.TwoForAmount => CalculateTwoForAmountDiscount(product, quantity, quantityAsInt, unitPrice, offer), + SpecialOfferType.FiveForAmount => CalculateFiveForAmountDiscount(product, quantity, quantityAsInt, unitPrice, offer), + SpecialOfferType.TenPercentDiscount => CalculateTenPercentDiscount(product, quantity, unitPrice, offer), + _ => null + }; + } + + private Discount CalculateThreeForTwoDiscount(Product product, double quantity, int quantityAsInt, double unitPrice, Offer offer) + { + const int requiredQuantity = 3; + if (quantityAsInt < requiredQuantity) + return null; + + int numberOfTrios = quantityAsInt / requiredQuantity; + int remainingItems = quantityAsInt % requiredQuantity; + double originalPrice = CalculateOriginalPrice(quantity, unitPrice); + double discountedPrice = (numberOfTrios * 2 * unitPrice) + (remainingItems * unitPrice); + double discountAmount = originalPrice - discountedPrice; + + return new Discount(product, "3 for 2", -discountAmount); + } + + private Discount CalculateTwoForAmountDiscount(Product product, double quantity, int quantityAsInt, double unitPrice, Offer offer) + { + const int requiredQuantity = 2; + if (quantityAsInt < requiredQuantity) + return null; + + int numberOfPairs = quantityAsInt / requiredQuantity; + int remainingItems = quantityAsInt % requiredQuantity; + double originalPrice = CalculateOriginalPrice(quantity, unitPrice); + double discountedPrice = (offer.Argument * numberOfPairs) + (remainingItems * unitPrice); + double discountAmount = originalPrice - discountedPrice; + + return new Discount(product, $"2 for {PrintPrice(offer.Argument)}", -discountAmount); + } + + private Discount CalculateFiveForAmountDiscount(Product product, double quantity, int quantityAsInt, double unitPrice, Offer offer) + { + const int requiredQuantity = 5; + if (quantityAsInt < requiredQuantity) + return null; + + int numberOfFives = quantityAsInt / requiredQuantity; + int remainingItems = quantityAsInt % requiredQuantity; + double originalPrice = CalculateOriginalPrice(quantity, unitPrice); + double discountedPrice = (offer.Argument * numberOfFives) + (remainingItems * unitPrice); + double discountAmount = originalPrice - discountedPrice; + + return new Discount(product, $"5 for {PrintPrice(offer.Argument)}", -discountAmount); + } + + private Discount CalculateTenPercentDiscount(Product product, double quantity, double unitPrice, Offer offer) + { + double originalPrice = CalculateOriginalPrice(quantity, unitPrice); + double discountAmount = originalPrice * offer.Argument / 100.0; + return new Discount(product, $"{offer.Argument}% off", -discountAmount); + } + + private double CalculateOriginalPrice(double quantity, double unitPrice) + { + return quantity * unitPrice; } private string PrintPrice(double price) diff --git a/csharp/SupermarketReceipt/Teller.cs b/csharp/SupermarketReceipt/Teller.cs index d6b313e..574967d 100644 --- a/csharp/SupermarketReceipt/Teller.cs +++ b/csharp/SupermarketReceipt/Teller.cs @@ -20,7 +20,14 @@ public void AddSpecialOffer(SpecialOfferType offerType, Product product, double public Receipt ChecksOutArticlesFrom(ShoppingCart theCart) { var receipt = new Receipt(); - var productQuantities = theCart.GetItems(); + AddProductsToReceipt(theCart, receipt); + ApplyOffersToReceipt(theCart, receipt); + return receipt; + } + + private void AddProductsToReceipt(ShoppingCart cart, Receipt receipt) + { + var productQuantities = cart.GetItems(); foreach (var pq in productQuantities) { var p = pq.Product; @@ -29,10 +36,11 @@ public Receipt ChecksOutArticlesFrom(ShoppingCart theCart) var price = quantity * unitPrice; receipt.AddProduct(p, quantity, unitPrice, price); } + } - theCart.HandleOffers(receipt, _offers, _catalog); - - return receipt; + private void ApplyOffersToReceipt(ShoppingCart cart, Receipt receipt) + { + cart.HandleOffers(receipt, _offers, _catalog); } } } \ No newline at end of file diff --git a/elixir/.gitignore b/elixir/.gitignore deleted file mode 100644 index bfd308a..0000000 --- a/elixir/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -# The directory Mix will write compiled artifacts to. -/_build/ - -# If you run "mix test --cover", coverage assets end up here. -/cover/ - -# The directory Mix downloads your dependencies sources to. -/deps/ - -# Where third-party dependencies like ExDoc output generated docs. -/doc/ - -# Ignore .fetch files in case you like to edit your project deps locally. -/.fetch - -# If the VM crashes, it generates a dump, let's ignore it too. -erl_crash.dump - -# Also ignore archive artifacts (built via "mix archive.build"). -*.ez - -# Ignore package tarball (built via "mix hex.build"). -supermarket-*.tar - -# Temporary files, for example, from tests. -/tmp/ diff --git a/elixir/README-elixir.md b/elixir/README-elixir.md deleted file mode 100644 index e26eff1..0000000 --- a/elixir/README-elixir.md +++ /dev/null @@ -1,3 +0,0 @@ -# README (Elixir) - -There are no external dependencies other than Elixir itself. diff --git a/elixir/lib/supermarket/model/discount.ex b/elixir/lib/supermarket/model/discount.ex deleted file mode 100644 index 8de23c6..0000000 --- a/elixir/lib/supermarket/model/discount.ex +++ /dev/null @@ -1,7 +0,0 @@ -defmodule Supermarket.Model.Discount do - defstruct [:product, :description, :discount_amount] - - def new(product, description, discount_amount) do - %__MODULE__{product: product, description: description, discount_amount: discount_amount} - end -end diff --git a/elixir/lib/supermarket/model/offer.ex b/elixir/lib/supermarket/model/offer.ex deleted file mode 100644 index 329b0af..0000000 --- a/elixir/lib/supermarket/model/offer.ex +++ /dev/null @@ -1,7 +0,0 @@ -defmodule Supermarket.Model.Offer do - defstruct [:offer_type, :product, :argument] - - def new(offer_type, product, argument) do - %__MODULE__{offer_type: offer_type, argument: argument, product: product} - end -end diff --git a/elixir/lib/supermarket/model/product.ex b/elixir/lib/supermarket/model/product.ex deleted file mode 100644 index e7e0771..0000000 --- a/elixir/lib/supermarket/model/product.ex +++ /dev/null @@ -1,5 +0,0 @@ -defmodule Supermarket.Model.Product do - defstruct [:name, :unit] - - def new(name, unit), do: %__MODULE__{name: name, unit: unit} -end diff --git a/elixir/lib/supermarket/model/product_quantity.ex b/elixir/lib/supermarket/model/product_quantity.ex deleted file mode 100644 index e48c007..0000000 --- a/elixir/lib/supermarket/model/product_quantity.ex +++ /dev/null @@ -1,5 +0,0 @@ -defmodule Supermarket.Model.ProductQuantity do - defstruct [:product, :quantity] - - def new(product, weight), do: %__MODULE__{product: product, quantity: weight} -end diff --git a/elixir/lib/supermarket/model/receipt.ex b/elixir/lib/supermarket/model/receipt.ex deleted file mode 100644 index 8a95b4f..0000000 --- a/elixir/lib/supermarket/model/receipt.ex +++ /dev/null @@ -1,27 +0,0 @@ -defmodule Supermarket.Model.Receipt do - alias Supermarket.Model.ReceiptItem - - defstruct [:items, :discounts] - - def new, do: %__MODULE__{items: [], discounts: []} - - def add_product(receipt, product, quantity, price, total_price) do - item = ReceiptItem.new(product, quantity, price, total_price) - Map.update!(receipt, :items, &[item | &1]) - end - - def add_discount(receipt, discount) do - Map.update!(receipt, :discounts, &[discount | &1]) - end - - def total_price(receipt) do - item_total = Enum.reduce(receipt.items, 0.0, fn item, total -> item.total_price + total end) - - discount_total = - Enum.reduce(receipt.discounts, 0.0, fn discount, total -> - discount.discount_amount + total - end) - - item_total + discount_total - end -end diff --git a/elixir/lib/supermarket/model/receipt_item.ex b/elixir/lib/supermarket/model/receipt_item.ex deleted file mode 100644 index 8999b92..0000000 --- a/elixir/lib/supermarket/model/receipt_item.ex +++ /dev/null @@ -1,7 +0,0 @@ -defmodule Supermarket.Model.ReceiptItem do - defstruct [:product, :quantity, :price, :total_price] - - def new(product, quantity, price, total_price) do - %__MODULE__{product: product, quantity: quantity, price: price, total_price: total_price} - end -end diff --git a/elixir/lib/supermarket/model/shopping_cart.ex b/elixir/lib/supermarket/model/shopping_cart.ex deleted file mode 100644 index b0fc089..0000000 --- a/elixir/lib/supermarket/model/shopping_cart.ex +++ /dev/null @@ -1,99 +0,0 @@ -defmodule Supermarket.Model.ShoppingCart do - require IEx - alias Supermarket.Model.Discount - alias Supermarket.Model.ProductQuantity - alias Supermarket.Model.Receipt - alias Supermarket.Model.SupermarketCatalog - - defstruct [:items, :product_quantities] - - def new, do: %__MODULE__{items: [], product_quantities: %{}} - - def add_item(cart, product) do - add_item_quantity(cart, product, 1.0) - end - - def add_item_quantity(cart, product, quantity) do - cart - |> Map.update!(:items, &[ProductQuantity.new(product, quantity) | &1]) - |> Map.update!(:product_quantities, fn product_quantities -> - if Map.has_key?(product_quantities, product) do - Map.put(product_quantities, product, product_quantities[product] + quantity) - else - Map.put(product_quantities, product, quantity) - end - end) - end - - def handle_offers(cart, receipt, offers, catalog) do - cart.product_quantities - |> Map.keys() - |> Enum.reduce(receipt, fn p, receipt -> - quantity = cart.product_quantities[p] - - if Map.has_key?(offers, p) do - offer = offers[p] - unit_price = SupermarketCatalog.get_unit_price(catalog, p) - quantity_as_int = trunc(quantity) - discount = nil - x = 1 - - {discount, x} = - cond do - offer.offer_type == :three_for_two -> - {discount, 3} - - offer.offer_type == :two_for_amount -> - if quantity_as_int >= 2 do - x = 2 - int_division = div(quantity_as_int, x) - price_per_unit = offer.argument * int_division - the_total = Integer.mod(quantity_as_int, 2) * unit_price - total = price_per_unit + the_total - discount_n = unit_price * quantity - total - {Discount.new(p, "2 for #{offer.argument}", -discount_n), 2} - else - {discount, x} - end - - true -> - {discount, 2} - end - - x = if offer.offer_type == :five_for_amount, do: 5, else: x - number_of_xs = div(quantity_as_int, x) - - discount = - cond do - offer.offer_type == :three_for_two and quantity_as_int > 2 -> - discount_amount = - quantity * unit_price - - (number_of_xs * 2 * unit_price + Integer.mod(quantity_as_int, 3) * unit_price) - - Discount.new(p, "3 for 2", -discount_amount) - - offer.offer_type == :ten_percent_discount -> - Discount.new( - p, - "#{offer.argument}% off", - -quantity * unit_price * offer.argument / 100.0 - ) - - offer.offer_type == :five_for_amount and quantity_as_int >= 5 -> - discount_total = - unit_price * quantity - - (offer.argument * number_of_xs + Integer.mod(quantity_as_int, 5) * unit_price) - - Discount.new(p, "#{x} for #{offer.argument}", -discount_total) - - true -> - discount - end - - if !is_nil(discount), do: Receipt.add_discount(receipt, discount), else: receipt - else - receipt - end - end) - end -end diff --git a/elixir/lib/supermarket/model/supermarket_catalog.ex b/elixir/lib/supermarket/model/supermarket_catalog.ex deleted file mode 100644 index e4c28b1..0000000 --- a/elixir/lib/supermarket/model/supermarket_catalog.ex +++ /dev/null @@ -1,4 +0,0 @@ -defprotocol Supermarket.Model.SupermarketCatalog do - def add_product(catalog, product, price) - def get_unit_price(catalog, product) -end diff --git a/elixir/lib/supermarket/model/teller.ex b/elixir/lib/supermarket/model/teller.ex deleted file mode 100644 index 305546b..0000000 --- a/elixir/lib/supermarket/model/teller.ex +++ /dev/null @@ -1,34 +0,0 @@ -defmodule Supermarket.Model.Teller do - alias Supermarket.Model.Offer - alias Supermarket.Model.Receipt - alias Supermarket.Model.ShoppingCart - alias Supermarket.Model.SupermarketCatalog - - defstruct [:catalog, :offers] - - def new(catalog) do - %__MODULE__{catalog: catalog, offers: %{}} - end - - def add_special_offer(teller, offer_type, product, argument) do - Map.update!(teller, :offers, &Map.put(&1, product, Offer.new(offer_type, product, argument))) - end - - def checks_out_articles_from(teller, the_cart) do - receipt = Receipt.new() - product_quantities = the_cart.items - - receipt = - product_quantities - |> Enum.reverse() - |> Enum.reduce(receipt, fn pq, receipt -> - p = pq.product - quantity = pq.quantity - unit_price = SupermarketCatalog.get_unit_price(teller.catalog, p) - price = quantity * unit_price - Receipt.add_product(receipt, p, quantity, unit_price, price) - end) - - ShoppingCart.handle_offers(the_cart, receipt, teller.offers, teller.catalog) - end -end diff --git a/elixir/mix.exs b/elixir/mix.exs deleted file mode 100644 index 5441a27..0000000 --- a/elixir/mix.exs +++ /dev/null @@ -1,32 +0,0 @@ -defmodule Supermarket.MixProject do - use Mix.Project - - def project do - [ - app: :supermarket, - version: "0.1.0", - elixir: "~> 1.15", - start_permanent: Mix.env() == :prod, - deps: deps(), - elixirc_paths: elixirc_paths(Mix.env()) - ] - end - - # Run "mix help compile.app" to learn about applications. - def application do - [ - extra_applications: [:logger] - ] - end - - # Run "mix help deps" to learn about dependencies. - defp deps do - [ - # {:dep_from_hexpm, "~> 0.3.0"}, - # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} - ] - end - - defp elixirc_paths(:test), do: ["lib", "test/support"] - defp elixirc_paths(_env), do: ["lib"] -end diff --git a/elixir/test/supermarket/model/supermarket_test.exs b/elixir/test/supermarket/model/supermarket_test.exs deleted file mode 100644 index f414371..0000000 --- a/elixir/test/supermarket/model/supermarket_test.exs +++ /dev/null @@ -1,41 +0,0 @@ -defmodule Supermarket.Model.SupermarketTest do - use ExUnit.Case, async: true - - alias Supermarket.Model.Product - alias Supermarket.Model.Receipt - alias Supermarket.Model.ShoppingCart - alias Supermarket.Model.SupermarketCatalog - alias Supermarket.Model.Teller - - # Todo: test all kinds of discounts are applied properly - - test "ten percent discount" do - toothbrush = Product.new("toothbrush", :each) - apples = Product.new("apples", :kilo) - - catalog = - FakeCatalog.new() - |> SupermarketCatalog.add_product(toothbrush, 0.99) - |> SupermarketCatalog.add_product(apples, 1.99) - - teller = - catalog - |> Teller.new() - |> Teller.add_special_offer(:ten_percent_discount, toothbrush, 10.0) - - the_cart = ShoppingCart.new() |> ShoppingCart.add_item_quantity(apples, 2.5) - - # ACT - receipt = Teller.checks_out_articles_from(teller, the_cart) - - # ASSERT - assert_in_delta Receipt.total_price(receipt), 4.975, 0.01 - assert receipt.discounts == [] - assert length(receipt.items) == 1 - receipt_item = List.first(receipt.items) - assert receipt_item.product == apples - assert receipt_item.price == 1.99 - assert receipt_item.total_price == 2.5 * 1.99 - assert receipt_item.quantity == 2.5 - end -end diff --git a/elixir/test/support/fake_catalog.ex b/elixir/test/support/fake_catalog.ex deleted file mode 100644 index 0ea6155..0000000 --- a/elixir/test/support/fake_catalog.ex +++ /dev/null @@ -1,17 +0,0 @@ -defmodule FakeCatalog do - defstruct [:products, :prices] - - def new, do: %__MODULE__{products: %{}, prices: %{}} - - defimpl Supermarket.Model.SupermarketCatalog do - def add_product(catalog, product, price) do - catalog - |> Map.update!(:products, &Map.put(&1, product.name, product)) - |> Map.update!(:prices, &Map.put(&1, product.name, price)) - end - - def get_unit_price(catalog, product) do - catalog.prices[product.name] - end - end -end diff --git a/elixir/test/test_helper.exs b/elixir/test/test_helper.exs deleted file mode 100644 index 869559e..0000000 --- a/elixir/test/test_helper.exs +++ /dev/null @@ -1 +0,0 @@ -ExUnit.start() diff --git a/go/README.md b/go/README.md deleted file mode 100644 index 30ea485..0000000 --- a/go/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Supermarket Receipt in [Go](http://golang.org/) - -## Setup - -* Have Go installed in version 1.18 or later -* Clone the repository -* On the command line, enter the `SupermarketReceipt-Refactoring-Kata/go` directory -* Run `go mod download` - -## Running Tests - -On the command line, enter the `SupermarketReceipt-Refactoring-Kata/go` directory and run - -``` -go test ./... -``` diff --git a/go/go.mod b/go/go.mod deleted file mode 100644 index fde60a8..0000000 --- a/go/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/SupermarketReceipt-Refactoring-Kata/go - -go 1.18 - -require ( - github.com/approvals/go-approval-tests v0.0.0-20220530063708-32d5677069bd - golang.org/x/text v0.4.0 -) diff --git a/go/go.sum b/go/go.sum deleted file mode 100644 index 8754360..0000000 --- a/go/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -github.com/approvals/go-approval-tests v0.0.0-20220530063708-32d5677069bd h1:8j7sBEy0h6+Bvr0AeKHIHCsmzCzWGXAQweA7k+uiRYk= -github.com/approvals/go-approval-tests v0.0.0-20220530063708-32d5677069bd/go.mod h1:PJOqSY8IofNv3heAD6k8E7EfFS6okiSS9bSAasaAUME= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= diff --git a/go/supermarket/SupermarketReceipt_test.go b/go/supermarket/SupermarketReceipt_test.go deleted file mode 100644 index bb3197e..0000000 --- a/go/supermarket/SupermarketReceipt_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package supermarket - -import ( - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "testing" -) - -type FakeCatalog struct { - _products map[string]Product - _prices map[string]float64 -} - - -func (c FakeCatalog) unitPrice(product Product) float64 { - return c._prices[product.name] -} - -func (c FakeCatalog) addProduct(product Product, price float64) { - c._products[product.name] = product - c._prices[product.name] = price -} - -func NewFakeCatalog() *FakeCatalog { - var c FakeCatalog - c._products = make(map[string]Product) - c._prices = make(map[string]float64) - return &c -} - -func TestTenPercentDiscount(t *testing.T) { - // ARRANGE - var toothbrush = Product{name: "toothbrush", unit: Each} - var apples = Product{name: "apples", unit: Kilo} - var catalog = NewFakeCatalog() - catalog.addProduct(toothbrush, 0.99) - catalog.addProduct(apples, 1.99) - - var teller = NewTeller(catalog) - teller.addSpecialOffer(TenPercentDiscount, toothbrush, 10.0) - - var cart = NewShoppingCart() - cart.addItemQuantity(apples, 2.5) - - // ACT - var receipt = teller.checksOutArticlesFrom(cart) - - // ASSERT - assert.Equal(t, 4.975, receipt.totalPrice()) - assert.Equal(t, 0, len(receipt.discounts)) - require.Equal(t, 1, len(receipt.items)) - var receiptItem = receipt.items[0] - assert.Equal(t, 1.99, receiptItem.price) - assert.Equal(t, 2.5*1.99, receiptItem.totalPrice) - assert.Equal(t, 2.5, receiptItem.quantity) -} diff --git a/go/supermarket/printers.go b/go/supermarket/printers.go deleted file mode 100644 index e742392..0000000 --- a/go/supermarket/printers.go +++ /dev/null @@ -1,82 +0,0 @@ -package supermarket - -import ( - "fmt" - "golang.org/x/text/language" - "golang.org/x/text/message" - "strings" -) - -type ReceiptPrinter struct { - columns int - lp *message.Printer -} - -func NewReceiptPrinter() *ReceiptPrinter { - var p ReceiptPrinter - p.columns = 40 - p.lp = message.NewPrinter(language.BritishEnglish) - return &p -} - -func (p ReceiptPrinter) printReceipt(receipt *Receipt) string { - var result string - for _, item := range receipt.sortedItems() { - var receiptItem = p.presentReceiptItem(item) - result += receiptItem - } - for _, discount := range receipt.sortedDiscounts() { - result += p.presentDiscount(discount) - } - result += "\n" - result += p.presentTotal(receipt) - - return result -} - -func (p ReceiptPrinter) presentReceiptItem(item ReceiptItem) string { - var totalPricePresentation string = p.presentPrice(item.totalPrice) - var line = p.formatLineWithWhitespace(item.product.name, totalPricePresentation) - if item.quantity != 1 { - line += fmt.Sprintf(" %s * %s\n", p.presentPrice(item.price), p.presentQuantity(item)) - } - return line -} - -func (p ReceiptPrinter) formatLineWithWhitespace(name string, value string) string { - var result strings.Builder - fmt.Fprint(&result, name) - var whitespaceSize = p.columns - len(name) - len(value) - for i := 1; i <= whitespaceSize; i++ { - fmt.Fprint(&result, " ") - } - fmt.Fprintf(&result, "%s\n", value) - return result.String() -} - -func (p ReceiptPrinter) presentPrice(price float64) string { - return p.lp.Sprintf("%.2f", price) -} - -func (p ReceiptPrinter) presentQuantity(item ReceiptItem) string { - var result string - if Each == item.product.unit { - result = fmt.Sprintf("%d", int(item.quantity)) - } else { - result = p.lp.Sprintf("%.3f", item.quantity) - } - return result -} - -func (p ReceiptPrinter) presentTotal(receipt *Receipt) string { - var name = "Total: " - var value = p.presentPrice(receipt.totalPrice()) - return p.formatLineWithWhitespace(name, value) -} - -func (p ReceiptPrinter) presentDiscount(discount Discount) string { - var name = fmt.Sprintf("%s (%s)", discount.description, discount.product.name) - var value = p.presentPrice(discount.discountAmount) - return p.formatLineWithWhitespace(name, value) -} - diff --git a/go/supermarket/product.go b/go/supermarket/product.go deleted file mode 100644 index f2f4420..0000000 --- a/go/supermarket/product.go +++ /dev/null @@ -1,17 +0,0 @@ -package supermarket - -type ProductUnit int - -type Catalog interface { - unitPrice(product Product) float64 -} - -const ( - Each ProductUnit = iota - Kilo -) - -type Product struct { - name string - unit ProductUnit -} diff --git a/go/supermarket/receipt.go b/go/supermarket/receipt.go deleted file mode 100644 index 9de1f2e..0000000 --- a/go/supermarket/receipt.go +++ /dev/null @@ -1,61 +0,0 @@ -package supermarket - -import "sort" - -type Receipt struct { - items []ReceiptItem - discounts []Discount -} - -type ReceiptItem struct { - product Product - quantity float64 - price float64 - totalPrice float64 -} - -func NewReceipt() *Receipt { - var r Receipt - r.items = []ReceiptItem{} - r.discounts = []Discount{} - return &r -} - -func (r *Receipt) totalPrice() float64 { - var total float64 = 0 - for _, item := range r.items { - total += item.totalPrice - } - for _, discount := range r.discounts { - total += discount.discountAmount - } - return total -} - - -func (r *Receipt) addProduct(product Product, quantity float64, unitPrice float64, price float64) { - r.items = append(r.items, ReceiptItem{ - product: product, - quantity: quantity, - price: unitPrice, - totalPrice: price, - }) -} - -func (r *Receipt) addDiscount(discount Discount) { - r.discounts = append(r.discounts, discount) -} - -func (r Receipt) sortedItems() []ReceiptItem { - sort.Slice(r.items, func(i, j int) bool { - return r.items[i].product.name < r.items[j].product.name - }) - return r.items -} - -func (r Receipt) sortedDiscounts() []Discount { - sort.Slice(r.discounts, func(i, j int) bool { - return r.discounts[i].product.name < r.discounts[j].product.name - }) - return r.discounts -} diff --git a/go/supermarket/shopping_cart.go b/go/supermarket/shopping_cart.go deleted file mode 100644 index 3079f92..0000000 --- a/go/supermarket/shopping_cart.go +++ /dev/null @@ -1,81 +0,0 @@ -package supermarket - -import ( - "fmt" - "math" -) - -type ProductQuantity struct { - product Product - quantity float64 -} - -type ShoppingCart struct { - items []ProductQuantity - productQuantities map[Product]float64 -} - -func NewShoppingCart() *ShoppingCart { - var s ShoppingCart - s.items = []ProductQuantity{} - s.productQuantities = make(map[Product]float64) - return &s -} - -func (c *ShoppingCart) addItem(product Product) { - c.addItemQuantity(product, 1) -} - -func (c *ShoppingCart) addItemQuantity(product Product, amount float64) { - c.items = append(c.items, ProductQuantity{product: product, quantity: amount}) - currentAmount, ok := c.productQuantities[product] - if ok { - c.productQuantities[product] = currentAmount + amount - } else { - c.productQuantities[product] = amount - } -} - -func (c *ShoppingCart) handleOffers(receipt *Receipt, offers map[Product]SpecialOffer, catalog Catalog) { - for p, _ := range c.productQuantities { - var quantity = c.productQuantities[p] - if offer, ok := offers[p]; ok { - var unitPrice = catalog.unitPrice(p) - var quantityAsInt = int(math.Round(quantity)) - var discount *Discount = nil - var x = 1 - if offer.offerType == ThreeForTwo { - x = 3 - - } else if offer.offerType == TwoForAmount { - x = 2 - if quantityAsInt >= 2 { - var total = offer.argument * float64(quantityAsInt / x) + float64(quantityAsInt % 2) * unitPrice - var discountN = unitPrice * quantity - total; - discount = &Discount{product: p, description: fmt.Sprintf("2 for %.2f", offer.argument), discountAmount: -discountN} - } - - } - if offer.offerType == FiveForAmount { - x = 5 - } - var numberOfXs int = quantityAsInt / x; - if offer.offerType == ThreeForTwo && quantityAsInt > 2 { - var discountAmount = quantity * unitPrice - (float64(numberOfXs * 2) * unitPrice + float64(quantityAsInt % 3) * unitPrice) - discount = &Discount{product: p, description: "3 for 2", discountAmount: -discountAmount} - } - if offer.offerType == TenPercentDiscount { - discount = &Discount{product: p, description: fmt.Sprintf("%.0f %% off", offer.argument), discountAmount: -quantity * unitPrice * offer.argument / 100.0} - } - if offer.offerType == FiveForAmount && quantityAsInt >= 5 { - var discountTotal = unitPrice * quantity - (offer.argument * float64(numberOfXs) + float64(quantityAsInt % 5) * unitPrice) - discount = &Discount{product: p, description: fmt.Sprintf("%d for %.2f", x, offer.argument), discountAmount: -discountTotal} - } - if discount != nil { - receipt.addDiscount(*discount) - } - - } - - } -} diff --git a/go/supermarket/special_offers.go b/go/supermarket/special_offers.go deleted file mode 100644 index dc2ffa3..0000000 --- a/go/supermarket/special_offers.go +++ /dev/null @@ -1,23 +0,0 @@ -package supermarket - -type SpecialOfferType int - -const ( - TenPercentDiscount SpecialOfferType = iota - ThreeForTwo - TwoForAmount - FiveForAmount -) - -type SpecialOffer struct { - offerType SpecialOfferType - product Product - argument float64 -} - -type Discount struct { - product Product - description string - discountAmount float64 -} - diff --git a/go/supermarket/teller.go b/go/supermarket/teller.go deleted file mode 100644 index 7bf9c2a..0000000 --- a/go/supermarket/teller.go +++ /dev/null @@ -1,30 +0,0 @@ -package supermarket - - -type Teller struct { - catalog Catalog - offers map[Product]SpecialOffer -} - -func NewTeller(catalog Catalog) *Teller { - var t Teller - t.catalog = catalog - t.offers = make(map[Product]SpecialOffer) - return &t -} - -func (t *Teller) checksOutArticlesFrom(cart *ShoppingCart) *Receipt { - var receipt = NewReceipt() - for p, q := range cart.productQuantities { - var unitPrice = t.catalog.unitPrice(p) - var price = q * unitPrice - receipt.addProduct(p, q, unitPrice, price) - } - cart.handleOffers(receipt, t.offers, t.catalog) - return receipt -} - -func (t *Teller) addSpecialOffer(offerType SpecialOfferType, product Product, argument float64) { - var offer = SpecialOffer{offerType: offerType, product: product, argument: argument} - t.offers[product] = offer -} diff --git a/java/pom.xml b/java/pom.xml deleted file mode 100644 index 73ba4a7..0000000 --- a/java/pom.xml +++ /dev/null @@ -1,55 +0,0 @@ - - 4.0.0 - - org.codingdojo - Supermarket-Receipt - 1.0.0-SNAPSHOT - jar - - - UTF-8 - 5.10.2 - - - - - - org.junit.jupiter - junit-jupiter-api - ${junit.jupiter.version} - test - - - org.junit.jupiter - junit-jupiter-params - ${junit.jupiter.version} - test - - - org.junit.jupiter - junit-jupiter-engine - ${junit.jupiter.version} - test - - - com.approvaltests - approvaltests - 24.2.0 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.0 - - 22 - 22 - - - - - - diff --git a/java/src/main/java/dojo/supermarket/model/Discount.java b/java/src/main/java/dojo/supermarket/model/Discount.java deleted file mode 100644 index 0416743..0000000 --- a/java/src/main/java/dojo/supermarket/model/Discount.java +++ /dev/null @@ -1,26 +0,0 @@ -package dojo.supermarket.model; - -public class Discount { - - private final String description; - private final double discountAmount; - private final Product product; - - public Discount(Product product, String description, double discountAmount) { - this.product = product; - this.description = description; - this.discountAmount = discountAmount; - } - - public String getDescription() { - return description; - } - - public double getDiscountAmount() { - return discountAmount; - } - - public Product getProduct() { - return product; - } -} diff --git a/java/src/main/java/dojo/supermarket/model/Offer.java b/java/src/main/java/dojo/supermarket/model/Offer.java deleted file mode 100644 index 2f3ed30..0000000 --- a/java/src/main/java/dojo/supermarket/model/Offer.java +++ /dev/null @@ -1,18 +0,0 @@ -package dojo.supermarket.model; - -public class Offer { - - SpecialOfferType offerType; - private final Product product; - double argument; - - public Offer(SpecialOfferType offerType, Product product, double argument) { - this.offerType = offerType; - this.argument = argument; - this.product = product; - } - - Product getProduct() { - return product; - } -} diff --git a/java/src/main/java/dojo/supermarket/model/Product.java b/java/src/main/java/dojo/supermarket/model/Product.java deleted file mode 100644 index 89a3147..0000000 --- a/java/src/main/java/dojo/supermarket/model/Product.java +++ /dev/null @@ -1,36 +0,0 @@ -package dojo.supermarket.model; - -import java.util.Objects; - -public class Product { - - private final String name; - private final ProductUnit unit; - - public Product(String name, ProductUnit unit) { - this.name = name; - this.unit = unit; - } - - public String getName() { - return name; - } - - public ProductUnit getUnit() { - return unit; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Product)) return false; - Product product = (Product) o; - return Objects.equals(name, product.name) && - unit == product.unit; - } - - @Override - public int hashCode() { - return Objects.hash(name, unit); - } -} diff --git a/java/src/main/java/dojo/supermarket/model/ProductQuantity.java b/java/src/main/java/dojo/supermarket/model/ProductQuantity.java deleted file mode 100644 index 99f7125..0000000 --- a/java/src/main/java/dojo/supermarket/model/ProductQuantity.java +++ /dev/null @@ -1,20 +0,0 @@ -package dojo.supermarket.model; - -public class ProductQuantity { - - private final Product product; - private final double quantity; - - public ProductQuantity(Product product, double weight) { - this.product = product; - this.quantity = weight; - } - - public Product getProduct() { - return product; - } - - public double getQuantity() { - return quantity; - } -} diff --git a/java/src/main/java/dojo/supermarket/model/ProductUnit.java b/java/src/main/java/dojo/supermarket/model/ProductUnit.java deleted file mode 100644 index 36b6fc3..0000000 --- a/java/src/main/java/dojo/supermarket/model/ProductUnit.java +++ /dev/null @@ -1,5 +0,0 @@ -package dojo.supermarket.model; - -public enum ProductUnit { - KILO, EACH -} diff --git a/java/src/main/java/dojo/supermarket/model/Receipt.java b/java/src/main/java/dojo/supermarket/model/Receipt.java deleted file mode 100644 index 27af0f1..0000000 --- a/java/src/main/java/dojo/supermarket/model/Receipt.java +++ /dev/null @@ -1,38 +0,0 @@ -package dojo.supermarket.model; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class Receipt { - - private final List items = new ArrayList<>(); - private final List discounts = new ArrayList<>(); - - public double getTotalPrice() { - double total = 0.0; - for (ReceiptItem item : items) { - total += item.getTotalPrice(); - } - for (Discount discount : discounts) { - total += discount.getDiscountAmount(); - } - return total; - } - - public void addProduct(Product p, double quantity, double price, double totalPrice) { - items.add(new ReceiptItem(p, quantity, price, totalPrice)); - } - - public List getItems() { - return Collections.unmodifiableList(items); - } - - public void addDiscount(Discount discount) { - discounts.add(discount); - } - - public List getDiscounts() { - return discounts; - } -} diff --git a/java/src/main/java/dojo/supermarket/model/ReceiptItem.java b/java/src/main/java/dojo/supermarket/model/ReceiptItem.java deleted file mode 100644 index 9dc36f6..0000000 --- a/java/src/main/java/dojo/supermarket/model/ReceiptItem.java +++ /dev/null @@ -1,50 +0,0 @@ -package dojo.supermarket.model; - -import java.util.Objects; - -public class ReceiptItem { - - private final Product product; - private final double price; - private final double totalPrice; - private final double quantity; - - ReceiptItem(Product p, double quantity, double price, double totalPrice) { - this.product = p; - this.quantity = quantity; - this.price = price; - this.totalPrice = totalPrice; - } - - public double getPrice() { - return price; - } - - public Product getProduct() { - return product; - } - - public double getQuantity() { - return quantity; - } - - public double getTotalPrice() { - return totalPrice; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ReceiptItem)) return false; - ReceiptItem that = (ReceiptItem) o; - return Double.compare(that.price, price) == 0 && - Double.compare(that.totalPrice, totalPrice) == 0 && - Double.compare(that.quantity, quantity) == 0 && - Objects.equals(product, that.product); - } - - @Override - public int hashCode() { - return Objects.hash(product, price, totalPrice, quantity); - } -} diff --git a/java/src/main/java/dojo/supermarket/model/ShoppingCart.java b/java/src/main/java/dojo/supermarket/model/ShoppingCart.java deleted file mode 100644 index 9293086..0000000 --- a/java/src/main/java/dojo/supermarket/model/ShoppingCart.java +++ /dev/null @@ -1,75 +0,0 @@ -package dojo.supermarket.model; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ShoppingCart { - - private final List items = new ArrayList<>(); - private final Map productQuantities = new HashMap<>(); - - List getItems() { - return Collections.unmodifiableList(items); - } - - void addItem(Product product) { - addItemQuantity(product, 1.0); - } - - Map productQuantities() { - return Collections.unmodifiableMap(productQuantities); - } - - public void addItemQuantity(Product product, double quantity) { - items.add(new ProductQuantity(product, quantity)); - if (productQuantities.containsKey(product)) { - productQuantities.put(product, productQuantities.get(product) + quantity); - } else { - productQuantities.put(product, quantity); - } - } - - void handleOffers(Receipt receipt, Map offers, SupermarketCatalog catalog) { - for (Product p: productQuantities().keySet()) { - double quantity = productQuantities.get(p); - if (offers.containsKey(p)) { - Offer offer = offers.get(p); - double unitPrice = catalog.getUnitPrice(p); - int quantityAsInt = (int) quantity; - Discount discount = null; - int x = 1; - if (offer.offerType == SpecialOfferType.THREE_FOR_TWO) { - x = 3; - - } else if (offer.offerType == SpecialOfferType.TWO_FOR_AMOUNT) { - x = 2; - if (quantityAsInt >= 2) { - double total = offer.argument * (quantityAsInt / x) + quantityAsInt % 2 * unitPrice; - double discountN = unitPrice * quantity - total; - discount = new Discount(p, "2 for " + offer.argument, -discountN); - } - - } if (offer.offerType == SpecialOfferType.FIVE_FOR_AMOUNT) { - x = 5; - } - int numberOfXs = quantityAsInt / x; - if (offer.offerType == SpecialOfferType.THREE_FOR_TWO && quantityAsInt > 2) { - double discountAmount = quantity * unitPrice - ((numberOfXs * 2 * unitPrice) + quantityAsInt % 3 * unitPrice); - discount = new Discount(p, "3 for 2", -discountAmount); - } - if (offer.offerType == SpecialOfferType.TEN_PERCENT_DISCOUNT) { - discount = new Discount(p, offer.argument + "% off", -quantity * unitPrice * offer.argument / 100.0); - } - if (offer.offerType == SpecialOfferType.FIVE_FOR_AMOUNT && quantityAsInt >= 5) { - double discountTotal = unitPrice * quantity - (offer.argument * numberOfXs + quantityAsInt % 5 * unitPrice); - discount = new Discount(p, x + " for " + offer.argument, -discountTotal); - } - if (discount != null) - receipt.addDiscount(discount); - } - } - } -} diff --git a/java/src/main/java/dojo/supermarket/model/SpecialOfferType.java b/java/src/main/java/dojo/supermarket/model/SpecialOfferType.java deleted file mode 100644 index ffc4bf0..0000000 --- a/java/src/main/java/dojo/supermarket/model/SpecialOfferType.java +++ /dev/null @@ -1,8 +0,0 @@ -package dojo.supermarket.model; - -public enum SpecialOfferType { - THREE_FOR_TWO, - TEN_PERCENT_DISCOUNT, - TWO_FOR_AMOUNT, - FIVE_FOR_AMOUNT, -} diff --git a/java/src/main/java/dojo/supermarket/model/SupermarketCatalog.java b/java/src/main/java/dojo/supermarket/model/SupermarketCatalog.java deleted file mode 100644 index 8c9f037..0000000 --- a/java/src/main/java/dojo/supermarket/model/SupermarketCatalog.java +++ /dev/null @@ -1,8 +0,0 @@ -package dojo.supermarket.model; - -public interface SupermarketCatalog { - - void addProduct(Product product, double price); - - double getUnitPrice(Product product); -} diff --git a/java/src/main/java/dojo/supermarket/model/Teller.java b/java/src/main/java/dojo/supermarket/model/Teller.java deleted file mode 100644 index 72dc6e2..0000000 --- a/java/src/main/java/dojo/supermarket/model/Teller.java +++ /dev/null @@ -1,34 +0,0 @@ -package dojo.supermarket.model; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class Teller { - - private final SupermarketCatalog catalog; - private final Map offers = new HashMap<>(); - - public Teller(SupermarketCatalog catalog) { - this.catalog = catalog; - } - - public void addSpecialOffer(SpecialOfferType offerType, Product product, double argument) { - offers.put(product, new Offer(offerType, product, argument)); - } - - public Receipt checksOutArticlesFrom(ShoppingCart theCart) { - Receipt receipt = new Receipt(); - List productQuantities = theCart.getItems(); - for (ProductQuantity pq: productQuantities) { - Product p = pq.getProduct(); - double quantity = pq.getQuantity(); - double unitPrice = catalog.getUnitPrice(p); - double price = quantity * unitPrice; - receipt.addProduct(p, quantity, unitPrice, price); - } - theCart.handleOffers(receipt, offers, catalog); - - return receipt; - } -} diff --git a/java/src/test/java/dojo/supermarket/PackageSettings.java b/java/src/test/java/dojo/supermarket/PackageSettings.java deleted file mode 100644 index 16b17b6..0000000 --- a/java/src/test/java/dojo/supermarket/PackageSettings.java +++ /dev/null @@ -1,12 +0,0 @@ -package dojo.supermarket; - -import org.approvaltests.core.ApprovalFailureReporter; -import org.approvaltests.reporters.JunitReporter; - -/** - * Configure the reporter used by Approval Tests. - * Documentation: https://github.com/approvals/ApprovalTests.Java/blob/master/approvaltests/docs/Reporters.md - */ -public class PackageSettings { - public static ApprovalFailureReporter UseReporter = JunitReporter.INSTANCE; -} diff --git a/java/src/test/java/dojo/supermarket/ReceiptPrinter.java b/java/src/test/java/dojo/supermarket/ReceiptPrinter.java deleted file mode 100644 index 070a13f..0000000 --- a/java/src/test/java/dojo/supermarket/ReceiptPrinter.java +++ /dev/null @@ -1,81 +0,0 @@ -package dojo.supermarket; - -import dojo.supermarket.model.*; - -import java.util.Locale; - -public class ReceiptPrinter { - - private final int columns; - - public ReceiptPrinter() { - this(40); - } - - public ReceiptPrinter(int columns) { - this.columns = columns; - } - - public String printReceipt(Receipt receipt) { - StringBuilder result = new StringBuilder(); - for (ReceiptItem item : receipt.getItems()) { - String receiptItem = presentReceiptItem(item); - result.append(receiptItem); - } - for (Discount discount : receipt.getDiscounts()) { - String discountPresentation = presentDiscount(discount); - result.append(discountPresentation); - } - - result.append("\n"); - result.append(presentTotal(receipt)); - return result.toString(); - } - - private String presentReceiptItem(ReceiptItem item) { - String totalPricePresentation = presentPrice(item.getTotalPrice()); - String name = item.getProduct().getName(); - - String line = formatLineWithWhitespace(name, totalPricePresentation); - - if (item.getQuantity() != 1) { - line += " " + presentPrice(item.getPrice()) + " * " + presentQuantity(item) + "\n"; - } - return line; - } - - private String presentDiscount(Discount discount) { - String name = discount.getDescription() + "(" + discount.getProduct().getName() + ")"; - String value = presentPrice(discount.getDiscountAmount()); - - return formatLineWithWhitespace(name, value); - } - - private String presentTotal(Receipt receipt) { - String name = "Total: "; - String value = presentPrice(receipt.getTotalPrice()); - return formatLineWithWhitespace(name, value); - } - - private String formatLineWithWhitespace(String name, String value) { - StringBuilder line = new StringBuilder(); - line.append(name); - int whitespaceSize = this.columns - name.length() - value.length(); - for (int i = 0; i < whitespaceSize; i++) { - line.append(" "); - } - line.append(value); - line.append('\n'); - return line.toString(); - } - - private static String presentPrice(double price) { - return String.format(Locale.UK, "%.2f", price); - } - - private static String presentQuantity(ReceiptItem item) { - return ProductUnit.EACH.equals(item.getProduct().getUnit()) - ? String.format("%d", (int)item.getQuantity()) - : String.format(Locale.UK, "%.3f", item.getQuantity()); - } -} diff --git a/java/src/test/java/dojo/supermarket/model/FakeCatalog.java b/java/src/test/java/dojo/supermarket/model/FakeCatalog.java deleted file mode 100644 index d150f85..0000000 --- a/java/src/test/java/dojo/supermarket/model/FakeCatalog.java +++ /dev/null @@ -1,20 +0,0 @@ -package dojo.supermarket.model; - -import java.util.HashMap; -import java.util.Map; - -public class FakeCatalog implements SupermarketCatalog { - private Map products = new HashMap<>(); - private Map prices = new HashMap<>(); - - @Override - public void addProduct(Product product, double price) { - this.products.put(product.getName(), product); - this.prices.put(product.getName(), price); - } - - @Override - public double getUnitPrice(Product p) { - return this.prices.get(p.getName()); - } -} diff --git a/java/src/test/java/dojo/supermarket/model/SupermarketTest.java b/java/src/test/java/dojo/supermarket/model/SupermarketTest.java deleted file mode 100644 index 5a81d18..0000000 --- a/java/src/test/java/dojo/supermarket/model/SupermarketTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package dojo.supermarket.model; - -import dojo.supermarket.ReceiptPrinter; -import org.approvaltests.Approvals; -import org.junit.jupiter.api.Test; - -import java.util.Collections; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class SupermarketTest { - - // Todo: test all kinds of discounts are applied properly - - @Test - void tenPercentDiscount() { - SupermarketCatalog catalog = new FakeCatalog(); - Product toothbrush = new Product("toothbrush", ProductUnit.EACH); - catalog.addProduct(toothbrush, 0.99); - Product apples = new Product("apples", ProductUnit.KILO); - catalog.addProduct(apples, 1.99); - - Teller teller = new Teller(catalog); - teller.addSpecialOffer(SpecialOfferType.TEN_PERCENT_DISCOUNT, toothbrush, 10.0); - - ShoppingCart cart = new ShoppingCart(); - cart.addItemQuantity(apples, 2.5); - - // ACT - Receipt receipt = teller.checksOutArticlesFrom(cart); - - // ASSERT - assertEquals(4.975, receipt.getTotalPrice(), 0.01); - assertEquals(Collections.emptyList(), receipt.getDiscounts()); - assertEquals(1, receipt.getItems().size()); - ReceiptItem receiptItem = receipt.getItems().get(0); - assertEquals(apples, receiptItem.getProduct()); - assertEquals(1.99, receiptItem.getPrice()); - assertEquals(2.5*1.99, receiptItem.getTotalPrice()); - assertEquals(2.5, receiptItem.getQuantity()); - - } - - -} diff --git a/kotlin/build.gradle b/kotlin/build.gradle deleted file mode 100644 index 2292a17..0000000 --- a/kotlin/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' version '1.3.20' -} - -ext { - junitVersion = "5.4.0" -} - -group 'org.codingdojo' -version '1.0.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -test { - useJUnitPlatform() -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - - testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" - testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitVersion" -} - -compileKotlin { - kotlinOptions.jvmTarget = "1.8" -} -compileTestKotlin { - kotlinOptions.jvmTarget = "1.8" -} diff --git a/kotlin/gradle.properties b/kotlin/gradle.properties deleted file mode 100644 index 29e08e8..0000000 --- a/kotlin/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -kotlin.code.style=official \ No newline at end of file diff --git a/kotlin/gradle/wrapper/gradle-wrapper.jar b/kotlin/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 28861d273a5d270fd8f65dd74570c17c9c507736..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56172 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

ZM+x{iQDVZRnLYbdXrQjU&=u%hsN4|smH&B~F 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 b+=;!jU|P+J`>$CfFsoU|bwOU-ceCsYH7qU$ diff --git a/kotlin/gradle/wrapper/gradle-wrapper.properties b/kotlin/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 115e6ac..0000000 --- a/kotlin/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/kotlin/gradlew b/kotlin/gradlew deleted file mode 100755 index cccdd3d..0000000 --- a/kotlin/gradlew +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env sh - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" diff --git a/kotlin/gradlew.bat b/kotlin/gradlew.bat deleted file mode 100644 index f955316..0000000 --- a/kotlin/gradlew.bat +++ /dev/null @@ -1,84 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/kotlin/settings.gradle b/kotlin/settings.gradle deleted file mode 100644 index d1f0f84..0000000 --- a/kotlin/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'Supermarket-Receipt' - diff --git a/kotlin/src/main/kotlin/supermarket/ReceiptPrinter.kt b/kotlin/src/main/kotlin/supermarket/ReceiptPrinter.kt deleted file mode 100644 index 004097f..0000000 --- a/kotlin/src/main/kotlin/supermarket/ReceiptPrinter.kt +++ /dev/null @@ -1,61 +0,0 @@ -package supermarket - -import supermarket.model.ProductUnit -import supermarket.model.Receipt -import supermarket.model.ReceiptItem -import java.util.* - -class ReceiptPrinter @JvmOverloads constructor(private val columns: Int = 40) { - - fun printReceipt(receipt: Receipt): String { - val result = StringBuilder() - for (item in receipt.getItems()) { - val price = String.format(Locale.UK, "%.2f", item.totalPrice) - val quantity = presentQuantity(item) - val name = item.product.name - val unitPrice = String.format(Locale.UK, "%.2f", item.price) - - val whitespaceSize = this.columns - name.length - price.length - var line = name + getWhitespace(whitespaceSize) + price + "\n" - - if (item.quantity != 1.0) { - line += " $unitPrice * $quantity\n" - } - result.append(line) - } - for (discount in receipt.getDiscounts()) { - val productPresentation = discount.product.name - val pricePresentation = String.format(Locale.UK, "%.2f", discount.discountAmount) - val description = discount.description - result.append(description) - result.append("(") - result.append(productPresentation) - result.append(")") - result.append(getWhitespace(this.columns - 3 - productPresentation.length - description.length - pricePresentation.length)) - result.append("-") - result.append(pricePresentation) - result.append("\n") - } - result.append("\n") - val pricePresentation = String.format(Locale.UK, "%.2f", receipt.totalPrice as Double) - val total = "Total: " - val whitespace = getWhitespace(this.columns - total.length - pricePresentation.length) - result.append(total).append(whitespace).append(pricePresentation) - return result.toString() - } - - private fun presentQuantity(item: ReceiptItem): String { - return if (ProductUnit.Each.equals(item.product.unit)) - String.format("%x", item.quantity.toInt()) - else - String.format(Locale.UK, "%.3f", item.quantity) - } - - private fun getWhitespace(whitespaceSize: Int): String { - val whitespace = StringBuilder() - for (i in 0 until whitespaceSize) { - whitespace.append(" ") - } - return whitespace.toString() - } -} diff --git a/kotlin/src/main/kotlin/supermarket/model/Discount.kt b/kotlin/src/main/kotlin/supermarket/model/Discount.kt deleted file mode 100644 index 8e0f8f5..0000000 --- a/kotlin/src/main/kotlin/supermarket/model/Discount.kt +++ /dev/null @@ -1,3 +0,0 @@ -package supermarket.model - -class Discount(val product: Product, val description: String, val discountAmount: Double) diff --git a/kotlin/src/main/kotlin/supermarket/model/Offer.kt b/kotlin/src/main/kotlin/supermarket/model/Offer.kt deleted file mode 100644 index dd2c340..0000000 --- a/kotlin/src/main/kotlin/supermarket/model/Offer.kt +++ /dev/null @@ -1,3 +0,0 @@ -package supermarket.model - -class Offer(internal var offerType: SpecialOfferType, internal val product: Product, internal var argument: Double) diff --git a/kotlin/src/main/kotlin/supermarket/model/Product.kt b/kotlin/src/main/kotlin/supermarket/model/Product.kt deleted file mode 100644 index dc2cf5c..0000000 --- a/kotlin/src/main/kotlin/supermarket/model/Product.kt +++ /dev/null @@ -1,3 +0,0 @@ -package supermarket.model - -data class Product(val name: String, val unit: ProductUnit) diff --git a/kotlin/src/main/kotlin/supermarket/model/ProductQuantity.kt b/kotlin/src/main/kotlin/supermarket/model/ProductQuantity.kt deleted file mode 100644 index 8fcc16c..0000000 --- a/kotlin/src/main/kotlin/supermarket/model/ProductQuantity.kt +++ /dev/null @@ -1,3 +0,0 @@ -package supermarket.model - -class ProductQuantity(val product: Product, val quantity: Double) diff --git a/kotlin/src/main/kotlin/supermarket/model/ProductUnit.kt b/kotlin/src/main/kotlin/supermarket/model/ProductUnit.kt deleted file mode 100644 index bdb428c..0000000 --- a/kotlin/src/main/kotlin/supermarket/model/ProductUnit.kt +++ /dev/null @@ -1,5 +0,0 @@ -package supermarket.model - -enum class ProductUnit { - Kilo, Each -} diff --git a/kotlin/src/main/kotlin/supermarket/model/Receipt.kt b/kotlin/src/main/kotlin/supermarket/model/Receipt.kt deleted file mode 100644 index f7ab78d..0000000 --- a/kotlin/src/main/kotlin/supermarket/model/Receipt.kt +++ /dev/null @@ -1,36 +0,0 @@ -package supermarket.model - -import java.util.* - -class Receipt { - private val items = ArrayList() - private val discounts = ArrayList() - - val totalPrice: Double? - get() { - var total = 0.0 - for (item in this.items) { - total += item.totalPrice - } - for (discount in this.discounts) { - total -= discount.discountAmount - } - return total - } - - fun addProduct(p: Product, quantity: Double, price: Double, totalPrice: Double) { - this.items.add(ReceiptItem(p, quantity, price, totalPrice)) - } - - fun getItems(): List { - return ArrayList(this.items) - } - - fun addDiscount(discount: Discount) { - this.discounts.add(discount) - } - - fun getDiscounts(): List { - return discounts - } -} diff --git a/kotlin/src/main/kotlin/supermarket/model/ReceiptItem.kt b/kotlin/src/main/kotlin/supermarket/model/ReceiptItem.kt deleted file mode 100644 index f714534..0000000 --- a/kotlin/src/main/kotlin/supermarket/model/ReceiptItem.kt +++ /dev/null @@ -1,3 +0,0 @@ -package supermarket.model - -data class ReceiptItem(val product: Product, val quantity: Double, val price: Double, val totalPrice: Double) diff --git a/kotlin/src/main/kotlin/supermarket/model/ShoppingCart.kt b/kotlin/src/main/kotlin/supermarket/model/ShoppingCart.kt deleted file mode 100644 index 83e7f30..0000000 --- a/kotlin/src/main/kotlin/supermarket/model/ShoppingCart.kt +++ /dev/null @@ -1,79 +0,0 @@ -package supermarket.model - -import java.util.ArrayList -import java.util.HashMap - -class ShoppingCart { - - private val items = ArrayList() - internal var productQuantities: MutableMap = HashMap() - - - internal fun getItems(): List { - return ArrayList(items) - } - - internal fun addItem(product: Product) { - this.addItemQuantity(product, 1.0) - } - - internal fun productQuantities(): Map { - return productQuantities - } - - - fun addItemQuantity(product: Product, quantity: Double) { - items.add(ProductQuantity(product, quantity)) - if (productQuantities.containsKey(product)) { - productQuantities[product] = productQuantities[product]!! + quantity - } else { - productQuantities[product] = quantity - } - } - - internal fun handleOffers(receipt: Receipt, offers: Map, catalog: SupermarketCatalog) { - for (p in productQuantities().keys) { - val quantity = productQuantities[p]!! - if (offers.containsKey(p)) { - val offer = offers[p]!! - val unitPrice = catalog.getUnitPrice(p) - val quantityAsInt = quantity.toInt() - var discount: Discount? = null - var x = 1 - if (offer.offerType === SpecialOfferType.ThreeForTwo) { - x = 3 - - } else if (offer.offerType === SpecialOfferType.TwoForAmount) { - x = 2 - if (quantityAsInt >= 2) { - val total = offer.argument * (quantityAsInt / x) + quantityAsInt % 2 * unitPrice - val discountN = unitPrice * quantity - total - discount = Discount(p, "2 for " + offer.argument, discountN) - } - - } - if (offer.offerType === SpecialOfferType.FiveForAmount) { - x = 5 - } - val numberOfXs = quantityAsInt / x - if (offer.offerType === SpecialOfferType.ThreeForTwo && quantityAsInt > 2) { - val discountAmount = - quantity * unitPrice - (numberOfXs.toDouble() * 2.0 * unitPrice + quantityAsInt % 3 * unitPrice) - discount = Discount(p, "3 for 2", discountAmount) - } - if (offer.offerType === SpecialOfferType.TenPercentDiscount) { - discount = - Discount(p, offer.argument.toString() + "% off", quantity * unitPrice * offer.argument / 100.0) - } - if (offer.offerType === SpecialOfferType.FiveForAmount && quantityAsInt >= 5) { - val discountTotal = - unitPrice * quantity - (offer.argument * numberOfXs + quantityAsInt % 5 * unitPrice) - discount = Discount(p, x.toString() + " for " + offer.argument, discountTotal) - } - if (discount != null) - receipt.addDiscount(discount) - } - - } - } -} diff --git a/kotlin/src/main/kotlin/supermarket/model/SpecialOfferType.kt b/kotlin/src/main/kotlin/supermarket/model/SpecialOfferType.kt deleted file mode 100644 index 3a379c3..0000000 --- a/kotlin/src/main/kotlin/supermarket/model/SpecialOfferType.kt +++ /dev/null @@ -1,5 +0,0 @@ -package supermarket.model - -enum class SpecialOfferType { - ThreeForTwo, TenPercentDiscount, TwoForAmount, FiveForAmount -} diff --git a/kotlin/src/main/kotlin/supermarket/model/SupermarketCatalog.kt b/kotlin/src/main/kotlin/supermarket/model/SupermarketCatalog.kt deleted file mode 100644 index 2ed75da..0000000 --- a/kotlin/src/main/kotlin/supermarket/model/SupermarketCatalog.kt +++ /dev/null @@ -1,7 +0,0 @@ -package supermarket.model - -interface SupermarketCatalog { - fun addProduct(product: Product, price: Double) - - fun getUnitPrice(product: Product): Double -} diff --git a/kotlin/src/main/kotlin/supermarket/model/Teller.kt b/kotlin/src/main/kotlin/supermarket/model/Teller.kt deleted file mode 100644 index c0d0ddf..0000000 --- a/kotlin/src/main/kotlin/supermarket/model/Teller.kt +++ /dev/null @@ -1,28 +0,0 @@ -package supermarket.model - -import java.util.HashMap - - -class Teller(private val catalog: SupermarketCatalog) { - private val offers = HashMap() - - fun addSpecialOffer(offerType: SpecialOfferType, product: Product, argument: Double) { - this.offers[product] = Offer(offerType, product, argument) - } - - fun checksOutArticlesFrom(theCart: ShoppingCart): Receipt { - val receipt = Receipt() - val productQuantities = theCart.getItems() - for (pq in productQuantities) { - val p = pq.product - val quantity = pq.quantity - val unitPrice = this.catalog.getUnitPrice(p) - val price = quantity * unitPrice - receipt.addProduct(p, quantity, unitPrice, price) - } - theCart.handleOffers(receipt, this.offers, this.catalog) - - return receipt - } - -} diff --git a/kotlin/src/test/kotlin/dojo/supermarket/model/FakeCatalog.kt b/kotlin/src/test/kotlin/dojo/supermarket/model/FakeCatalog.kt deleted file mode 100644 index 4f6baaf..0000000 --- a/kotlin/src/test/kotlin/dojo/supermarket/model/FakeCatalog.kt +++ /dev/null @@ -1,19 +0,0 @@ -package dojo.supermarket.model - -import supermarket.model.Product -import supermarket.model.SupermarketCatalog -import java.util.* - -class FakeCatalog : SupermarketCatalog { - private val products = HashMap() - private val prices = HashMap() - - override fun addProduct(product: Product, price: Double) { - this.products[product.name] = product - this.prices[product.name] = price - } - - override fun getUnitPrice(p: Product): Double { - return this.prices[p.name]!! - } -} diff --git a/kotlin/src/test/kotlin/dojo/supermarket/model/SupermarketTest.kt b/kotlin/src/test/kotlin/dojo/supermarket/model/SupermarketTest.kt deleted file mode 100644 index 99249c9..0000000 --- a/kotlin/src/test/kotlin/dojo/supermarket/model/SupermarketTest.kt +++ /dev/null @@ -1,27 +0,0 @@ -package dojo.supermarket.model - -import org.junit.jupiter.api.Test -import supermarket.model.* - -class SupermarketTest { - - @Test - fun testSomething() { - val catalog = FakeCatalog() - val toothbrush = Product("toothbrush", ProductUnit.Each) - catalog.addProduct(toothbrush, 0.99) - val apples = Product("apples", ProductUnit.Kilo) - catalog.addProduct(apples, 1.99) - - val cart = - ShoppingCart() - cart.addItemQuantity(apples, 2.5) - - val teller = Teller(catalog) - teller.addSpecialOffer(SpecialOfferType.TenPercentDiscount, toothbrush, 10.0) - - val receipt = teller.checksOutArticlesFrom(cart) - - // Todo: complete this test - } -} diff --git a/php/.gitignore b/php/.gitignore deleted file mode 100644 index 179636c..0000000 --- a/php/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/.idea -/build -/vendor -/.editorconfig -/.phpunit.result.cache -/composer.lock diff --git a/php/README.md b/php/README.md deleted file mode 100644 index 1881b29..0000000 --- a/php/README.md +++ /dev/null @@ -1,145 +0,0 @@ -# The Supermarket Receipt Refactoring Kata - PHP version - -See the [top level readme](../README.md) for general information about this exercise. This is the PHP version of The -Supermarket Receipt Refactoring Kata - -## Installation - -The project uses: - -- [PHP 8.0+](https://www.php.net/downloads.php) -- [Composer](https://getcomposer.org) - -Recommended: - -- [Git](https://git-scm.com/downloads) - -See [GitHub cloning a repository](https://help.github.com/en/articles/cloning-a-repository) for details on how to -create a local copy of this project on your computer. - -```sh -git clone git@github.com:emilybache/SupermarketReceipt-Refactoring-Kata.git -``` - -or - -```shell script -git clone https://github.com/emilybache/SupermarketReceipt-Refactoring-Kata.git -``` - -Install all the dependencies using composer: - -```sh -cd ./SupermarketReceipt-Refactoring-Kata/php -composer install -``` - -## Dependencies - -The project uses composer to install: - -- [PHPUnit](https://phpunit.de/) -- [ApprovalTests.PHP](https://github.com/approvals/ApprovalTests.php) -- [PHPStan](https://github.com/phpstan/phpstan) -- [Easy Coding Standard (ECS)](https://github.com/symplify/easy-coding-standard) - -## Folders - -- `src` - Contains the **ReceiptPrinter** Class which needs to be tested and refactored. - - `models` - Contains the 11 Classes and Interface which need to be tested and refactored. -- `tests` - Contains **SupermarketTest** which tests **ReceiptPrinter**. **FakeCatalog** is a basic implementation of - the **SupermarketCatalog** interface allowing the tests to run. - -## Testing - -PHPUnit is configured for testing, a composer script has been provided. To run the unit tests, from the root of the -project run: - -```shell script -composer tests -``` - -On Windows a batch file has been created, like an alias on Linux/Mac (e.g. `alias pu="composer test"`), the same -PHPUnit `composer test` can be run: - -```shell script -pu.bat -``` - -### Tests with Coverage Report - -To run all test and generate a html coverage report run: - -```shell script -composer test-coverage -``` - -The coverage report is created in /builds, it is best viewed by opening **index.html** in your browser. - -The [XDebug](https://xdebug.org/download) extension is required for coverage report generating. - -## Code Standard - -Easy Coding Standard (ECS) is used to check for style and code standards, -**[PSR-12](https://www.php-fig.org/psr/psr-12/)** is used. Tip: Only periodically run ECS, when tests are green, to keep -the focus on writing tests, refactoring the code and adding new features. - -### Check Code - -To check code, but not fix errors: - -```shell script -composer check-cs -``` - -On Windows a batch file has been created, like an alias on Linux/Mac (e.g. `alias cc="composer check-cs"`), the same -ECS `composer check-cs` can be run: - -```shell script -cc.bat -``` - -### Fix Code - -Many code fixes are automatically provided by ECS, if advised to run --fix, the following script can be run: - -```shell script -composer fix-cs -``` - -On Windows a batch file has been created, like an alias on Linux/Mac (e.g. `alias fc="composer fix-cs"`), the same -ECS `composer fix-cs` can be run: - -```shell script -fc.bat -``` - -## Static Analysis - -PHPStan is used to run static analysis checks. As the code is constantly being refactored only run static analysis -checks once the chapter is complete. Tip: Only periodically run PHPStan, when tests are green, to keep the focus on -writing tests, refactoring the code and adding new features. - -```shell script -composer phpstan -``` - -On Windows a batch file has been created, like an alias on Linux/Mac (e.g. `alias ps="composer phpstan"`), the same -PHPStan `composer phpstan` can be run: - -```shell script -ps.bat -``` - -## Approval Tests - -ApprovalTests.php can be used to compare the output of the **ReceiptPrinter**, see -[ApprovalTests.PHP](https://github.com/approvals/ApprovalTests.php) for more information, or see the `with_tests` -branch for working examples. - -## Start with the refactoring - -If you would like to just do the refactoring part of this exercise, you can instead check out the `with_tests` branch. -Those tests have reasonably good coverage and should support most kinds of refactorings you'd like to do. - -**Happy coding**! diff --git a/php/cc.bat b/php/cc.bat deleted file mode 100644 index fa81b65..0000000 --- a/php/cc.bat +++ /dev/null @@ -1 +0,0 @@ -composer check-cs diff --git a/php/composer.json b/php/composer.json deleted file mode 100644 index 2baafa8..0000000 --- a/php/composer.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "emilybache/supermarket-receipt-refactoring-kata", - "description": "The Supermarket Receipt Refactoring Kata - PHP version", - "type": "project", - "license": "MIT", - "require": { - "php": "^8.0", - "myclabs/php-enum": "^1.7", - "php-ds/php-ds": "^1.2" - }, - "require-dev": { - "approvals/approval-tests": "dev-Main", - "phpunit/phpunit": "^9.5", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-phpunit": "^1.3", - "symplify/easy-coding-standard": "^11.1", - "symplify/phpstan-extensions": "^11.1" - }, - "suggest": { - "ext-ds": "*" - }, - "autoload": { - "psr-4": { - "Supermarket\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Tests\\": "tests/" - } - }, - "scripts": { - "tests": "phpunit", - "test-coverage": "phpunit --coverage-html build/coverage", - "check-cs": "ecs check", - "fix-cs": "ecs check --fix", - "phpstan": "phpstan analyse --ansi" - } -} diff --git a/php/ecs.php b/php/ecs.php deleted file mode 100644 index ef649b1..0000000 --- a/php/ecs.php +++ /dev/null @@ -1,47 +0,0 @@ -paths([ - __DIR__ . '/src', - __DIR__ . '/tests', - __DIR__ . '/ecs.php', // check this file too! - ]); - - $ecsConfig->skip([ - // skip specific rules - ]); - - // run and fix, one by one - $ecsConfig->sets([ - SetList::SPACES, - SetList::ARRAY, - SetList::DOCBLOCK, - SetList::NAMESPACES, - SetList::CONTROL_STRUCTURES, - SetList::CLEAN_CODE, - SetList::STRICT, - SetList::PSR_12, - SetList::PHPUNIT, - ]); - - // add declare(strict_types=1); to all php files: - $ecsConfig->rule(DeclareStrictTypesFixer::class); - - // change $array = array(); to $array = []; - $ecsConfig->ruleWithConfiguration(ArraySyntaxFixer::class, [ - 'syntax' => 'short', - ]); - - // [default: PHP_EOL]; other options: "\n" - $ecsConfig->lineEnding("\n"); -}; diff --git a/php/fc.bat b/php/fc.bat deleted file mode 100644 index c571652..0000000 --- a/php/fc.bat +++ /dev/null @@ -1 +0,0 @@ -composer fix-cs diff --git a/php/phpstan.neon b/php/phpstan.neon deleted file mode 100644 index e8f556a..0000000 --- a/php/phpstan.neon +++ /dev/null @@ -1,34 +0,0 @@ -includes: - - vendor/symplify/phpstan-extensions/config/config.neon - - vendor/phpstan/phpstan-phpunit/extension.neon - -parameters: - paths: - - src - - tests - - # max is the highest level - level: max - - # Ignore Enum classes: - excludePaths: - - src\Model\SpecialOfferType.php - - src\Model\ProductUnit.php - - ignoreErrors: - # Magic method is used is simulate enum - - '#Call to an undefined static method#' - - - message: "#^Property Tests\\\\FakeCatalog\\:\\:\\$products is never read, only written\\.$#" - count: 1 - path: tests/FakeCatalog.php - - # buggy - - # mixed - - # cache buggy - - # tests - - # iterable diff --git a/php/phpunit.xml b/php/phpunit.xml deleted file mode 100644 index b8dd1d4..0000000 --- a/php/phpunit.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - ./src - - - - - ./tests - - - diff --git a/php/ps.bat b/php/ps.bat deleted file mode 100644 index 8bda091..0000000 --- a/php/ps.bat +++ /dev/null @@ -1 +0,0 @@ -composer phpstan diff --git a/php/pu.bat b/php/pu.bat deleted file mode 100644 index 38a0c5e..0000000 --- a/php/pu.bat +++ /dev/null @@ -1 +0,0 @@ -composer tests diff --git a/php/src/Model/Discount.php b/php/src/Model/Discount.php deleted file mode 100644 index 9ad0c5a..0000000 --- a/php/src/Model/Discount.php +++ /dev/null @@ -1,30 +0,0 @@ -description; - } - - public function getDiscountAmount(): float - { - return $this->discount; - } - - public function getProduct(): Product - { - return $this->product; - } -} diff --git a/php/src/Model/Offer.php b/php/src/Model/Offer.php deleted file mode 100644 index db261f8..0000000 --- a/php/src/Model/Offer.php +++ /dev/null @@ -1,30 +0,0 @@ -argument; - } - - public function getProduct(): Product - { - return $this->product; - } - - public function getOfferType(): SpecialOfferType - { - return $this->offerType; - } -} diff --git a/php/src/Model/Product.php b/php/src/Model/Product.php deleted file mode 100644 index 33e6765..0000000 --- a/php/src/Model/Product.php +++ /dev/null @@ -1,41 +0,0 @@ -name; - } - - public function getUnit(): ProductUnit - { - return $this->unit; - } - - /** - * @param Product $obj - */ - public function equals($obj): bool - { - return $obj instanceof self && - $this->getName() === $obj->getName() && - $this->getUnit() === $obj->getUnit(); - } - - public function hash(): string - { - return "{$this->getName()}__{$this->getUnit()}"; - } -} diff --git a/php/src/Model/ProductQuantity.php b/php/src/Model/ProductQuantity.php deleted file mode 100644 index 0c31402..0000000 --- a/php/src/Model/ProductQuantity.php +++ /dev/null @@ -1,24 +0,0 @@ -product; - } - - public function getQuantity(): float - { - return $this->quantity; - } -} diff --git a/php/src/Model/ProductUnit.php b/php/src/Model/ProductUnit.php deleted file mode 100644 index b0d3fb4..0000000 --- a/php/src/Model/ProductUnit.php +++ /dev/null @@ -1,22 +0,0 @@ -items as $item) { - $total += $item->getTotalPrice(); - } - foreach ($this->discounts as $discount) { - $total += $discount->getDiscountAmount(); - } - return $total; - } - - public function addProduct(Product $product, float $quantity, float $price, float $totalPrice): void - { - $this->items[] = new ReceiptItem($product, $quantity, $price, $totalPrice); - } - - /** - * @return ReceiptItem[] - */ - public function getItems(): array - { - return $this->items; - } - - public function addDiscount(Discount $discount): void - { - $this->discounts[] = $discount; - } - - /** - * @return Discount[] - */ - public function getDiscounts(): array - { - return $this->discounts; - } -} diff --git a/php/src/Model/ReceiptItem.php b/php/src/Model/ReceiptItem.php deleted file mode 100644 index b55f0e2..0000000 --- a/php/src/Model/ReceiptItem.php +++ /dev/null @@ -1,36 +0,0 @@ -product; - } - - public function getQuantity(): float - { - return $this->quantity; - } - - public function getPrice(): float - { - return $this->price; - } - - public function getTotalPrice(): float - { - return $this->totalPrice; - } -} diff --git a/php/src/Model/ShoppingCart.php b/php/src/Model/ShoppingCart.php deleted file mode 100644 index d2f6c78..0000000 --- a/php/src/Model/ShoppingCart.php +++ /dev/null @@ -1,105 +0,0 @@ - - */ - private Map $productQuantities; - - public function __construct() - { - $this->productQuantities = new Map(); - } - - public function addItem(Product $product): void - { - $this->addItemQuantity($product, 1.0); - } - - /** - * @return ProductQuantity[] - */ - public function getItems(): array - { - return $this->items; - } - - public function addItemQuantity(Product $product, float $quantity): void - { - $this->items[] = new ProductQuantity($product, $quantity); - if ($this->productQuantities->hasKey($product)) { - $newAmount = $this->productQuantities[$product] + $quantity; - $this->productQuantities[$product] = $newAmount; - } else { - $this->productQuantities[$product] = $quantity; - } - } - - /** - * @param Map $offers - */ - public function handleOffers(Receipt $receipt, Map $offers, SupermarketCatalog $catalog): void - { - /** - * @var Product $p - * @var float $quantity - */ - foreach ($this->productQuantities as $p => $quantity) { - $quantityAsInt = (int) $quantity; - if ($offers->hasKey($p)) { - /** @var Offer $offer */ - $offer = $offers[$p]; - $unitPrice = $catalog->getUnitPrice($p); - $discount = null; - $x = 1; - if ($offer->getOfferType()->equals(SpecialOfferType::THREE_FOR_TWO())) { - $x = 3; - } elseif ($offer->getOfferType()->equals(SpecialOfferType::TWO_FOR_AMOUNT())) { - $x = 2; - if ($quantityAsInt >= 2) { - $total = $offer->getArgument() * intdiv($quantityAsInt, $x) + $quantityAsInt % 2 * $unitPrice; - $discountN = $unitPrice * $quantity - $total; - $discount = new Discount($p, "2 for {$offer->getArgument()}", -1 * $discountN); - } - } - - if ($offer->getOfferType()->equals(SpecialOfferType::FIVE_FOR_AMOUNT())) { - $x = 5; - } - $numberOfXs = intdiv($quantityAsInt, $x); - if ($offer->getOfferType()->equals(SpecialOfferType::THREE_FOR_TWO()) && $quantityAsInt > 2) { - $discountAmount = $quantity * $unitPrice - ($numberOfXs * 2 * $unitPrice + $quantityAsInt % 3 * $unitPrice); - $discount = new Discount($p, '3 for 2', -$discountAmount); - } - - if ($offer->getOfferType()->equals(SpecialOfferType::TEN_PERCENT_DISCOUNT())) { - $discount = new Discount( - $p, - "{$offer->getArgument()}% off", - -$quantity * $unitPrice * $offer->getArgument() / 100.0 - ); - } - if ($offer->getOfferType()->equals(SpecialOfferType::FIVE_FOR_AMOUNT()) && $quantityAsInt >= 5) { - $discountTotal = $unitPrice * $quantity - ($offer->getArgument() * $numberOfXs + $quantityAsInt % 5 * $unitPrice); - $discount = new Discount($p, "${x} for {$offer->getArgument()}", -$discountTotal); - } - - if ($discount !== null) { - $receipt->addDiscount($discount); - } - } - } - } -} diff --git a/php/src/Model/SpecialOfferType.php b/php/src/Model/SpecialOfferType.php deleted file mode 100644 index 3c89bbc..0000000 --- a/php/src/Model/SpecialOfferType.php +++ /dev/null @@ -1,28 +0,0 @@ - - */ - private Map $offers; - - public function __construct( - private SupermarketCatalog $catalog - ) { - $this->offers = new Map(); - } - - public function addSpecialOffer(SpecialOfferType $offerType, Product $product, float $argument): void - { - $this->offers[$product] = new Offer($offerType, $product, $argument); - } - - public function checkoutArticlesFrom(ShoppingCart $cart): Receipt - { - $receipt = new Receipt(); - $productQuantities = $cart->getItems(); - foreach ($productQuantities as $pq) { - $p = $pq->getProduct(); - $quantity = $pq->getQuantity(); - $unitPrice = $this->catalog->getUnitPrice($p); - $price = $quantity * $unitPrice; - $receipt->addProduct($p, $quantity, $unitPrice, $price); - } - - $cart->handleOffers($receipt, $this->offers, $this->catalog); - - return $receipt; - } -} diff --git a/php/src/ReceiptPrinter.php b/php/src/ReceiptPrinter.php deleted file mode 100644 index 2aca856..0000000 --- a/php/src/ReceiptPrinter.php +++ /dev/null @@ -1,82 +0,0 @@ -getItems() as $item) { - $itemPresentation = $this->presentReceiptItem($item); - $result .= $itemPresentation; - } - - foreach ($receipt->getDiscounts() as $discount) { - $discountPresentation = $this->presentDiscount($discount); - $result .= $discountPresentation; - } - - $result .= "\n"; - $result .= $this->presentTotal($receipt); - return $result; - } - - protected function presentReceiptItem(ReceiptItem $item): string - { - $price = self::presentPrice($item->getTotalPrice()); - $name = $item->getProduct()->getName(); - - $line = $this->formatLineWithWhitespace($name, $price) . "\n"; - - if ($item->getQuantity() !== 1.0) { - $line .= ' ' . self::presentPrice($item->getPrice()) . ' * ' . self::presentQuantity($item) . "\n"; - } - return $line; - } - - protected function presentDiscount(Discount $discount): string - { - $name = "{$discount->getDescription()}({$discount->getProduct()->getName()})"; - $value = self::presentPrice($discount->getDiscountAmount()); - - return $this->formatLineWithWhitespace($name, $value) . "\n"; - } - - protected function presentTotal(Receipt $receipt): string - { - $name = 'Total: '; - $value = self::presentPrice($receipt->getTotalPrice()); - return $this->formatLineWithWhitespace($name, $value); - } - - protected function formatLineWithWhitespace(string $name, string $value): string - { - $whitespaceSize = $this->columns - strlen($name) - strlen($value); - return $name . str_repeat(' ', $whitespaceSize) . $value; - } - - protected static function presentPrice(float $price): string - { - return sprintf('%.2F', $price); - } - - private static function presentQuantity(ReceiptItem $item): string - { - return $item->getProduct()->getUnit()->equals(ProductUnit::EACH()) ? - sprintf('%x', $item->getQuantity()) : - sprintf('%.3F', $item->getQuantity()); - } -} diff --git a/php/tests/FakeCatalog.php b/php/tests/FakeCatalog.php deleted file mode 100644 index a6da8ac..0000000 --- a/php/tests/FakeCatalog.php +++ /dev/null @@ -1,32 +0,0 @@ - - */ - private array $prices = []; - - /** - * @var array - */ - private array $products = []; - - public function addProduct(Product $product, float $price): void - { - $this->products[$product->getName()] = $product; - $this->prices[$product->getName()] = $price; - } - - public function getUnitPrice(Product $product): float - { - return $this->prices[$product->getName()]; - } -} diff --git a/php/tests/SupermarketTest.php b/php/tests/SupermarketTest.php deleted file mode 100644 index d0fa89d..0000000 --- a/php/tests/SupermarketTest.php +++ /dev/null @@ -1,44 +0,0 @@ -addProduct($toothbrush, 0.99); - $apples = new Product('apples', ProductUnit::KILO()); - $catalog->addProduct($apples, 1.99); - - $cart = new ShoppingCart(); - $cart->addItemQuantity($apples, 2.5); - - $teller = new Teller($catalog); - $teller->addSpecialOffer(SpecialOfferType::TEN_PERCENT_DISCOUNT(), $toothbrush, 10.0); - - // Act - $receipt = $teller->checkoutArticlesFrom($cart); - - // Assert - self::assertSame(4.975, $receipt->getTotalPrice()); - self::assertSame([], $receipt->getDiscounts()); - self::assertCount(1, $receipt->getItems()); - $receiptItem = $receipt->getItems()[0]; - self::assertSame($apples, $receiptItem->getProduct()); - self::assertSame(1.99, $receiptItem->getPrice()); - self::assertSame(2.5 * 1.99, $receiptItem->getTotalPrice()); - self::assertSame(2.5, $receiptItem->getQuantity()); - } -} diff --git a/python/.gitignore b/python/.gitignore deleted file mode 100644 index e48c9ad..0000000 --- a/python/.gitignore +++ /dev/null @@ -1,78 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ -.pytest_cache - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask instance folder -instance/ - -# Sphinx documentation -docs/_build/ - -# MkDocs documentation -/site/ - -# PyBuilder -target/ - -# IPython Notebook -.ipynb_checkpoints - -# pyenv -.python-version -venv -.venv - -.idea -.vscode diff --git a/python/README.md b/python/README.md deleted file mode 100644 index 30f7d56..0000000 --- a/python/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Supermarket Receipt in [Python](https://www.python.org/) - -## Setup - -* Have Python installed -* Clone the repository -* On the command line, enter the `SupermarketReceipt-Refactoring-Kata/python` directory -* On the command line, install requirements, e.g. on the`python -m pip install -r requirements.txt` - -## Running Tests - -On the command line, enter the `SupermarketReceipt-Refactoring-Kata/python` directory and run - -``` -pytest -``` - -## Optional: Running [TextTest](https://www.texttest.org/) Tests - -Install TextTest according to the [instructions](https://www.texttest.org/index.html#getting-started-with-texttest) (platform specific). - -On the command line, enter the `SupermarketReceipt-Refactoring-Kata/python` directory and run - -``` -texttest -a sr -d . -``` diff --git a/python/catalog.py b/python/catalog.py deleted file mode 100644 index e522c8b..0000000 --- a/python/catalog.py +++ /dev/null @@ -1,9 +0,0 @@ - -class SupermarketCatalog: - - def add_product(self, product, price): - raise Exception("cannot be called from a unit test - it accesses the database") - - def unit_price(self, product): - raise Exception("cannot be called from a unit test - it accesses the database") - diff --git a/python/model_objects.py b/python/model_objects.py deleted file mode 100644 index 2d8d0ad..0000000 --- a/python/model_objects.py +++ /dev/null @@ -1,38 +0,0 @@ -from enum import Enum - - -class Product: - def __init__(self, name, unit): - self.name = name - self.unit = unit - - -class ProductQuantity: - def __init__(self, product, quantity): - self.product = product - self.quantity = quantity - - -class ProductUnit(Enum): - EACH = 1 - KILO = 2 - - -class SpecialOfferType(Enum): - THREE_FOR_TWO = 1 - TEN_PERCENT_DISCOUNT = 2 - TWO_FOR_AMOUNT = 3 - FIVE_FOR_AMOUNT = 4 - -class Offer: - def __init__(self, offer_type, product, argument): - self.offer_type = offer_type - self.product = product - self.argument = argument - - -class Discount: - def __init__(self, product, description, discount_amount): - self.product = product - self.description = description - self.discount_amount = discount_amount diff --git a/python/receipt.py b/python/receipt.py deleted file mode 100644 index 2a0a219..0000000 --- a/python/receipt.py +++ /dev/null @@ -1,35 +0,0 @@ - -class ReceiptItem: - def __init__(self, product, quantity, price, total_price): - self.product = product - self.quantity = quantity - self.price = price - self.total_price = total_price - - -class Receipt: - def __init__(self): - self._items = [] - self._discounts = [] - - def total_price(self): - total = 0 - for item in self.items: - total += item.total_price - for discount in self.discounts: - total += discount.discount_amount - return total - - def add_product(self, product, quantity, price, total_price): - self._items.append(ReceiptItem(product, quantity, price, total_price)) - - def add_discount(self, discount): - self._discounts.append(discount) - - @property - def items(self): - return self._items[:] - - @property - def discounts(self): - return self._discounts[:] diff --git a/python/receipt_printer.py b/python/receipt_printer.py deleted file mode 100644 index 7550fe9..0000000 --- a/python/receipt_printer.py +++ /dev/null @@ -1,56 +0,0 @@ -from model_objects import ProductUnit - -class ReceiptPrinter: - - def __init__(self, columns=40): - self.columns = columns - - def print_receipt(self, receipt): - result = "" - for item in receipt.items: - receipt_item = self.print_receipt_item(item) - result += receipt_item - - for discount in receipt.discounts: - discount_presentation = self.print_discount(discount) - result += discount_presentation - - result += "\n" - result += self.present_total(receipt) - return str(result) - - def print_receipt_item(self, item): - total_price_printed = self.print_price(item.total_price) - name = item.product.name - line = self.format_line_with_whitespace(name, total_price_printed) - if item.quantity != 1: - line += f" {self.print_price(item.price)} * {self.print_quantity(item)}\n" - return line - - def format_line_with_whitespace(self, name, value): - line = name - whitespace_size = self.columns - len(name) - len(value) - for i in range(whitespace_size): - line += " " - line += value - line += "\n" - return line - - def print_price(self, price): - return "%.2f" % price - - def print_quantity(self, item): - if ProductUnit.EACH == item.product.unit: - return str(item.quantity) - else: - return '%.3f' % item.quantity - - def print_discount(self, discount): - name = f"{discount.description} ({discount.product.name})" - value = self.print_price(discount.discount_amount) - return self.format_line_with_whitespace(name, value) - - def present_total(self, receipt): - name = "Total: " - value = self.print_price(receipt.total_price()) - return self.format_line_with_whitespace(name, value) diff --git a/python/requirements.txt b/python/requirements.txt deleted file mode 100644 index 20fb255..0000000 --- a/python/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -approvaltests -python-dateutil -pytest-approvaltests - diff --git a/python/shopping_cart.py b/python/shopping_cart.py deleted file mode 100644 index 4e36cc5..0000000 --- a/python/shopping_cart.py +++ /dev/null @@ -1,68 +0,0 @@ -import math - -from model_objects import ProductQuantity, SpecialOfferType, Discount - - -class ShoppingCart: - - def __init__(self): - self._items = [] - self._product_quantities = {} - - @property - def items(self): - return self._items - - def add_item(self, product): - self.add_item_quantity(product, 1.0) - - @property - def product_quantities(self): - return self._product_quantities - - def add_item_quantity(self, product, quantity): - self._items.append(ProductQuantity(product, quantity)) - if product in self._product_quantities.keys(): - self._product_quantities[product] = self._product_quantities[product] + quantity - else: - self._product_quantities[product] = quantity - - def handle_offers(self, receipt, offers, catalog): - for p in self._product_quantities.keys(): - quantity = self._product_quantities[p] - if p in offers.keys(): - offer = offers[p] - unit_price = catalog.unit_price(p) - quantity_as_int = int(quantity) - discount = None - x = 1 - if offer.offer_type == SpecialOfferType.THREE_FOR_TWO: - x = 3 - - elif offer.offer_type == SpecialOfferType.TWO_FOR_AMOUNT: - x = 2 - if quantity_as_int >= 2: - total = offer.argument * (quantity_as_int / x) + quantity_as_int % 2 * unit_price - discount_n = unit_price * quantity - total - discount = Discount(p, "2 for " + str(offer.argument), -discount_n) - - if offer.offer_type == SpecialOfferType.FIVE_FOR_AMOUNT: - x = 5 - - number_of_x = math.floor(quantity_as_int / x) - if offer.offer_type == SpecialOfferType.THREE_FOR_TWO and quantity_as_int > 2: - discount_amount = quantity * unit_price - ( - (number_of_x * 2 * unit_price) + quantity_as_int % 3 * unit_price) - discount = Discount(p, "3 for 2", -discount_amount) - - if offer.offer_type == SpecialOfferType.TEN_PERCENT_DISCOUNT: - discount = Discount(p, str(offer.argument) + "% off", - -quantity * unit_price * offer.argument / 100.0) - - if offer.offer_type == SpecialOfferType.FIVE_FOR_AMOUNT and quantity_as_int >= 5: - discount_total = unit_price * quantity - ( - offer.argument * number_of_x + quantity_as_int % 5 * unit_price) - discount = Discount(p, str(x) + " for " + str(offer.argument), -discount_total) - - if discount: - receipt.add_discount(discount) diff --git a/python/teller.py b/python/teller.py deleted file mode 100644 index 8bfdbe8..0000000 --- a/python/teller.py +++ /dev/null @@ -1,26 +0,0 @@ -from model_objects import Offer -from receipt import Receipt - - -class Teller: - - def __init__(self, catalog): - self.catalog = catalog - self.offers = {} - - def add_special_offer(self, offer_type, product, argument): - self.offers[product] = Offer(offer_type, product, argument) - - def checks_out_articles_from(self, the_cart): - receipt = Receipt() - product_quantities = the_cart.items - for pq in product_quantities: - p = pq.product - quantity = pq.quantity - unit_price = self.catalog.unit_price(p) - price = quantity * unit_price - receipt.add_product(p, quantity, unit_price, price) - - the_cart.handle_offers(receipt, self.offers, self.catalog) - - return receipt diff --git a/python/tests/approvaltests_config.json b/python/tests/approvaltests_config.json deleted file mode 100644 index 550e664..0000000 --- a/python/tests/approvaltests_config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "subdirectory": "approved_files" -} diff --git a/python/tests/fake_catalog.py b/python/tests/fake_catalog.py deleted file mode 100644 index 86e5811..0000000 --- a/python/tests/fake_catalog.py +++ /dev/null @@ -1,15 +0,0 @@ -from catalog import SupermarketCatalog - - -class FakeCatalog(SupermarketCatalog): - def __init__(self): - self.products = {} - self.prices = {} - - def add_product(self, product, price): - self.products[product.name] = product - self.prices[product.name] = price - - def unit_price(self, product): - return self.prices[product.name] - diff --git a/python/tests/test_supermarket.py b/python/tests/test_supermarket.py deleted file mode 100644 index 3fafd09..0000000 --- a/python/tests/test_supermarket.py +++ /dev/null @@ -1,32 +0,0 @@ -import pytest - -from model_objects import Product, SpecialOfferType, ProductUnit -from shopping_cart import ShoppingCart -from teller import Teller -from tests.fake_catalog import FakeCatalog - - -def test_ten_percent_discount(): - catalog = FakeCatalog() - toothbrush = Product("toothbrush", ProductUnit.EACH) - catalog.add_product(toothbrush, 0.99) - - apples = Product("apples", ProductUnit.KILO) - catalog.add_product(apples, 1.99) - - teller = Teller(catalog) - teller.add_special_offer(SpecialOfferType.TEN_PERCENT_DISCOUNT, toothbrush, 10.0) - - cart = ShoppingCart() - cart.add_item_quantity(apples, 2.5) - - receipt = teller.checks_out_articles_from(cart) - - assert 4.975 == pytest.approx(receipt.total_price(), 0.01) - assert [] == receipt.discounts - assert 1 == len(receipt.items) - receipt_item = receipt.items[0] - assert apples == receipt_item.product - assert 1.99 == receipt_item.price - assert 2.5 * 1.99 == pytest.approx(receipt_item.total_price, 0.01) - assert 2.5 == receipt_item.quantity diff --git a/python_pytest/.gitignore b/python_pytest/.gitignore deleted file mode 100644 index 183fea0..0000000 --- a/python_pytest/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -venv -.pytest_cache -__pycache__ -.idea diff --git a/python_pytest/README.md b/python_pytest/README.md deleted file mode 100644 index 92fe52d..0000000 --- a/python_pytest/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Supermarket Receipt - -You are working on the software for a supermarket, in particular for the teller machine that cashiers use to calculate the price of a shopping cart full of items and give the customer a receipt. The supermarket has a catalog of products for sale at various prices. Normally the price of a shopping cart is the sum of the prices of all the items in it. However, at any given time there might be special offers and price reductions on particular products. For example: - -- 10% discount -- 3 for the price of 2 -- 2 items for a reduced price -- 5 items for a reduced price - -The starting position for this exercise contains the code for setting up the Teller object, a catalog of products, the shopping cart, and any special offers. It can calculate the price of a shopping cart and generate a receipt, but so far there aren't many test cases. - -## Setup -- make a venv -- install requirements, e.g. `python -m pip install -r requirements.txt` -- use pytest to run the tests diff --git a/python_pytest/requirements.txt b/python_pytest/requirements.txt deleted file mode 100644 index feb5793..0000000 --- a/python_pytest/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -pytest -approvaltests -pytest-approvaltests -coverage diff --git a/python_pytest/src/catalog.py b/python_pytest/src/catalog.py deleted file mode 100644 index e522c8b..0000000 --- a/python_pytest/src/catalog.py +++ /dev/null @@ -1,9 +0,0 @@ - -class SupermarketCatalog: - - def add_product(self, product, price): - raise Exception("cannot be called from a unit test - it accesses the database") - - def unit_price(self, product): - raise Exception("cannot be called from a unit test - it accesses the database") - diff --git a/python_pytest/src/model_objects.py b/python_pytest/src/model_objects.py deleted file mode 100644 index 2d8d0ad..0000000 --- a/python_pytest/src/model_objects.py +++ /dev/null @@ -1,38 +0,0 @@ -from enum import Enum - - -class Product: - def __init__(self, name, unit): - self.name = name - self.unit = unit - - -class ProductQuantity: - def __init__(self, product, quantity): - self.product = product - self.quantity = quantity - - -class ProductUnit(Enum): - EACH = 1 - KILO = 2 - - -class SpecialOfferType(Enum): - THREE_FOR_TWO = 1 - TEN_PERCENT_DISCOUNT = 2 - TWO_FOR_AMOUNT = 3 - FIVE_FOR_AMOUNT = 4 - -class Offer: - def __init__(self, offer_type, product, argument): - self.offer_type = offer_type - self.product = product - self.argument = argument - - -class Discount: - def __init__(self, product, description, discount_amount): - self.product = product - self.description = description - self.discount_amount = discount_amount diff --git a/python_pytest/src/receipt.py b/python_pytest/src/receipt.py deleted file mode 100644 index 2a0a219..0000000 --- a/python_pytest/src/receipt.py +++ /dev/null @@ -1,35 +0,0 @@ - -class ReceiptItem: - def __init__(self, product, quantity, price, total_price): - self.product = product - self.quantity = quantity - self.price = price - self.total_price = total_price - - -class Receipt: - def __init__(self): - self._items = [] - self._discounts = [] - - def total_price(self): - total = 0 - for item in self.items: - total += item.total_price - for discount in self.discounts: - total += discount.discount_amount - return total - - def add_product(self, product, quantity, price, total_price): - self._items.append(ReceiptItem(product, quantity, price, total_price)) - - def add_discount(self, discount): - self._discounts.append(discount) - - @property - def items(self): - return self._items[:] - - @property - def discounts(self): - return self._discounts[:] diff --git a/python_pytest/src/shopping_cart.py b/python_pytest/src/shopping_cart.py deleted file mode 100644 index 4e36cc5..0000000 --- a/python_pytest/src/shopping_cart.py +++ /dev/null @@ -1,68 +0,0 @@ -import math - -from model_objects import ProductQuantity, SpecialOfferType, Discount - - -class ShoppingCart: - - def __init__(self): - self._items = [] - self._product_quantities = {} - - @property - def items(self): - return self._items - - def add_item(self, product): - self.add_item_quantity(product, 1.0) - - @property - def product_quantities(self): - return self._product_quantities - - def add_item_quantity(self, product, quantity): - self._items.append(ProductQuantity(product, quantity)) - if product in self._product_quantities.keys(): - self._product_quantities[product] = self._product_quantities[product] + quantity - else: - self._product_quantities[product] = quantity - - def handle_offers(self, receipt, offers, catalog): - for p in self._product_quantities.keys(): - quantity = self._product_quantities[p] - if p in offers.keys(): - offer = offers[p] - unit_price = catalog.unit_price(p) - quantity_as_int = int(quantity) - discount = None - x = 1 - if offer.offer_type == SpecialOfferType.THREE_FOR_TWO: - x = 3 - - elif offer.offer_type == SpecialOfferType.TWO_FOR_AMOUNT: - x = 2 - if quantity_as_int >= 2: - total = offer.argument * (quantity_as_int / x) + quantity_as_int % 2 * unit_price - discount_n = unit_price * quantity - total - discount = Discount(p, "2 for " + str(offer.argument), -discount_n) - - if offer.offer_type == SpecialOfferType.FIVE_FOR_AMOUNT: - x = 5 - - number_of_x = math.floor(quantity_as_int / x) - if offer.offer_type == SpecialOfferType.THREE_FOR_TWO and quantity_as_int > 2: - discount_amount = quantity * unit_price - ( - (number_of_x * 2 * unit_price) + quantity_as_int % 3 * unit_price) - discount = Discount(p, "3 for 2", -discount_amount) - - if offer.offer_type == SpecialOfferType.TEN_PERCENT_DISCOUNT: - discount = Discount(p, str(offer.argument) + "% off", - -quantity * unit_price * offer.argument / 100.0) - - if offer.offer_type == SpecialOfferType.FIVE_FOR_AMOUNT and quantity_as_int >= 5: - discount_total = unit_price * quantity - ( - offer.argument * number_of_x + quantity_as_int % 5 * unit_price) - discount = Discount(p, str(x) + " for " + str(offer.argument), -discount_total) - - if discount: - receipt.add_discount(discount) diff --git a/python_pytest/src/teller.py b/python_pytest/src/teller.py deleted file mode 100644 index cc123fa..0000000 --- a/python_pytest/src/teller.py +++ /dev/null @@ -1,29 +0,0 @@ -from model_objects import Offer -from receipt import Receipt - - -class Teller: - - def __init__(self, catalog): - self.catalog = catalog - self.offers = {} - - def add_special_offer(self, offer_type, product, argument): - self.offers[product] = Offer(offer_type, product, argument) - - def checks_out_articles_from(self, the_cart): - receipt = Receipt() - product_quantities = the_cart.items - for pq in product_quantities: - p = pq.product - quantity = pq.quantity - unit_price = self.catalog.unit_price(p) - price = quantity * unit_price - receipt.add_product(p, quantity, unit_price, price) - - the_cart.handle_offers(receipt, self.offers, self.catalog) - - return receipt - - def product_with_name(self, name): - return self.catalog.products.get(name, None) \ No newline at end of file diff --git a/python_pytest/src/texttest_fixture.py b/python_pytest/src/texttest_fixture.py deleted file mode 100644 index 229bf4e..0000000 --- a/python_pytest/src/texttest_fixture.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -Start texttest from a command prompt in the same folder as this file with this command: - -texttest -a sr -d . -""" - -import sys,csv -from pathlib import Path - -from model_objects import Product, SpecialOfferType, ProductUnit -from receipt_printer import ReceiptPrinter -from shopping_cart import ShoppingCart -from teller import Teller -from tests.fake_catalog import FakeCatalog - - -def read_catalog(catalog_file): - catalog = FakeCatalog() - if not catalog_file.exists(): - return catalog - with open(catalog_file, "r") as f: - reader = csv.DictReader(f) - for row in reader: - name = row['name'] - unit = ProductUnit[row['unit']] - price = float(row['price']) - product = Product(name, unit) - catalog.add_product(product, price) - return catalog - - -def read_offers(offers_file, teller): - if not offers_file.exists(): - return - with open(offers_file, "r") as f: - reader = csv.DictReader(f) - for row in reader: - name = row['name'] - offerType = SpecialOfferType[row['offer']] - argument = float(row['argument']) - product = teller.product_with_name(name) - teller.add_special_offer(offerType, product, argument) - - -def read_basket(cart_file, catalog): - cart = ShoppingCart() - if not cart_file.exists(): - return cart - with open(cart_file, "r") as f: - reader = csv.DictReader(f) - for row in reader: - name = row['name'] - quantity = float(row['quantity']) - product = catalog.products[name] - cart.add_item_quantity(product, quantity) - return cart - - -def main(args): - catalog = read_catalog(Path("catalog.csv")) - teller = Teller(catalog) - read_offers(Path("offers.csv"), teller) - basket = read_basket(Path("cart.csv"), catalog) - receipt = teller.checks_out_articles_from(basket) - print(ReceiptPrinter().print_receipt(receipt)) - - -if __name__ == "__main__": - main(sys.argv[1:]) \ No newline at end of file diff --git a/python_pytest/tests/approvaltests_config.json b/python_pytest/tests/approvaltests_config.json deleted file mode 100644 index 550e664..0000000 --- a/python_pytest/tests/approvaltests_config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "subdirectory": "approved_files" -} diff --git a/python_pytest/tests/fake_catalog.py b/python_pytest/tests/fake_catalog.py deleted file mode 100644 index 86e5811..0000000 --- a/python_pytest/tests/fake_catalog.py +++ /dev/null @@ -1,15 +0,0 @@ -from catalog import SupermarketCatalog - - -class FakeCatalog(SupermarketCatalog): - def __init__(self): - self.products = {} - self.prices = {} - - def add_product(self, product, price): - self.products[product.name] = product - self.prices[product.name] = price - - def unit_price(self, product): - return self.prices[product.name] - diff --git a/python_pytest/tests/receipt_printer.py b/python_pytest/tests/receipt_printer.py deleted file mode 100644 index 7550fe9..0000000 --- a/python_pytest/tests/receipt_printer.py +++ /dev/null @@ -1,56 +0,0 @@ -from model_objects import ProductUnit - -class ReceiptPrinter: - - def __init__(self, columns=40): - self.columns = columns - - def print_receipt(self, receipt): - result = "" - for item in receipt.items: - receipt_item = self.print_receipt_item(item) - result += receipt_item - - for discount in receipt.discounts: - discount_presentation = self.print_discount(discount) - result += discount_presentation - - result += "\n" - result += self.present_total(receipt) - return str(result) - - def print_receipt_item(self, item): - total_price_printed = self.print_price(item.total_price) - name = item.product.name - line = self.format_line_with_whitespace(name, total_price_printed) - if item.quantity != 1: - line += f" {self.print_price(item.price)} * {self.print_quantity(item)}\n" - return line - - def format_line_with_whitespace(self, name, value): - line = name - whitespace_size = self.columns - len(name) - len(value) - for i in range(whitespace_size): - line += " " - line += value - line += "\n" - return line - - def print_price(self, price): - return "%.2f" % price - - def print_quantity(self, item): - if ProductUnit.EACH == item.product.unit: - return str(item.quantity) - else: - return '%.3f' % item.quantity - - def print_discount(self, discount): - name = f"{discount.description} ({discount.product.name})" - value = self.print_price(discount.discount_amount) - return self.format_line_with_whitespace(name, value) - - def present_total(self, receipt): - name = "Total: " - value = self.print_price(receipt.total_price()) - return self.format_line_with_whitespace(name, value) diff --git a/python_pytest/tests/test_supermarket.py b/python_pytest/tests/test_supermarket.py deleted file mode 100644 index 7834b18..0000000 --- a/python_pytest/tests/test_supermarket.py +++ /dev/null @@ -1,31 +0,0 @@ -import pytest - -from model_objects import Product, SpecialOfferType, ProductUnit -from shopping_cart import ShoppingCart -from teller import Teller -from tests.fake_catalog import FakeCatalog - - -def test_ten_percent_discount(): - catalog = FakeCatalog() - toothbrush = Product("toothbrush", ProductUnit.EACH) - catalog.add_product(toothbrush, 0.99) - apples = Product("apples", ProductUnit.KILO) - catalog.add_product(apples, 1.99) - - teller = Teller(catalog) - teller.add_special_offer(SpecialOfferType.TEN_PERCENT_DISCOUNT, toothbrush, 10.0) - - cart = ShoppingCart() - cart.add_item_quantity(apples, 2.5) - - receipt = teller.checks_out_articles_from(cart) - - assert 4.975 == pytest.approx(receipt.total_price(), 0.01) - assert [] == receipt.discounts - assert 1 == len(receipt.items) - receipt_item = receipt.items[0] - assert apples == receipt_item.product - assert 1.99 == receipt_item.price - assert 2.5 * 1.99 == pytest.approx(receipt_item.total_price, 0.01) - assert 2.5 == receipt_item.quantity diff --git a/ruby/Gemfile b/ruby/Gemfile deleted file mode 100644 index d276072..0000000 --- a/ruby/Gemfile +++ /dev/null @@ -1,4 +0,0 @@ -source 'https://rubygems.org' - -gem 'rake' -gem 'minitest' diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock deleted file mode 100644 index 1f2b401..0000000 --- a/ruby/Gemfile.lock +++ /dev/null @@ -1,15 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - minitest (5.11.3) - rake (12.3.3) - -PLATFORMS - ruby - -DEPENDENCIES - minitest - rake - -BUNDLED WITH - 1.17.3 diff --git a/ruby/Rakefile b/ruby/Rakefile deleted file mode 100644 index 19b90b8..0000000 --- a/ruby/Rakefile +++ /dev/null @@ -1,8 +0,0 @@ -require "rake/testtask" - -Rake::TestTask.new do |t| - t.description = "Run tests" - t.test_files = FileList['test/**/*_test.rb'] -end - -task default: :test diff --git a/ruby/lib/models/discount.rb b/ruby/lib/models/discount.rb deleted file mode 100644 index 8630e52..0000000 --- a/ruby/lib/models/discount.rb +++ /dev/null @@ -1,11 +0,0 @@ -class Discount - - attr_reader :product, :description, :discount_amount - - def initialize(product, description, discount_amount) - @product = product - @description = description - @discount_amount = discount_amount - end - -end diff --git a/ruby/lib/models/offer.rb b/ruby/lib/models/offer.rb deleted file mode 100644 index efbd86f..0000000 --- a/ruby/lib/models/offer.rb +++ /dev/null @@ -1,11 +0,0 @@ -class Offer - - attr_reader :product, :offer_type, :argument - - def initialize(offer_type, product, argument) - @offer_type = offer_type - @argument = argument - @product = product - end - -end diff --git a/ruby/lib/models/product.rb b/ruby/lib/models/product.rb deleted file mode 100644 index a531ecd..0000000 --- a/ruby/lib/models/product.rb +++ /dev/null @@ -1,5 +0,0 @@ -Product = Struct.new(:name, :unit) do - - undef :name=, :unit= - -end diff --git a/ruby/lib/models/product_quantity.rb b/ruby/lib/models/product_quantity.rb deleted file mode 100644 index bff232d..0000000 --- a/ruby/lib/models/product_quantity.rb +++ /dev/null @@ -1,10 +0,0 @@ -class ProductQuantity - - attr_reader :product, :quantity - - def initialize(product, weight) - @product = product - @quantity = weight - end - -end diff --git a/ruby/lib/models/product_unit.rb b/ruby/lib/models/product_unit.rb deleted file mode 100644 index 63d9b82..0000000 --- a/ruby/lib/models/product_unit.rb +++ /dev/null @@ -1,4 +0,0 @@ -module ProductUnit - EACH = Object.new - KILO = Object.new -end diff --git a/ruby/lib/models/receipt.rb b/ruby/lib/models/receipt.rb deleted file mode 100644 index 336e7ca..0000000 --- a/ruby/lib/models/receipt.rb +++ /dev/null @@ -1,37 +0,0 @@ -class Receipt - - def initialize - @items = [] - @discounts = [] - end - - def total_price - total = 0.0 - for item in @items do - total += item.total_price - end - for discount in @discounts do - total -= discount.discount_amount - end - total - end - - def add_product(product, quantity, price, total_price) - @items << ReceiptItem.new(product, quantity, price, total_price) - nil - end - - def items - Array.new @items - end - - def add_discount(discount) - @discounts << discount - nil - end - - def discounts - Array.new @discounts - end - -end diff --git a/ruby/lib/models/receipt_item.rb b/ruby/lib/models/receipt_item.rb deleted file mode 100644 index 9dc05d9..0000000 --- a/ruby/lib/models/receipt_item.rb +++ /dev/null @@ -1,5 +0,0 @@ -ReceiptItem = Struct.new(:product, :quantity, :price, :total_price) do - - undef :product=, :quantity=, :price=, :total_price= - -end diff --git a/ruby/lib/models/shopping_cart.rb b/ruby/lib/models/shopping_cart.rb deleted file mode 100644 index 6eea42e..0000000 --- a/ruby/lib/models/shopping_cart.rb +++ /dev/null @@ -1,72 +0,0 @@ -class ShoppingCart - - def initialize - @items = [] - @product_quantities = {} - end - - def items - Array.new @items - end - - def add_item(product) - add_item_quantity(product, 1.0) - nil - end - - def product_quantities - @product_quantities - end - - def add_item_quantity(product, quantity) - @items << ProductQuantity.new(product, quantity) - if @product_quantities.key?(product) - product_quantities[product] = product_quantities[product] + quantity - else - product_quantities[product] = quantity - end - end - - def handle_offers(receipt, offers, catalog) - for p in @product_quantities.keys do - quantity = @product_quantities[p] - if offers.key?(p) - offer = offers[p] - unit_price = catalog.unit_price(p) - quantity_as_int = quantity.to_i - discount = nil - x = 1 - if offer.offer_type == SpecialOfferType::THREE_FOR_TWO - x = 3 - - elsif offer.offer_type == SpecialOfferType::TWO_FOR_AMOUNT - x = 2 - if quantity_as_int >= 2 - total = offer.argument * (quantity_as_int / x) + quantity_as_int % 2 * unit_price - discount_n = unit_price * quantity - total - discount = Discount.new(p, "2 for " + offer.argument.to_s, discount_n) - end - - end - if offer.offer_type == SpecialOfferType:: FIVE_FOR_AMOUNT - x = 5 - end - number_of_x = quantity_as_int / x - if offer.offer_type == SpecialOfferType::THREE_FOR_TWO && quantity_as_int > 2 - discount_amount = quantity * unit_price - ((number_of_x * 2 * unit_price) + quantity_as_int % 3 * unit_price) - discount = Discount.new(p, "3 for 2", discount_amount) - end - if offer.offer_type == SpecialOfferType::TEN_PERCENT_DISCOUNT - discount = Discount.new(p, offer.argument.to_s + "% off", quantity * unit_price * offer.argument / 100.0) - end - if offer.offer_type == SpecialOfferType::FIVE_FOR_AMOUNT && quantity_as_int >= 5 - discount_total = unit_price * quantity - (offer.argument * number_of_x + quantity_as_int % 5 * unit_price) - discount = Discount.new(p, x.to_s + " for " + offer.argument.to_s, discount_total) - end - - receipt.add_discount(discount) if discount - end - end - end - -end diff --git a/ruby/lib/models/special_offer_type.rb b/ruby/lib/models/special_offer_type.rb deleted file mode 100644 index 5c7b22d..0000000 --- a/ruby/lib/models/special_offer_type.rb +++ /dev/null @@ -1,6 +0,0 @@ -module SpecialOfferType - THREE_FOR_TWO = Object.new - TEN_PERCENT_DISCOUNT = Object.new - TWO_FOR_AMOUNT = Object.new - FIVE_FOR_AMOUNT = Object.new -end diff --git a/ruby/lib/models/supermarket_catalog.rb b/ruby/lib/models/supermarket_catalog.rb deleted file mode 100644 index 8b8c7eb..0000000 --- a/ruby/lib/models/supermarket_catalog.rb +++ /dev/null @@ -1,11 +0,0 @@ -class SupermarketCatalog - - def add_product(product, price) - raise NotImplementedError - end - - def unit_price(product) - raise NotImplementedError - end - -end diff --git a/ruby/lib/models/teller.rb b/ruby/lib/models/teller.rb deleted file mode 100644 index ed74356..0000000 --- a/ruby/lib/models/teller.rb +++ /dev/null @@ -1,27 +0,0 @@ -class Teller - - def initialize(catalog) - @catalog = catalog - @offers = {} - end - - def add_special_offer(offer_type, product, argument) - @offers[product] = Offer.new(offer_type, product, argument) - end - - def checks_out_articles_from(the_cart) - receipt = Receipt.new - product_quantities = the_cart.items - for pq in product_quantities do - p = pq.product - quantity = pq.quantity - unit_price = @catalog.unit_price(p) - price = quantity * unit_price - receipt.add_product(p, quantity, unit_price, price) - end - the_cart.handle_offers(receipt, @offers, @catalog) - - receipt - end - -end diff --git a/ruby/lib/receipt_printer.rb b/ruby/lib/receipt_printer.rb deleted file mode 100644 index 3c06478..0000000 --- a/ruby/lib/receipt_printer.rb +++ /dev/null @@ -1,57 +0,0 @@ -class ReceiptPrinter - - def initialize(columns = 40) - @columns = columns - end - - def print_receipt(receipt) - result = "" - for item in receipt.items do - price = "%.2f" % item.total_price - quantity = self.class.present_quantity(item) - name = item.product.name - unit_price = "%.2f" % item.price - - whitespace_size = @columns - name.size - price.size - line = name + self.class.whitespace(whitespace_size) + price + "\n" - - if item.quantity != 1 - line += " " + unit_price + " * " + quantity + "\n" - end - - result.concat(line); - end - for discount in receipt.discounts do - product_presentation = discount.product.name - price_presentation = "%.2f" % discount.discount_amount - description = discount.description - result.concat(description) - result.concat("(") - result.concat(product_presentation) - result.concat(")") - result.concat(self.class.whitespace(@columns - 3 - product_presentation.size - description.size - price_presentation.size)) - result.concat("-"); - result.concat(price_presentation); - result.concat("\n"); - end - result.concat("\n") - price_presentation = "%.2f" % receipt.total_price.to_f - total = "Total: " - whitespace = self.class.whitespace(@columns - total.size - price_presentation.size) - result.concat(total, whitespace, price_presentation) - return result.to_s - end - - def self.present_quantity(item) - return ProductUnit::EACH == item.product.unit ? '%x' % item.quantity.to_i : '%.3f' % item.quantity - end - - def self.whitespace(whitespace_size) - whitespace = '' - whitespace_size.times do - whitespace.concat(' ') - end - return whitespace - end - -end diff --git a/ruby/main.rb b/ruby/main.rb deleted file mode 100644 index 6715358..0000000 --- a/ruby/main.rb +++ /dev/null @@ -1,2 +0,0 @@ -pattern = File.join(File.dirname(__FILE__), 'lib', '**', '*.rb') -Dir[pattern].each { |filepath| require_relative filepath } diff --git a/ruby/test/fake_catalog.rb b/ruby/test/fake_catalog.rb deleted file mode 100644 index 0c175b2..0000000 --- a/ruby/test/fake_catalog.rb +++ /dev/null @@ -1,17 +0,0 @@ -class FakeCatalog < SupermarketCatalog - - def initialize - @products = {} - @prices = {} - end - - def add_product(product, price) - @products[product.name] = product - @prices[product.name] = price - end - - def unit_price(p) - @prices.fetch(p.name) - end - -end diff --git a/ruby/test/supermarket_test.rb b/ruby/test/supermarket_test.rb deleted file mode 100644 index 2ceb55f..0000000 --- a/ruby/test/supermarket_test.rb +++ /dev/null @@ -1,24 +0,0 @@ -require_relative './test_helper' - -class SupermarketTest < Minitest::Test - - def test_ten_percent_discount - catalog = FakeCatalog.new - toothbrush = Product.new("toothbrush", ProductUnit::EACH) - catalog.add_product(toothbrush, 0.99) - - apples = Product.new("apples", ProductUnit::KILO) - catalog.add_product(apples, 1.99) - - cart = ShoppingCart.new - cart.add_item_quantity(apples, 2.5) - - teller = Teller.new(catalog) - teller.add_special_offer(SpecialOfferType::TEN_PERCENT_DISCOUNT, toothbrush, 10.0) - - receipt = teller.checks_out_articles_from(cart) - - assert_in_delta 4.975, receipt.total_price, 0.01 - end - -end diff --git a/ruby/test/test_helper.rb b/ruby/test/test_helper.rb deleted file mode 100644 index 9340860..0000000 --- a/ruby/test/test_helper.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'minitest/autorun' - -pattern = File.join(File.dirname(__FILE__), '..', 'lib', '**', '*.rb') -Dir[pattern].each { |filepath| require_relative filepath } - -require_relative './fake_catalog' diff --git a/swift/SupermarketReceipt.xcodeproj/project.pbxproj b/swift/SupermarketReceipt.xcodeproj/project.pbxproj deleted file mode 100644 index b0d4441..0000000 --- a/swift/SupermarketReceipt.xcodeproj/project.pbxproj +++ /dev/null @@ -1,514 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXBuildFile section */ - A1038187234689D200A959E4 /* SupermarketReceipt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A103817D234689D200A959E4 /* SupermarketReceipt.framework */; }; - A103818C234689D200A959E4 /* SupermarketReceiptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A103818B234689D200A959E4 /* SupermarketReceiptTests.swift */; }; - A103818E234689D200A959E4 /* SupermarketReceipt.h in Headers */ = {isa = PBXBuildFile; fileRef = A1038180234689D200A959E4 /* SupermarketReceipt.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A175285B234A7C8800610C19 /* ReceiptPrinter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A175285A234A7C8800610C19 /* ReceiptPrinter.swift */; }; - A175285D234A7CC200610C19 /* ReceiptPrinterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A175285C234A7CC200610C19 /* ReceiptPrinterTests.swift */; }; - A1F2906A2347D532004F55F7 /* FakeCatalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F290692347D532004F55F7 /* FakeCatalog.swift */; }; - A1F290772347D62A004F55F7 /* ProductQuantity.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F2906B2347D629004F55F7 /* ProductQuantity.swift */; }; - A1F290782347D62A004F55F7 /* Teller.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F2906C2347D629004F55F7 /* Teller.swift */; }; - A1F290792347D62A004F55F7 /* SupermarketCatalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F2906D2347D629004F55F7 /* SupermarketCatalog.swift */; }; - A1F2907A2347D62A004F55F7 /* SpecialOfferType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F2906E2347D629004F55F7 /* SpecialOfferType.swift */; }; - A1F2907B2347D62A004F55F7 /* Product.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F2906F2347D629004F55F7 /* Product.swift */; }; - A1F2907C2347D62A004F55F7 /* Offer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F290702347D629004F55F7 /* Offer.swift */; }; - A1F2907D2347D62A004F55F7 /* ProductUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F290712347D629004F55F7 /* ProductUnit.swift */; }; - A1F2907E2347D62A004F55F7 /* ReceiptItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F290722347D629004F55F7 /* ReceiptItem.swift */; }; - A1F2907F2347D62A004F55F7 /* Receipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F290732347D629004F55F7 /* Receipt.swift */; }; - A1F290812347D62A004F55F7 /* ShoppingCart.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F290752347D62A004F55F7 /* ShoppingCart.swift */; }; - A1F290822347D62A004F55F7 /* Discount.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F290762347D62A004F55F7 /* Discount.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - A1038188234689D200A959E4 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = A1038174234689D200A959E4 /* Project object */; - proxyType = 1; - remoteGlobalIDString = A103817C234689D200A959E4; - remoteInfo = SupermarketReceipt; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - A103817D234689D200A959E4 /* SupermarketReceipt.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SupermarketReceipt.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - A1038180234689D200A959E4 /* SupermarketReceipt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SupermarketReceipt.h; sourceTree = ""; }; - A1038181234689D200A959E4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A1038186234689D200A959E4 /* SupermarketReceiptTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SupermarketReceiptTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - A103818B234689D200A959E4 /* SupermarketReceiptTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupermarketReceiptTests.swift; sourceTree = ""; }; - A103818D234689D200A959E4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A175285A234A7C8800610C19 /* ReceiptPrinter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReceiptPrinter.swift; sourceTree = ""; }; - A175285C234A7CC200610C19 /* ReceiptPrinterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptPrinterTests.swift; sourceTree = ""; }; - A1F290692347D532004F55F7 /* FakeCatalog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FakeCatalog.swift; sourceTree = ""; }; - A1F2906B2347D629004F55F7 /* ProductQuantity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProductQuantity.swift; sourceTree = ""; }; - A1F2906C2347D629004F55F7 /* Teller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Teller.swift; sourceTree = ""; }; - A1F2906D2347D629004F55F7 /* SupermarketCatalog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SupermarketCatalog.swift; sourceTree = ""; }; - A1F2906E2347D629004F55F7 /* SpecialOfferType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpecialOfferType.swift; sourceTree = ""; }; - A1F2906F2347D629004F55F7 /* Product.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Product.swift; sourceTree = ""; }; - A1F290702347D629004F55F7 /* Offer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Offer.swift; sourceTree = ""; }; - A1F290712347D629004F55F7 /* ProductUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProductUnit.swift; sourceTree = ""; }; - A1F290722347D629004F55F7 /* ReceiptItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReceiptItem.swift; sourceTree = ""; }; - A1F290732347D629004F55F7 /* Receipt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Receipt.swift; sourceTree = ""; }; - A1F290752347D62A004F55F7 /* ShoppingCart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShoppingCart.swift; sourceTree = ""; }; - A1F290762347D62A004F55F7 /* Discount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Discount.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - A103817A234689D200A959E4 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A1038183234689D200A959E4 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - A1038187234689D200A959E4 /* SupermarketReceipt.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - A1038173234689D200A959E4 = { - isa = PBXGroup; - children = ( - A103817F234689D200A959E4 /* SupermarketReceipt */, - A103818A234689D200A959E4 /* SupermarketReceiptTests */, - A103817E234689D200A959E4 /* Products */, - ); - sourceTree = ""; - }; - A103817E234689D200A959E4 /* Products */ = { - isa = PBXGroup; - children = ( - A103817D234689D200A959E4 /* SupermarketReceipt.framework */, - A1038186234689D200A959E4 /* SupermarketReceiptTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - A103817F234689D200A959E4 /* SupermarketReceipt */ = { - isa = PBXGroup; - children = ( - A1F290762347D62A004F55F7 /* Discount.swift */, - A1F290702347D629004F55F7 /* Offer.swift */, - A1F2906F2347D629004F55F7 /* Product.swift */, - A1F2906B2347D629004F55F7 /* ProductQuantity.swift */, - A1F290712347D629004F55F7 /* ProductUnit.swift */, - A1F290732347D629004F55F7 /* Receipt.swift */, - A1F290722347D629004F55F7 /* ReceiptItem.swift */, - A175285A234A7C8800610C19 /* ReceiptPrinter.swift */, - A1F290752347D62A004F55F7 /* ShoppingCart.swift */, - A1F2906E2347D629004F55F7 /* SpecialOfferType.swift */, - A1F2906D2347D629004F55F7 /* SupermarketCatalog.swift */, - A1F2906C2347D629004F55F7 /* Teller.swift */, - A1038180234689D200A959E4 /* SupermarketReceipt.h */, - A1038181234689D200A959E4 /* Info.plist */, - ); - path = SupermarketReceipt; - sourceTree = ""; - }; - A103818A234689D200A959E4 /* SupermarketReceiptTests */ = { - isa = PBXGroup; - children = ( - A1F290692347D532004F55F7 /* FakeCatalog.swift */, - A103818B234689D200A959E4 /* SupermarketReceiptTests.swift */, - A103818D234689D200A959E4 /* Info.plist */, - A175285C234A7CC200610C19 /* ReceiptPrinterTests.swift */, - ); - path = SupermarketReceiptTests; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - A1038178234689D200A959E4 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - A103818E234689D200A959E4 /* SupermarketReceipt.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - A103817C234689D200A959E4 /* SupermarketReceipt */ = { - isa = PBXNativeTarget; - buildConfigurationList = A1038191234689D200A959E4 /* Build configuration list for PBXNativeTarget "SupermarketReceipt" */; - buildPhases = ( - A1038178234689D200A959E4 /* Headers */, - A1038179234689D200A959E4 /* Sources */, - A103817A234689D200A959E4 /* Frameworks */, - A103817B234689D200A959E4 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = SupermarketReceipt; - productName = SupermarketReceipt; - productReference = A103817D234689D200A959E4 /* SupermarketReceipt.framework */; - productType = "com.apple.product-type.framework"; - }; - A1038185234689D200A959E4 /* SupermarketReceiptTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = A1038194234689D200A959E4 /* Build configuration list for PBXNativeTarget "SupermarketReceiptTests" */; - buildPhases = ( - A1038182234689D200A959E4 /* Sources */, - A1038183234689D200A959E4 /* Frameworks */, - A1038184234689D200A959E4 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - A1038189234689D200A959E4 /* PBXTargetDependency */, - ); - name = SupermarketReceiptTests; - productName = SupermarketReceiptTests; - productReference = A1038186234689D200A959E4 /* SupermarketReceiptTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - A1038174234689D200A959E4 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1100; - LastUpgradeCheck = 1100; - ORGANIZATIONNAME = "Andy Smith"; - TargetAttributes = { - A103817C234689D200A959E4 = { - CreatedOnToolsVersion = 11.0; - LastSwiftMigration = 1100; - }; - A1038185234689D200A959E4 = { - CreatedOnToolsVersion = 11.0; - }; - }; - }; - buildConfigurationList = A1038177234689D200A959E4 /* Build configuration list for PBXProject "SupermarketReceipt" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = A1038173234689D200A959E4; - productRefGroup = A103817E234689D200A959E4 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - A103817C234689D200A959E4 /* SupermarketReceipt */, - A1038185234689D200A959E4 /* SupermarketReceiptTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - A103817B234689D200A959E4 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A1038184234689D200A959E4 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - A1038179234689D200A959E4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A1F2907D2347D62A004F55F7 /* ProductUnit.swift in Sources */, - A1F2907C2347D62A004F55F7 /* Offer.swift in Sources */, - A1F290822347D62A004F55F7 /* Discount.swift in Sources */, - A1F290792347D62A004F55F7 /* SupermarketCatalog.swift in Sources */, - A1F2907F2347D62A004F55F7 /* Receipt.swift in Sources */, - A1F290772347D62A004F55F7 /* ProductQuantity.swift in Sources */, - A1F290812347D62A004F55F7 /* ShoppingCart.swift in Sources */, - A1F2907B2347D62A004F55F7 /* Product.swift in Sources */, - A1F2907A2347D62A004F55F7 /* SpecialOfferType.swift in Sources */, - A1F290782347D62A004F55F7 /* Teller.swift in Sources */, - A1F2907E2347D62A004F55F7 /* ReceiptItem.swift in Sources */, - A175285B234A7C8800610C19 /* ReceiptPrinter.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A1038182234689D200A959E4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A1F2906A2347D532004F55F7 /* FakeCatalog.swift in Sources */, - A175285D234A7CC200610C19 /* ReceiptPrinterTests.swift in Sources */, - A103818C234689D200A959E4 /* SupermarketReceiptTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - A1038189234689D200A959E4 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = A103817C234689D200A959E4 /* SupermarketReceipt */; - targetProxy = A1038188234689D200A959E4 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - A103818F234689D200A959E4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - A1038190234689D200A959E4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - A1038192234689D200A959E4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = SupermarketReceipt/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.andy.SupermarketReceipt; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - A1038193234689D200A959E4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = SupermarketReceipt/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.andy.SupermarketReceipt; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - A1038195234689D200A959E4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = SupermarketReceiptTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.andy.SupermarketReceiptTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - A1038196234689D200A959E4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = SupermarketReceiptTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.andy.SupermarketReceiptTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - A1038177234689D200A959E4 /* Build configuration list for PBXProject "SupermarketReceipt" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A103818F234689D200A959E4 /* Debug */, - A1038190234689D200A959E4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - A1038191234689D200A959E4 /* Build configuration list for PBXNativeTarget "SupermarketReceipt" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A1038192234689D200A959E4 /* Debug */, - A1038193234689D200A959E4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - A1038194234689D200A959E4 /* Build configuration list for PBXNativeTarget "SupermarketReceiptTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A1038195234689D200A959E4 /* Debug */, - A1038196234689D200A959E4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = A1038174234689D200A959E4 /* Project object */; -} diff --git a/swift/SupermarketReceipt.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/swift/SupermarketReceipt.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index ab23f88..0000000 --- a/swift/SupermarketReceipt.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/swift/SupermarketReceipt.xcodeproj/xcshareddata/xcschemes/SupermarketReceipt.xcscheme b/swift/SupermarketReceipt.xcodeproj/xcshareddata/xcschemes/SupermarketReceipt.xcscheme deleted file mode 100644 index 6a79263..0000000 --- a/swift/SupermarketReceipt.xcodeproj/xcshareddata/xcschemes/SupermarketReceipt.xcscheme +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/swift/SupermarketReceipt.xcodeproj/xcuserdata/andy.xcuserdatad/xcschemes/xcschememanagement.plist b/swift/SupermarketReceipt.xcodeproj/xcuserdata/andy.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 4561f48..0000000 --- a/swift/SupermarketReceipt.xcodeproj/xcuserdata/andy.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,27 +0,0 @@ - - - - - SchemeUserState - - SupermarketReceipt.xcscheme_^#shared#^_ - - orderHint - 5 - - - SuppressBuildableAutocreation - - A103817C234689D200A959E4 - - primary - - - A1038185234689D200A959E4 - - primary - - - - - diff --git a/swift/SupermarketReceipt.xcworkspace/contents.xcworkspacedata b/swift/SupermarketReceipt.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 7e03c0d..0000000 --- a/swift/SupermarketReceipt.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/swift/SupermarketReceipt.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/swift/SupermarketReceipt.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/swift/SupermarketReceipt.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/swift/SupermarketReceipt.xcworkspace/xcshareddata/xcschemes/ApprovalTests_Swift.xcscheme b/swift/SupermarketReceipt.xcworkspace/xcshareddata/xcschemes/ApprovalTests_Swift.xcscheme deleted file mode 100644 index 2d82fb6..0000000 --- a/swift/SupermarketReceipt.xcworkspace/xcshareddata/xcschemes/ApprovalTests_Swift.xcscheme +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/swift/SupermarketReceipt/Discount.swift b/swift/SupermarketReceipt/Discount.swift deleted file mode 100644 index c6c6066..0000000 --- a/swift/SupermarketReceipt/Discount.swift +++ /dev/null @@ -1,5 +0,0 @@ -public struct Discount { - let description: String - let discountAmount: Double - let product: Product -} diff --git a/swift/SupermarketReceipt/Info.plist b/swift/SupermarketReceipt/Info.plist deleted file mode 100644 index eb29618..0000000 --- a/swift/SupermarketReceipt/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSHumanReadableCopyright - Copyright © 2019 Andy Smith. All rights reserved. - - diff --git a/swift/SupermarketReceipt/Offer.swift b/swift/SupermarketReceipt/Offer.swift deleted file mode 100644 index e9ee48c..0000000 --- a/swift/SupermarketReceipt/Offer.swift +++ /dev/null @@ -1,5 +0,0 @@ -public struct Offer { - let offerType: SpecialOfferType - let product: Product - let argument: Double -} diff --git a/swift/SupermarketReceipt/Product.swift b/swift/SupermarketReceipt/Product.swift deleted file mode 100644 index 51e28d2..0000000 --- a/swift/SupermarketReceipt/Product.swift +++ /dev/null @@ -1,4 +0,0 @@ -public struct Product: Hashable { - let name: String - let unit: ProductUnit -} diff --git a/swift/SupermarketReceipt/ProductQuantity.swift b/swift/SupermarketReceipt/ProductQuantity.swift deleted file mode 100644 index 2fbfcf8..0000000 --- a/swift/SupermarketReceipt/ProductQuantity.swift +++ /dev/null @@ -1,4 +0,0 @@ -public struct ProductQuantity { - let product: Product - let quantity: Double -} diff --git a/swift/SupermarketReceipt/ProductUnit.swift b/swift/SupermarketReceipt/ProductUnit.swift deleted file mode 100644 index 2c75b77..0000000 --- a/swift/SupermarketReceipt/ProductUnit.swift +++ /dev/null @@ -1,4 +0,0 @@ -public enum ProductUnit { - case Kilo - case Each -} diff --git a/swift/SupermarketReceipt/Receipt.swift b/swift/SupermarketReceipt/Receipt.swift deleted file mode 100644 index 473d655..0000000 --- a/swift/SupermarketReceipt/Receipt.swift +++ /dev/null @@ -1,23 +0,0 @@ -public class Receipt { - var items = [ReceiptItem]() - var discounts = [Discount]() - - public func getTotalPrice() -> Double { - var total: Double = 0.0 - for item in self.items { - total += item.totalPrice - } - for discount in self.discounts { - total -= discount.discountAmount - } - return total - } - - public func addProduct(p: Product, quantity: Double, price: Double, totalPrice: Double) { - self.items.append(ReceiptItem(product: p, price: price, totalPrice: totalPrice, quantity: quantity)) - } - - public func addDiscount(discount: Discount) { - self.discounts.append(discount) - } -} diff --git a/swift/SupermarketReceipt/ReceiptItem.swift b/swift/SupermarketReceipt/ReceiptItem.swift deleted file mode 100644 index 374e1f9..0000000 --- a/swift/SupermarketReceipt/ReceiptItem.swift +++ /dev/null @@ -1,6 +0,0 @@ -public struct ReceiptItem { - let product: Product - let price: Double - let totalPrice: Double - let quantity: Double -} diff --git a/swift/SupermarketReceipt/ReceiptPrinter.swift b/swift/SupermarketReceipt/ReceiptPrinter.swift deleted file mode 100644 index eb6ea18..0000000 --- a/swift/SupermarketReceipt/ReceiptPrinter.swift +++ /dev/null @@ -1,61 +0,0 @@ -public class ReceiptPrinter { - - private var columns: Int = 40 - - public init(columns: Int) { - self.columns = columns - } - - public func printReceipt(receipt: Receipt) -> String { - var result = "" - for item in receipt.items { - var price = String(format: "%.2f", item.totalPrice) - var quantity = ReceiptPrinter.presentQuantity(item: item) - var name = item.product.name - var unitPrice = String(format :"%.2f", item.price) - - var whitespaceSize = self.columns - name.count - price.count - var line = name + ReceiptPrinter.getWhitespace(whitespaceSize: whitespaceSize) + price + "\n" - - if (item.quantity != 1) { - line += " " + unitPrice + " * " + quantity + "\n" - } - result.append(line) - } - for discount in receipt.discounts { - var productPresentation = discount.product.name - var pricePresentation = String(format: "%.2f", discount.discountAmount) - var description = discount.description - result.append(description) - result.append("(") - result.append(productPresentation) - result.append(")") - result.append(ReceiptPrinter.getWhitespace(whitespaceSize: self.columns - 3 - productPresentation.count - description.count - pricePresentation.count)) - result.append("-") - result.append(pricePresentation) - result.append("\n") - } - result.append("\n") - var pricePresentation = String(format: "%.2f", Double(receipt.getTotalPrice())) - var total = "Total: " - var whitespace = ReceiptPrinter.getWhitespace(whitespaceSize: self.columns - total.count - pricePresentation.count) - result.append(total) - result.append(whitespace) - result.append(pricePresentation) - return result - } - - private static func presentQuantity(item: ReceiptItem ) -> String { - return ProductUnit.Each == item.product.unit - ? String(format: "%x", Int(item.quantity)) - : String(format: "%.3f", item.quantity) - } - - private static func getWhitespace(whitespaceSize: Int) -> String { - var whitespace = "" - for i in 0..= 2) { - - var intDivision = quantityAsInt / x - - var pricePerUnit = (offer!.argument * Double(intDivision)) - - var theTotal = Double(quantityAsInt % 2) * unitPrice - - var total = pricePerUnit + theTotal - var discountN = unitPrice * quantity! - total - discount = Discount(description: "2 for \(offer!.argument)", discountAmount: discountN, product: p) - } - - } else if offer?.offerType == SpecialOfferType.FiveForAmount { - x = 5 - } - var numberOfXs = quantityAsInt / x - if offer?.offerType == SpecialOfferType.ThreeForTwo && quantityAsInt > 2 { - var left = Double(numberOfXs * 2) * unitPrice - var right = Double(quantityAsInt % 3) * unitPrice - var lastPart = left + right - var discountAmount = ((quantity ?? 1) * unitPrice) - lastPart - discount = Discount(description: "3 for 2", discountAmount: discountAmount, product: p) - } - if offer?.offerType == SpecialOfferType.TenPercentDiscount { - discount = Discount(description: "\(offer!.argument)% off", discountAmount: (quantity ?? 1) * unitPrice * (offer?.argument ?? 1) / 100.0, product: p) - } - if offer?.offerType == SpecialOfferType.FiveForAmount && quantityAsInt >= 5 { - var left = (unitPrice * (quantity ?? 1)) - var right = ((offer?.argument ?? 1) * Double(numberOfXs)) + (Double(quantityAsInt % 5) * unitPrice) - var discountTotal = left - right - discount = Discount(description: "\(x) for \(offer!.argument)", discountAmount: discountTotal, product: p) - } - if discount != nil { - receipt.addDiscount(discount: discount!) - } - } - - } - } -} diff --git a/swift/SupermarketReceipt/SpecialOfferType.swift b/swift/SupermarketReceipt/SpecialOfferType.swift deleted file mode 100644 index b262cda..0000000 --- a/swift/SupermarketReceipt/SpecialOfferType.swift +++ /dev/null @@ -1,6 +0,0 @@ -public enum SpecialOfferType { - case ThreeForTwo - case TenPercentDiscount - case TwoForAmount - case FiveForAmount -} diff --git a/swift/SupermarketReceipt/SupermarketCatalog.swift b/swift/SupermarketReceipt/SupermarketCatalog.swift deleted file mode 100644 index 411b0ce..0000000 --- a/swift/SupermarketReceipt/SupermarketCatalog.swift +++ /dev/null @@ -1,4 +0,0 @@ -public protocol SupermarketCatalog { - func addProduct(product: Product, price: Double) - func getUnitPrice(product: Product) -> Double -} diff --git a/swift/SupermarketReceipt/SupermarketReceipt.h b/swift/SupermarketReceipt/SupermarketReceipt.h deleted file mode 100644 index 047c7d1..0000000 --- a/swift/SupermarketReceipt/SupermarketReceipt.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// SupermarketReceipt.h -// SupermarketReceipt -// -// Created by Andy Smith on 03/10/2019. -// Copyright © 2019 Andy Smith. All rights reserved. -// - -#import - -//! Project version number for SupermarketReceipt. -FOUNDATION_EXPORT double SupermarketReceiptVersionNumber; - -//! Project version string for SupermarketReceipt. -FOUNDATION_EXPORT const unsigned char SupermarketReceiptVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/swift/SupermarketReceipt/Teller.swift b/swift/SupermarketReceipt/Teller.swift deleted file mode 100644 index ed335f7..0000000 --- a/swift/SupermarketReceipt/Teller.swift +++ /dev/null @@ -1,30 +0,0 @@ -public class Teller { - - private let catalog: SupermarketCatalog - private var offers = [Product: Offer]() - - public init(catalog: SupermarketCatalog) { - self.catalog = catalog - } - - public func addSpecialOffer(offerType: SpecialOfferType, product: Product, argument: Double) { - self.offers[product] = Offer(offerType: offerType, product: product, argument: argument) - } - - public func checksOutArticlesFrom(theCart: ShoppingCart) -> Receipt { - var receipt = Receipt() - var productQuantities = theCart.items - for pq in productQuantities { - var p = pq.product - var quantity = pq.quantity - var unitPrice = self.catalog.getUnitPrice(product: p) - var price = quantity * unitPrice - var priceTo3dp = round(100 * price) / 100 - receipt.addProduct(p: p, quantity: quantity, price: unitPrice, totalPrice: priceTo3dp) - } - theCart.handleOffers(receipt: receipt, offers: self.offers, catalog: self.catalog) - - return receipt - } - -} diff --git a/swift/SupermarketReceiptTests/Info.plist b/swift/SupermarketReceiptTests/Info.plist deleted file mode 100644 index 64d65ca..0000000 --- a/swift/SupermarketReceiptTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/typescript/.gitignore b/typescript/.gitignore deleted file mode 100644 index 8c425a0..0000000 --- a/typescript/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules -*.js - -*.received.* diff --git a/typescript/package-lock.json b/typescript/package-lock.json deleted file mode 100644 index 54a2a4f..0000000 --- a/typescript/package-lock.json +++ /dev/null @@ -1,5218 +0,0 @@ -{ - "name": "supermarket-receipt-refactoring-kata", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "supermarket-receipt-refactoring-kata", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@types/lodash": "^4.14.119", - "@types/node": "^20.11.25", - "lodash": "^4.17.19", - "ts-node": "^10.9.2", - "typescript": "^5.4.2" - }, - "devDependencies": { - "@types/chai": "^4.1.7", - "@types/mocha": "^10.0.6", - "approvals": "^6.2.4", - "chai": "^4.4.1", - "mocha": "^10.3.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", - "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.0", - "@babel/parser": "^7.24.0", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", - "@babel/types": "^7.24.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", - "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz", - "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", - "@babel/types": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", - "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", - "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", - "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", - "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@jest/core/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@jest/core/node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@jest/reporters/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/chai": { - "version": "4.3.12", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.12.tgz", - "integrity": "sha512-zNKDHG/1yxm8Il6uCCVsm+dRdEsJlFoDu73X17y09bId6UwoYww+vFBsAcRzl8knM1sab3Dp1VRikFQwDOtDDw==", - "dev": true - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/lodash": { - "version": "4.14.202", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", - "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==" - }, - "node_modules/@types/mocha": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", - "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.11.25", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.25.tgz", - "integrity": "sha512-TBHyJxk2b7HceLVGFcpAUjsa5zIdsPWlR6XHfyGzd0SFu+/NFgQgMAl96MSDZgQDvJAvV6BKsFOrt6zIL09JDw==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", - "dev": true - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/approvals": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/approvals/-/approvals-6.2.4.tgz", - "integrity": "sha512-AdHO4salMqPf+5FpcUhx+Aong+YL2rlDMbbCK1AyBC0kEy/VzVmmWbtJ+BwZMdKA0jaiKEsYFKoj1RUDkEBbNg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "callsite": "^1.0.0", - "chalk": "^4.1.0", - "diff": "^5.1.0", - "event-stream": "^4.0.1", - "file-type": "^4.4.0", - "glob": "^8.0.3", - "jest": "^29.1.2", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "marked": "^4.0.15", - "marked-terminal": "^5.1.1", - "minimist": "^1.2.5", - "mkdirp": "^1.0.4", - "natives": "^1.1.6", - "shelljs": "^0.8.4", - "temp": "^0.9.4", - "ts-jest": "^29.0.3" - }, - "bin": { - "approvals": "bin/index.js" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/approvals/node_modules/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/approvals/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/approvals/node_modules/ts-jest": { - "version": "29.1.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", - "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/approvals/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/approvals/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001596", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001596.tgz", - "integrity": "sha512-zpkZ+kEr6We7w63ORkoJ2pOfBwBkY/bJrG/UZ90qNb45Isblu8wzDgevEOrRL1r9dWayHjYiiyCMEXPn4DweGQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", - "dev": true, - "dependencies": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - }, - "bin": { - "cdl": "bin/cdl.js" - } - }, - "node_modules/chai": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", - "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.0.8" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/create-jest/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/create-jest/node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/create-jest/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "node_modules/electron-to-chromium": { - "version": "1.4.699", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.699.tgz", - "integrity": "sha512-I7q3BbQi6e4tJJN5CRcyvxhK0iJb34TV8eJQcgh+fR2fQ8miMgZcEInckCo1U9exDHbfz7DLDnFn8oqH/VcRKw==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/event-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", - "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", - "dev": true, - "dependencies": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-type": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz", - "integrity": "sha512-f2UbFQEk7LXgWpi5ntcO86OeA/cC80fuDDDaX/fZ2ZGel+AF7leRQqBBW1eJNiiQkrZlAoM6P+VYP5P6bOlDEQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", - "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/jest-cli/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-cli/node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-runtime/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", - "dev": true - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/marked-terminal": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-5.2.0.tgz", - "integrity": "sha512-Piv6yNwAQXGFjZSaiNljyNFw7jKDdGrw70FSbtxEyldLsyeuV5ZHm/1wW++kWbrOF1VPnUgYOhB2oLL0ZpnekA==", - "dev": true, - "dependencies": { - "ansi-escapes": "^6.2.0", - "cardinal": "^2.1.1", - "chalk": "^5.2.0", - "cli-table3": "^0.6.3", - "node-emoji": "^1.11.0", - "supports-hyperlinks": "^2.3.0" - }, - "engines": { - "node": ">=14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "marked": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" - } - }, - "node_modules/marked-terminal/node_modules/ansi-escapes": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", - "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", - "dev": true, - "dependencies": { - "type-fest": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/marked-terminal/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/marked-terminal/node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.3.0.tgz", - "integrity": "sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==", - "dev": true, - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "8.1.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/mocha/node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/natives": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz", - "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==", - "deprecated": "This module relies on Node.js's internals and will break at some point. Do not use it, and update to graceful-fs@4.x.", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dev": true, - "dependencies": { - "lodash": "^4.17.21" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", - "dev": true, - "dependencies": { - "through": "~2.3" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pure-rand": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/redeyed": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", - "dev": true, - "dependencies": { - "esprima": "~4.0.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shelljs/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/shelljs/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/shelljs/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", - "dev": true, - "dependencies": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/temp": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", - "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", - "dev": true, - "dependencies": { - "mkdirp": "^0.5.1", - "rimraf": "~2.6.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/temp/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", - "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" - }, - "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/typescript/package.json b/typescript/package.json deleted file mode 100644 index f003fc1..0000000 --- a/typescript/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "supermarket-receipt-refactoring-kata", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "mocha --require ts-node/register --recursive test/**/*.test.ts", - "watch:test": "npm run test -- --watch-extensions ts --watch", - "compile": "tsc" - }, - "author": "Johan Martinsson", - "license": "ISC", - "devDependencies": { - "@types/chai": "^4.1.7", - "@types/mocha": "^10.0.6", - "approvals": "^6.2.4", - "chai": "^4.4.1", - "mocha": "^10.3.0" - }, - "dependencies": { - "@types/lodash": "^4.14.119", - "@types/node": "^20.11.25", - "lodash": "^4.17.19", - "ts-node": "^10.9.2", - "typescript": "^5.4.2" - } -} diff --git a/typescript/src/ReceiptPrinter.ts b/typescript/src/ReceiptPrinter.ts deleted file mode 100644 index 3e62566..0000000 --- a/typescript/src/ReceiptPrinter.ts +++ /dev/null @@ -1,67 +0,0 @@ -import {ProductUnit} from "./model/ProductUnit" -import {ReceiptItem} from "./model/ReceiptItem" -import {Receipt} from "./model/Receipt" - -export class ReceiptPrinter { - - public constructor(private readonly columns: number = 40) { - } - - public printReceipt( receipt: Receipt): string { - let result = ""; - for (const item of receipt.getItems()) { - let price = this.format2Decimals(item.totalPrice); - let quantity = ReceiptPrinter.presentQuantity(item); - let name = item.product.name; - let unitPrice = this.format2Decimals(item.price); - - let whitespaceSize = this.columns - name.length - price.length; - let line = name + ReceiptPrinter.getWhitespace(whitespaceSize) + price + "\n"; - - if (item.quantity != 1) { - line += " " + unitPrice + " * " + quantity + "\n"; - } - result += line; - } - for (const discount of receipt.getDiscounts()) { - let productPresentation = discount.product.name; - let pricePresentation = this.format2Decimals(discount.discountAmount); - let description = discount.description; - result += description; - result += "("; - result += productPresentation; - result += ")"; - result += ReceiptPrinter.getWhitespace(this.columns - 3 - productPresentation.length - description.length - pricePresentation.length); - result += "-"; - result += pricePresentation; - result += "\n"; - } - result += "\n"; - let pricePresentation = this.format2Decimals(receipt.getTotalPrice()); - let total = "Total: "; - let whitespace = ReceiptPrinter.getWhitespace(this.columns - total.length - pricePresentation.length); - result += total; - result += whitespace; - result += pricePresentation; - - return result; - } - - private format2Decimals(number: number) { - return new Intl.NumberFormat('en-UK', { - minimumFractionDigits: 2, - maximumFractionDigits: 2 - }).format(number) - } - - private static presentQuantity( item: ReceiptItem): string { - return ProductUnit.Each == item.product.unit - // TODO make sure this is the simplest way to make something similar to the java version - ? new Intl.NumberFormat('en-UK', {maximumFractionDigits: 0}).format(item.quantity) - : new Intl.NumberFormat('en-UK', {minimumFractionDigits: 3}).format(item.quantity); - } - - private static getWhitespace(whitespaceSize: number): string { - return " ".repeat(whitespaceSize); - } -} diff --git a/typescript/src/model/Discount.ts b/typescript/src/model/Discount.ts deleted file mode 100644 index 5859e78..0000000 --- a/typescript/src/model/Discount.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {Product} from "./Product" - -export class Discount { - - constructor(public readonly product: Product, - public readonly description: string, - public readonly discountAmount: number) { - } -} diff --git a/typescript/src/model/Offer.ts b/typescript/src/model/Offer.ts deleted file mode 100644 index f72a78b..0000000 --- a/typescript/src/model/Offer.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {Product} from "./Product" -import {SpecialOfferType} from "./SpecialOfferType" - -export class Offer { - - public constructor(public readonly offerType: SpecialOfferType, - public readonly product: Product, - public readonly argument: number) { - } - - getProduct(): Product { - return this.product; - } - -} diff --git a/typescript/src/model/Product.ts b/typescript/src/model/Product.ts deleted file mode 100644 index 7c9312b..0000000 --- a/typescript/src/model/Product.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {ProductUnit} from "./ProductUnit" - -export class Product { - - constructor(public readonly name: string, - public readonly unit: ProductUnit) { - } -} diff --git a/typescript/src/model/ProductQuantity.ts b/typescript/src/model/ProductQuantity.ts deleted file mode 100644 index f8cc343..0000000 --- a/typescript/src/model/ProductQuantity.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {Product} from "./Product" - -export class ProductQuantity { - - constructor(public readonly product: Product, - public readonly quantity: number) { - this.product = product; - this.quantity = quantity; - } -} diff --git a/typescript/src/model/ProductUnit.ts b/typescript/src/model/ProductUnit.ts deleted file mode 100644 index b013064..0000000 --- a/typescript/src/model/ProductUnit.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export enum ProductUnit { - Kilo, Each -} diff --git a/typescript/src/model/Receipt.ts b/typescript/src/model/Receipt.ts deleted file mode 100644 index 902206e..0000000 --- a/typescript/src/model/Receipt.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {Discount} from "./Discount" -import {Product} from "./Product" -import {ReceiptItem} from "./ReceiptItem" -import * as _ from "lodash" - -export class Receipt { - private items: ReceiptItem[] = []; - private discounts: Discount[] = []; - - public getTotalPrice(): number { - let total = 0.0; - for (let item of this.items) { - total += item.totalPrice; - } - for ( let discount of this.discounts) { - total -= discount.discountAmount; - } - return total; - } - - public addProduct( p: Product, quantity: number, price: number, totalPrice: number): void { - this.items.push(new ReceiptItem(p, quantity, price, totalPrice)); - } - - public getItems(): ReceiptItem[] { - return _.clone(this.items); - } - - public addDiscount( discount: Discount): void { - this.discounts.push(discount); - } - - public getDiscounts(): Discount[] { - return this.discounts; - } -} diff --git a/typescript/src/model/ReceiptItem.ts b/typescript/src/model/ReceiptItem.ts deleted file mode 100644 index 9bd79dd..0000000 --- a/typescript/src/model/ReceiptItem.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {Product} from "./Product" - -export class ReceiptItem { - - public constructor(public readonly product: Product, - public readonly quantity: number, - public readonly price: number, - public totalPrice: number) { - } -} diff --git a/typescript/src/model/ShoppingCart.ts b/typescript/src/model/ShoppingCart.ts deleted file mode 100644 index ac090cc..0000000 --- a/typescript/src/model/ShoppingCart.ts +++ /dev/null @@ -1,91 +0,0 @@ -import {Product} from "./Product" -import {SupermarketCatalog} from "./SupermarketCatalog" -import * as _ from "lodash" -import {ProductQuantity} from "./ProductQuantity" -import {Discount} from "./Discount" -import {Receipt} from "./Receipt" -import {Offer} from "./Offer" -import {SpecialOfferType} from "./SpecialOfferType" - -type ProductQuantities = { [productName: string]: ProductQuantity } -export type OffersByProduct = {[productName: string]: Offer}; - -export class ShoppingCart { - - private readonly items: ProductQuantity[] = []; - _productQuantities: ProductQuantities = {}; - - - getItems(): ProductQuantity[] { - return _.clone(this.items); - } - - addItem(product: Product): void { - this.addItemQuantity(product, 1.0); - } - - productQuantities(): ProductQuantities { - return this._productQuantities; - } - - - public addItemQuantity(product: Product, quantity: number): void { - let productQuantity = new ProductQuantity(product, quantity) - this.items.push(productQuantity); - let currentQuantity = this._productQuantities[product.name] - if (currentQuantity) { - this._productQuantities[product.name] = this.increaseQuantity(product, currentQuantity, quantity); - } else { - this._productQuantities[product.name] = productQuantity; - } - - } - - private increaseQuantity(product: Product, productQuantity: ProductQuantity, quantity: number) { - return new ProductQuantity(product, productQuantity.quantity + quantity) - } - - handleOffers(receipt: Receipt, offers: OffersByProduct, catalog: SupermarketCatalog ):void { - for (const productName in this.productQuantities()) { - const productQuantity = this._productQuantities[productName] - const product = productQuantity.product; - const quantity: number = this._productQuantities[productName].quantity; - if (offers[productName]) { - const offer : Offer = offers[productName]; - const unitPrice: number= catalog.getUnitPrice(product); - let quantityAsInt = quantity; - let discount : Discount|null = null; - let x = 1; - if (offer.offerType == SpecialOfferType.ThreeForTwo) { - x = 3; - - } else if (offer.offerType == SpecialOfferType.TwoForAmount) { - x = 2; - if (quantityAsInt >= 2) { - const total = offer.argument * Math.floor(quantityAsInt / x) + quantityAsInt % 2 * unitPrice; - const discountN = unitPrice * quantity - total; - discount = new Discount(product, "2 for " + offer.argument, discountN); - } - - } if (offer.offerType == SpecialOfferType.FiveForAmount) { - x = 5; - } - const numberOfXs = Math.floor(quantityAsInt / x); - if (offer.offerType == SpecialOfferType.ThreeForTwo && quantityAsInt > 2) { - const discountAmount = quantity * unitPrice - ((numberOfXs * 2 * unitPrice) + quantityAsInt % 3 * unitPrice); - discount = new Discount(product, "3 for 2", discountAmount); - } - if (offer.offerType == SpecialOfferType.TenPercentDiscount) { - discount = new Discount(product, offer.argument + "% off", quantity * unitPrice * offer.argument / 100.0); - } - if (offer.offerType == SpecialOfferType.FiveForAmount && quantityAsInt >= 5) { - const discountTotal = unitPrice * quantity - (offer.argument * numberOfXs + quantityAsInt % 5 * unitPrice); - discount = new Discount(product, x + " for " + offer.argument, discountTotal); - } - if (discount != null) - receipt.addDiscount(discount); - } - - } - } -} diff --git a/typescript/src/model/SpecialOfferType.ts b/typescript/src/model/SpecialOfferType.ts deleted file mode 100644 index 6dae8cf..0000000 --- a/typescript/src/model/SpecialOfferType.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export enum SpecialOfferType { - ThreeForTwo, TenPercentDiscount, TwoForAmount, FiveForAmount -} diff --git a/typescript/src/model/SupermarketCatalog.ts b/typescript/src/model/SupermarketCatalog.ts deleted file mode 100644 index 80b6639..0000000 --- a/typescript/src/model/SupermarketCatalog.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {Product} from "./Product" - -export interface SupermarketCatalog { - addProduct(product: Product , price: number): void; - - getUnitPrice(product: Product): number; - -} diff --git a/typescript/src/model/Teller.ts b/typescript/src/model/Teller.ts deleted file mode 100644 index 97e626c..0000000 --- a/typescript/src/model/Teller.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {SupermarketCatalog} from "./SupermarketCatalog" -import {OffersByProduct, ShoppingCart} from "./ShoppingCart" -import {Product} from "./Product" -import {Receipt} from "./Receipt" -import {Offer} from "./Offer" -import {SpecialOfferType} from "./SpecialOfferType" - -export class Teller { - - private offers: OffersByProduct = {}; - - public constructor(private readonly catalog: SupermarketCatalog ) { - } - - public addSpecialOffer(offerType: SpecialOfferType , product: Product, argument: number): void { - this.offers[product.name] = new Offer(offerType, product, argument); - } - - public checksOutArticlesFrom(theCart: ShoppingCart): Receipt { - const receipt = new Receipt(); - const productQuantities = theCart.getItems(); - for (let pq of productQuantities) { - let p = pq.product; - let quantity = pq.quantity; - let unitPrice = this.catalog.getUnitPrice(p); - let price = quantity * unitPrice; - receipt.addProduct(p, quantity, unitPrice, price); - } - theCart.handleOffers(receipt, this.offers, this.catalog); - - return receipt; - } - -} diff --git a/typescript/test/FakeCatalog.ts b/typescript/test/FakeCatalog.ts deleted file mode 100644 index 1d703d2..0000000 --- a/typescript/test/FakeCatalog.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {Product} from "../src/model/Product" -import {SupermarketCatalog} from "../src/model/SupermarketCatalog" - -export class FakeCatalog implements SupermarketCatalog { - private products: {[key: string]: Product} = {}; - private prices: {[key: string]: number} = {}; - - public addProduct(product: Product, price: number): void { - this.products[product.name] = product; - this.prices[product.name] = price; - } - - public getUnitPrice(p: Product): number { - return this.prices[p.name]; - } -} diff --git a/typescript/test/Supermarket.test.ts b/typescript/test/Supermarket.test.ts deleted file mode 100644 index 86be68e..0000000 --- a/typescript/test/Supermarket.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {FakeCatalog} from "./FakeCatalog" -import {Product} from "../src/model/Product" -import {SupermarketCatalog} from "../src/model/SupermarketCatalog" -import {Receipt} from "../src/model/Receipt" -import {ShoppingCart} from "../src/model/ShoppingCart" -import {Teller} from "../src/model/Teller" -import {SpecialOfferType} from "../src/model/SpecialOfferType" -import {ProductUnit} from "../src/model/ProductUnit" -import {assert} from "chai"; - -describe('Supermarket', () => { - it('Ten percent discount', () => { - // ARRANGE - const catalog: SupermarketCatalog = new FakeCatalog(); - const toothbrush: Product = new Product("toothbrush", ProductUnit.Each); - catalog.addProduct(toothbrush, 0.99); - const apples: Product = new Product("apples", ProductUnit.Kilo); - catalog.addProduct(apples, 1.99); - - const teller: Teller = new Teller(catalog); - teller.addSpecialOffer(SpecialOfferType.TenPercentDiscount, toothbrush, 10.0); - - const cart: ShoppingCart = new ShoppingCart(); - cart.addItemQuantity(apples, 2.5); - - // ACT - const receipt: Receipt = teller.checksOutArticlesFrom(cart); - - // ASSERT - assert.approximately(receipt.getTotalPrice(), 4.975, 0.01); - assert.isEmpty(receipt.getDiscounts()); - assert.equal(receipt.getItems().length, 1); - const receiptItem = receipt.getItems()[0]; - assert.equal(receiptItem.product, apples); - assert.equal(receiptItem.price, 1.99); - assert.approximately(receiptItem.totalPrice, 2.5*1.99, 0.01); - assert.equal(receiptItem.quantity, 2.5); - }); -}); diff --git a/typescript/tsconfig.json b/typescript/tsconfig.json deleted file mode 100644 index 10b0ddf..0000000 --- a/typescript/tsconfig.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "compilerOptions": { - /* Basic Options */ - "target": "ES2016", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - } -}