diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..8de2ef94 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,8 @@ +# READ ME BEFORE SUBMITTING A PR + +Please do not submit a PR with your solution to the Gilded Rose Kata. This repo is intended to be used as a starting point for the kata. + +- [ ] I acknowledge that this PR is not a solution to the Gilded Rose Kata, but an improvement to the template. +- [ ] I acknowledge that I have read [CONTRIBUTING.md](https://github.com/emilybache/GildedRose-Refactoring-Kata/blob/main/CONTRIBUTING.md) + +## Please provide your PR description below this line diff --git a/.github/workflows/jq.yml b/.github/workflows/jq.yml new file mode 100644 index 00000000..887aa808 --- /dev/null +++ b/.github/workflows/jq.yml @@ -0,0 +1,39 @@ +name: jq + +on: + pull_request: + branches: + - main + paths: + - jq/* + +jobs: + test-jq-translation: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Rust Toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + # TODO: this step takes ~2m, consider using https://github.com/marketplace/actions/cargo-install + - name: Install jaq + run: | + cargo install --locked --git https://github.com/01mf02/jaq + + - name: Expect unit test to fail + working-directory: jq + run: | + ! ./test-gilded-rose.sh + + - name: Expect texttest fixture output (aka 'verify') + working-directory: jq + run: | + jaq --arg days 31 -nr "$(cat gilded-rose.jq) $(cat texttest_fixture.jq)" | + diff - ../texttests/ThirtyDays/stdout.gr diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml new file mode 100644 index 00000000..cc00c12e --- /dev/null +++ b/.github/workflows/pr-validation.yml @@ -0,0 +1,36 @@ +name: "PR Tasks Completed Check" +on: + pull_request: + types: opened + +jobs: + task-check: + # Only run from the base repository + if: github.event.repository.name == 'emilybache/GildedRose-Refactoring-Kata' + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - name: Comment if checkmark is missing + if: ${{ !contains(github.event.pull_request.body, '[X] I acknowledge') }} + uses: actions/github-script@v6 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: "Please don't submit a Pull Request containing a Kata solution to the original repo from Emily Bache. If you are instead trying to add an improvement, please resubmit this PR and check the `[X]` box in the PR template." + }) + - name: Close PR if checkmark is missing + if: ${{ !contains(github.event.pull_request.body, '[X] I acknowledge') }} + uses: actions/github-script@v6 + with: + script: | + github.rest.pulls.update({ + pull_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + state: 'closed' + }) diff --git a/.gitignore b/.gitignore index 8a0ce6bc..4232b6bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,19 @@ +<<<<<<< HEAD *.pyc .cache .coverage .idea/ *.iml +======= +bin +obj +*.sln.DotSettings.user +.vs +vendor +.idea +*.iml +**/*.received.* +venv +**/DS_Store/* +**/.DS_Store/* +>>>>>>> e2abba77cb5a395702f237e428b639f2129b1f07 diff --git a/Ada/.gitignore b/Ada/.gitignore new file mode 100644 index 00000000..c1446c8c --- /dev/null +++ b/Ada/.gitignore @@ -0,0 +1,6 @@ +*.o +*.ali +b~*.ads +b~*.adb +program +gilded_rose_tester diff --git a/Ada/Makefile b/Ada/Makefile new file mode 100644 index 00000000..e2fef701 --- /dev/null +++ b/Ada/Makefile @@ -0,0 +1,23 @@ +AHVEN_INCLUDE:=-aI/usr/share/ada/adainclude/ahven -aI/usr/include/ahven +AHVEN_LIBS:=-aO/usr/lib/x86_64-linux-gnu/ada/adalib/ahven -aO/usr/lib/ahven -aO/usr/lib -shared +ADAFLAGS:=-gnat2012 $(AHVEN_INCLUDE) $(AHVEN_LIBS) +LIBS:=-largs -lahven + +.PHONY: all +all: gilded_rose_tester program + +.PHONY: gilded_rose_tester +gilded_rose_tester: + @gnatmake -q $(ADAFLAGS) gilded_rose_tester $(LIBS) + +.PHONY: test +test: gilded_rose_tester + @./gilded_rose_tester + +.PHONY: program +program: + @gnatmake -q $(ADAFLAGS) program $(LIBS) + +.PHONY: clean +clean: + @gnatclean -q gilded_rose_tester program diff --git a/Ada/README.txt b/Ada/README.txt new file mode 100644 index 00000000..f0865543 --- /dev/null +++ b/Ada/README.txt @@ -0,0 +1,8 @@ +You will need the following programs: + + - GNU make (on Debian-like systems do 'apt-get install make') + - gcc with Ada enabled ('apt-get install gnat') + - the ahven unit-test framework ('apt-get install libahven5-dev') + +'make test' runs your tests once. + diff --git a/Ada/ThirtyDays.txt b/Ada/ThirtyDays.txt new file mode 100644 index 00000000..a04e48ac --- /dev/null +++ b/Ada/ThirtyDays.txt @@ -0,0 +1,373 @@ +OMGHAI! +-------- day 0 -------- +name, sellIn, quality ++5 Dexterity Vest, 10, 20 +Aged Brie, 2, 0 +Elixir of the Mongoose, 5, 7 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 15, 20 +Backstage passes to a TAFKAL80ETC concert, 10, 49 +Backstage passes to a TAFKAL80ETC concert, 5, 49 +Conjured Mana Cake, 3, 6 + +-------- day 1 -------- +name, sellIn, quality ++5 Dexterity Vest, 9, 19 +Aged Brie, 1, 1 +Elixir of the Mongoose, 4, 6 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 14, 21 +Backstage passes to a TAFKAL80ETC concert, 9, 50 +Backstage passes to a TAFKAL80ETC concert, 4, 50 +Conjured Mana Cake, 2, 5 + +-------- day 2 -------- +name, sellIn, quality ++5 Dexterity Vest, 8, 18 +Aged Brie, 0, 2 +Elixir of the Mongoose, 3, 5 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 13, 22 +Backstage passes to a TAFKAL80ETC concert, 8, 50 +Backstage passes to a TAFKAL80ETC concert, 3, 50 +Conjured Mana Cake, 1, 4 + +-------- day 3 -------- +name, sellIn, quality ++5 Dexterity Vest, 7, 17 +Aged Brie, -1, 4 +Elixir of the Mongoose, 2, 4 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 12, 23 +Backstage passes to a TAFKAL80ETC concert, 7, 50 +Backstage passes to a TAFKAL80ETC concert, 2, 50 +Conjured Mana Cake, 0, 3 + +-------- day 4 -------- +name, sellIn, quality ++5 Dexterity Vest, 6, 16 +Aged Brie, -2, 6 +Elixir of the Mongoose, 1, 3 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 11, 24 +Backstage passes to a TAFKAL80ETC concert, 6, 50 +Backstage passes to a TAFKAL80ETC concert, 1, 50 +Conjured Mana Cake, -1, 1 + +-------- day 5 -------- +name, sellIn, quality ++5 Dexterity Vest, 5, 15 +Aged Brie, -3, 8 +Elixir of the Mongoose, 0, 2 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 10, 25 +Backstage passes to a TAFKAL80ETC concert, 5, 50 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Conjured Mana Cake, -2, 0 + +-------- day 6 -------- +name, sellIn, quality ++5 Dexterity Vest, 4, 14 +Aged Brie, -4, 10 +Elixir of the Mongoose, -1, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 9, 27 +Backstage passes to a TAFKAL80ETC concert, 4, 50 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Conjured Mana Cake, -3, 0 + +-------- day 7 -------- +name, sellIn, quality ++5 Dexterity Vest, 3, 13 +Aged Brie, -5, 12 +Elixir of the Mongoose, -2, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 8, 29 +Backstage passes to a TAFKAL80ETC concert, 3, 50 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Conjured Mana Cake, -4, 0 + +-------- day 8 -------- +name, sellIn, quality ++5 Dexterity Vest, 2, 12 +Aged Brie, -6, 14 +Elixir of the Mongoose, -3, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 7, 31 +Backstage passes to a TAFKAL80ETC concert, 2, 50 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Conjured Mana Cake, -5, 0 + +-------- day 9 -------- +name, sellIn, quality ++5 Dexterity Vest, 1, 11 +Aged Brie, -7, 16 +Elixir of the Mongoose, -4, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 6, 33 +Backstage passes to a TAFKAL80ETC concert, 1, 50 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Conjured Mana Cake, -6, 0 + +-------- day 10 -------- +name, sellIn, quality ++5 Dexterity Vest, 0, 10 +Aged Brie, -8, 18 +Elixir of the Mongoose, -5, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 5, 35 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Conjured Mana Cake, -7, 0 + +-------- day 11 -------- +name, sellIn, quality ++5 Dexterity Vest, -1, 8 +Aged Brie, -9, 20 +Elixir of the Mongoose, -6, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 4, 38 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Conjured Mana Cake, -8, 0 + +-------- day 12 -------- +name, sellIn, quality ++5 Dexterity Vest, -2, 6 +Aged Brie, -10, 22 +Elixir of the Mongoose, -7, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 3, 41 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Conjured Mana Cake, -9, 0 + +-------- day 13 -------- +name, sellIn, quality ++5 Dexterity Vest, -3, 4 +Aged Brie, -11, 24 +Elixir of the Mongoose, -8, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 2, 44 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Conjured Mana Cake, -10, 0 + +-------- day 14 -------- +name, sellIn, quality ++5 Dexterity Vest, -4, 2 +Aged Brie, -12, 26 +Elixir of the Mongoose, -9, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 1, 47 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Conjured Mana Cake, -11, 0 + +-------- day 15 -------- +name, sellIn, quality ++5 Dexterity Vest, -5, 0 +Aged Brie, -13, 28 +Elixir of the Mongoose, -10, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Conjured Mana Cake, -12, 0 + +-------- day 16 -------- +name, sellIn, quality ++5 Dexterity Vest, -6, 0 +Aged Brie, -14, 30 +Elixir of the Mongoose, -11, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Conjured Mana Cake, -13, 0 + +-------- day 17 -------- +name, sellIn, quality ++5 Dexterity Vest, -7, 0 +Aged Brie, -15, 32 +Elixir of the Mongoose, -12, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Conjured Mana Cake, -14, 0 + +-------- day 18 -------- +name, sellIn, quality ++5 Dexterity Vest, -8, 0 +Aged Brie, -16, 34 +Elixir of the Mongoose, -13, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Conjured Mana Cake, -15, 0 + +-------- day 19 -------- +name, sellIn, quality ++5 Dexterity Vest, -9, 0 +Aged Brie, -17, 36 +Elixir of the Mongoose, -14, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Conjured Mana Cake, -16, 0 + +-------- day 20 -------- +name, sellIn, quality ++5 Dexterity Vest, -10, 0 +Aged Brie, -18, 38 +Elixir of the Mongoose, -15, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Conjured Mana Cake, -17, 0 + +-------- day 21 -------- +name, sellIn, quality ++5 Dexterity Vest, -11, 0 +Aged Brie, -19, 40 +Elixir of the Mongoose, -16, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Backstage passes to a TAFKAL80ETC concert, -16, 0 +Conjured Mana Cake, -18, 0 + +-------- day 22 -------- +name, sellIn, quality ++5 Dexterity Vest, -12, 0 +Aged Brie, -20, 42 +Elixir of the Mongoose, -17, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Backstage passes to a TAFKAL80ETC concert, -17, 0 +Conjured Mana Cake, -19, 0 + +-------- day 23 -------- +name, sellIn, quality ++5 Dexterity Vest, -13, 0 +Aged Brie, -21, 44 +Elixir of the Mongoose, -18, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Backstage passes to a TAFKAL80ETC concert, -18, 0 +Conjured Mana Cake, -20, 0 + +-------- day 24 -------- +name, sellIn, quality ++5 Dexterity Vest, -14, 0 +Aged Brie, -22, 46 +Elixir of the Mongoose, -19, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Backstage passes to a TAFKAL80ETC concert, -19, 0 +Conjured Mana Cake, -21, 0 + +-------- day 25 -------- +name, sellIn, quality ++5 Dexterity Vest, -15, 0 +Aged Brie, -23, 48 +Elixir of the Mongoose, -20, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Backstage passes to a TAFKAL80ETC concert, -20, 0 +Conjured Mana Cake, -22, 0 + +-------- day 26 -------- +name, sellIn, quality ++5 Dexterity Vest, -16, 0 +Aged Brie, -24, 50 +Elixir of the Mongoose, -21, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Backstage passes to a TAFKAL80ETC concert, -16, 0 +Backstage passes to a TAFKAL80ETC concert, -21, 0 +Conjured Mana Cake, -23, 0 + +-------- day 27 -------- +name, sellIn, quality ++5 Dexterity Vest, -17, 0 +Aged Brie, -25, 50 +Elixir of the Mongoose, -22, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Backstage passes to a TAFKAL80ETC concert, -17, 0 +Backstage passes to a TAFKAL80ETC concert, -22, 0 +Conjured Mana Cake, -24, 0 + +-------- day 28 -------- +name, sellIn, quality ++5 Dexterity Vest, -18, 0 +Aged Brie, -26, 50 +Elixir of the Mongoose, -23, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Backstage passes to a TAFKAL80ETC concert, -18, 0 +Backstage passes to a TAFKAL80ETC concert, -23, 0 +Conjured Mana Cake, -25, 0 + +-------- day 29 -------- +name, sellIn, quality ++5 Dexterity Vest, -19, 0 +Aged Brie, -27, 50 +Elixir of the Mongoose, -24, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Backstage passes to a TAFKAL80ETC concert, -19, 0 +Backstage passes to a TAFKAL80ETC concert, -24, 0 +Conjured Mana Cake, -26, 0 + +-------- day 30 -------- +name, sellIn, quality ++5 Dexterity Vest, -20, 0 +Aged Brie, -28, 50 +Elixir of the Mongoose, -25, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Backstage passes to a TAFKAL80ETC concert, -20, 0 +Backstage passes to a TAFKAL80ETC concert, -25, 0 +Conjured Mana Cake, -27, 0 + diff --git a/Ada/gilded_rose.adb b/Ada/gilded_rose.adb new file mode 100644 index 00000000..fdcc5aa3 --- /dev/null +++ b/Ada/gilded_rose.adb @@ -0,0 +1,62 @@ +with Ada.Strings.Unbounded; +use Ada.Strings.Unbounded; + +package body Gilded_Rose is + procedure Update_Quality(Self : in out Gilded_Rose) is + Cursor : Item_Vecs.Cursor := Item_Vecs.First(Self.Items); + begin + while Item_Vecs.Has_Element(Cursor) loop + if Self.Items(Cursor).Name /= To_Unbounded_String("Aged Brie") + and Self.Items(Cursor).Name /= To_Unbounded_String("Backstage passes to a TAFKAL80ETC concert") then + if Self.Items(Cursor).Quality > 0 then + if Self.Items(Cursor).Name /= To_Unbounded_String("Sulfuras, Hand of Ragnaros") then + Self.Items(Cursor).Quality := Self.Items(Cursor).Quality - 1; + end if; + end if; + else + if Self.Items(Cursor).Quality < 50 then + Self.Items(Cursor).Quality := Self.Items(Cursor).Quality + 1; + + if Self.Items(Cursor).Name = To_Unbounded_String("Backstage passes to a TAFKAL80ETC concert") then + if Self.Items(Cursor).Sell_In < 11 then + if Self.Items(Cursor).Quality < 50 then + Self.Items(Cursor).Quality := Self.Items(Cursor).Quality + 1; + end if; + end if; + + if Self.Items(Cursor).Sell_In < 6 then + if Self.Items(Cursor).Quality < 50 then + Self.Items(Cursor).Quality := Self.Items(Cursor).Quality + 1; + end if; + end if; + end if; + end if; + end if; + + if Self.Items(Cursor).Name /= To_Unbounded_String("Sulfuras, Hand of Ragnaros") then + Self.Items(Cursor).Sell_In := Self.Items(Cursor).Sell_In - 1; + end if; + + if Self.Items(Cursor).Sell_In < 0 then + if Self.Items(Cursor).Name /= To_Unbounded_String("Aged Brie") then + if Self.Items(Cursor).Name /= To_Unbounded_String("Backstage passes to a TAFKAL80ETC concert") then + if Self.Items(Cursor).Quality > 0 then + if Self.Items(Cursor).Name /= To_Unbounded_String("Sulfuras, Hand of Ragnaros") then + Self.Items(Cursor).Quality := Self.Items(Cursor).Quality - 1; + end if; + end if; + else + Self.Items(Cursor).Quality := Self.Items(Cursor).Quality - Self.Items(Cursor).Quality; + end if; + else + if Self.Items(Cursor).Quality < 50 then + Self.Items(Cursor).Quality := Self.Items(Cursor).Quality + 1; + end if; + end if; + end if; + + Item_Vecs.Next(Cursor); + end loop; + end; + +end Gilded_Rose; diff --git a/Ada/gilded_rose.ads b/Ada/gilded_rose.ads new file mode 100644 index 00000000..64fc0ad6 --- /dev/null +++ b/Ada/gilded_rose.ads @@ -0,0 +1,15 @@ +with Ada.Containers.Vectors; +with Items; +use Items; + +package Gilded_Rose is + package Item_Vecs is new Ada.Containers.Vectors ( + Element_Type => Item, + Index_Type => Positive + ); + + type Gilded_Rose is record + Items : Item_Vecs.Vector; + end record; + procedure Update_Quality(Self : in out Gilded_Rose); +end Gilded_Rose; diff --git a/Ada/gilded_rose_tester.adb b/Ada/gilded_rose_tester.adb new file mode 100644 index 00000000..938e16a2 --- /dev/null +++ b/Ada/gilded_rose_tester.adb @@ -0,0 +1,10 @@ +with Ahven.Text_Runner; +with Ahven.Framework; +with Gilded_Rose_Tests; + +procedure Gilded_Rose_Tester is + S : Ahven.Framework.Test_Suite := Ahven.Framework.Create_Suite("All"); +begin + Ahven.Framework.Add_Test(S, new Gilded_Rose_Tests.Test); + Ahven.Text_Runner.Run(S); +end Gilded_Rose_Tester; diff --git a/Ada/gilded_rose_tests.adb b/Ada/gilded_rose_tests.adb new file mode 100644 index 00000000..537f6cb8 --- /dev/null +++ b/Ada/gilded_rose_tests.adb @@ -0,0 +1,38 @@ +with Gilded_Rose; +use Gilded_Rose; + +with Items; +use Items; + +with Ahven; + +with Ada.Strings.Unbounded; + +package body Gilded_Rose_Tests is + procedure Initialize(T : in out Test) is + begin + Set_Name(T, "Gilded_Rose_Test"); + Ahven.Framework.Add_Test_Routine(T, Test_Gilded_Rose'Access, "Foo"); + end Initialize; + + procedure Test_Gilded_Rose is + Things : Item_Vecs.Vector; + begin + Things.Append(New_Item => + (Name => SU.To_Unbounded_String("Foo"), + Sell_In => 0, + Quality => 0)); + declare + App : Gilded_Rose.Gilded_Rose := (Items => Things); + + package SU renames Ada.Strings.Unbounded; + procedure Assert_Eq_Unbounded_String is + new Ahven.Assert_Equal(Data_Type => SU.Unbounded_String, Image => SU.To_String); + begin + Update_Quality(App); + Assert_Eq_Unbounded_String(Actual => App.Items(Item_Vecs.First(App.Items)).Name, + Expected => SU.To_Unbounded_String("fixme"), + Message => "fixme"); + end; + end; +end Gilded_Rose_Tests; diff --git a/Ada/gilded_rose_tests.ads b/Ada/gilded_rose_tests.ads new file mode 100644 index 00000000..f6c1b40c --- /dev/null +++ b/Ada/gilded_rose_tests.ads @@ -0,0 +1,11 @@ +with Ahven.Framework; + +package Gilded_Rose_Tests is + type Test is new Ahven.Framework.Test_Case with null record; + + procedure Initialize(T : in out Test); + + private + + procedure Test_Gilded_Rose; +end Gilded_Rose_Tests; diff --git a/Ada/items.adb b/Ada/items.adb new file mode 100644 index 00000000..5348d64d --- /dev/null +++ b/Ada/items.adb @@ -0,0 +1,31 @@ +with Ada.Strings.Unbounded; +use Ada.Strings.Unbounded; + +package body Items is + function Integer_Image(Self : in Integer) return String is + Img : constant String := Integer'Image(Self); + begin + if Self < 0 then + return Img; + else + return Img(2 .. Img'Length); + end if; + end; + + function To_String(Self : in Item) return String is + Name : constant Unbounded_String := Self.Name; + Sell_In : constant Unbounded_String := To_Unbounded_String(Integer_Image(Self.Sell_In)); + Quality : constant Unbounded_String := To_Unbounded_String(Integer_Image(Self.Quality)); + + Ret : Unbounded_String; + begin + Append(Ret, Name); + Append(Ret, ", "); + Append(Ret, Sell_In); + Append(Ret, ", "); + Append(Ret, Quality); + + return To_String(Ret); + end; + +end Items; diff --git a/Ada/items.ads b/Ada/items.ads new file mode 100644 index 00000000..a2a588d4 --- /dev/null +++ b/Ada/items.ads @@ -0,0 +1,14 @@ +with Ada.Strings.Unbounded; + +package Items is + package SU renames Ada.Strings.Unbounded; + + type Item is record + Name : SU.Unbounded_String; + Sell_In : Integer; + Quality : Integer; + end record; + + function To_String(Self : in Item) return String; + +end Items; diff --git a/Ada/program.adb b/Ada/program.adb new file mode 100644 index 00000000..336f952a --- /dev/null +++ b/Ada/program.adb @@ -0,0 +1,72 @@ +with Ada.Text_IO; +use Ada.Text_IO; + +with Items; +use Items; + +with Ada.Strings.Unbounded; +use Ada.Strings.Unbounded; + +with Gilded_Rose; +use Gilded_Rose; + +procedure Program is + Things : Item_Vecs.Vector; +begin + Things.Append(New_Item => + (Name => To_Unbounded_String("+5 Dexterity Vest"), + Sell_In => 10, + Quality => 20)); + Things.Append(New_Item => + (Name => To_Unbounded_String("Aged Brie"), + Sell_In => 2, + Quality => 0)); + Things.Append(New_Item => + (Name => To_Unbounded_String("Elixir of the Mongoose"), + Sell_In => 5, + Quality => 7)); + Things.Append(New_Item => + (Name => To_Unbounded_String("Sulfuras, Hand of Ragnaros"), + Sell_In => 0, + Quality => 80)); + Things.Append(New_Item => + (Name => To_Unbounded_String("Sulfuras, Hand of Ragnaros"), + Sell_In => -1, + Quality => 80)); + Things.Append(New_Item => + (Name => To_Unbounded_String("Backstage passes to a TAFKAL80ETC concert"), + Sell_In => 15, + Quality => 20)); + Things.Append(New_Item => + (Name => To_Unbounded_String("Backstage passes to a TAFKAL80ETC concert"), + Sell_In => 10, + Quality => 49)); + Things.Append(New_Item => + (Name => To_Unbounded_String("Backstage passes to a TAFKAL80ETC concert"), + Sell_In => 5, + Quality => 49)); + -- this conjured item does not work properly yet + Things.Append(New_Item => + (Name => To_Unbounded_String("Conjured Mana Cake"), + Sell_In => 3, + Quality => 6)); + + + declare + App : Gilded_Rose.Gilded_Rose := (Items => Things); + begin + Put_Line("OMGHAI!"); + + for I in 0 .. 30 loop + Put_Line("-------- day" & Integer'Image(I) & " --------"); + Put_Line("name, sellIn, quality"); + + for Each of App.Items loop + Put_Line(To_String(Each)); + end loop; + Put_Line(""); + + Update_Quality(App); + end loop; + end; +end; diff --git a/C/GildedRose.c b/C/GildedRose.c new file mode 100644 index 00000000..5c86edc3 --- /dev/null +++ b/C/GildedRose.c @@ -0,0 +1,98 @@ +#include +#include "GildedRose.h" +#include + +Item* +init_item(Item* item, const char *name, int sellIn, int quality) +{ + item->sellIn = sellIn; + item->quality = quality; + item->name = strdup(name); + + return item; +} + +extern char* +print_item(char* buffer, Item* item) +{ + sprintf(buffer, "%s, %d, %d", item->name, item->sellIn, item->quality); +} + +void +update_quality(Item items[], int size) +{ + int i; + + for (i = 0; i < size; i++) + { + if (strcmp(items[i].name, "Aged Brie") && strcmp(items[i].name, "Backstage passes to a TAFKAL80ETC concert")) + { + if (items[i].quality > 0) + { + if (strcmp(items[i].name, "Sulfuras, Hand of Ragnaros")) + { + items[i].quality = items[i].quality - 1; + } + } + } + else + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + + if (!strcmp(items[i].name, "Backstage passes to a TAFKAL80ETC concert")) + { + if (items[i].sellIn < 11) + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + + if (items[i].sellIn < 6) + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + } + } + } + + if (strcmp(items[i].name, "Sulfuras, Hand of Ragnaros")) + { + items[i].sellIn = items[i].sellIn - 1; + } + + if (items[i].sellIn < 0) + { + if (strcmp(items[i].name, "Aged Brie")) + { + if (strcmp(items[i].name, "Backstage passes to a TAFKAL80ETC concert")) + { + if (items[i].quality > 0) + { + if (strcmp(items[i].name, "Sulfuras, Hand of Ragnaros")) + { + items[i].quality = items[i].quality - 1; + } + } + } + else + { + items[i].quality = items[i].quality - items[i].quality; + } + } + else + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + } + } +} diff --git a/C/GildedRose.h b/C/GildedRose.h new file mode 100644 index 00000000..9883d7a9 --- /dev/null +++ b/C/GildedRose.h @@ -0,0 +1,15 @@ +#ifndef ROSE_INCLUDED +#define ROSE_INCLUDED + +typedef struct +{ + char *name; + int sellIn; + int quality; +} Item; + +extern Item* init_item(Item* item, const char *name, int sellIn, int quality); +extern void update_quality(Item items[], int size); +extern char* print_item(char* buffer, Item* item); + +#endif \ No newline at end of file diff --git a/C/GildedRoseTextTests.c b/C/GildedRoseTextTests.c new file mode 100644 index 00000000..d200ca0c --- /dev/null +++ b/C/GildedRoseTextTests.c @@ -0,0 +1,43 @@ +#include +#include "GildedRose.h" + +int +print_item(Item *item) +{ + return printf("%s, %d, %d\n", item->name, item->sellIn, item->quality); +} + +int main() +{ + Item items[9]; + int last = 0; + int day; + int index; + + init_item(items + last++, "+5 Dexterity Vest", 10, 20); + init_item(items + last++, "Aged Brie", 2, 0); + init_item(items + last++, "Elixir of the Mongoose", 5, 7); + init_item(items + last++, "Sulfuras, Hand of Ragnaros", 0, 80); + init_item(items + last++, "Sulfuras, Hand of Ragnaros", -1, 80); + init_item(items + last++, "Backstage passes to a TAFKAL80ETC concert", 15, 20); + init_item(items + last++, "Backstage passes to a TAFKAL80ETC concert", 10, 49); + init_item(items + last++, "Backstage passes to a TAFKAL80ETC concert", 5, 49); + // this Conjured item doesn't yet work properly + init_item(items + last++, "Conjured Mana Cake", 3, 6); + + puts("OMGHAI!"); + + for (day = 0; day <= 30; day++) + { + printf("-------- day %d --------\n", day); + printf("name, sellIn, quality\n"); + for(index = 0; index < last; index++) { + print_item(items + index); + } + + printf("\n"); + + update_quality(items, last); + } + return 0; +} diff --git a/C/GildedRoseUnitTests.cc b/C/GildedRoseUnitTests.cc new file mode 100644 index 00000000..ff7dec86 --- /dev/null +++ b/C/GildedRoseUnitTests.cc @@ -0,0 +1,43 @@ +#include +#include +#include + +extern "C" { +#include "GildedRose.h" +} + +TEST_GROUP(TestGildedRoseGroup) +{ + void setup() { + } + void teardown() { + } +}; + +TEST(TestGildedRoseGroup, FirstTest) +{ + Item items[1]; + init_item(items, "Foo", 0, 0); + update_quality(items, 1); + STRCMP_EQUAL("fixme", items[0].name); +} + +void example() +{ + Item items[6]; + int last = 0; + + init_item(items + last++, "+5 Dexterity Vest", 10, 20); + init_item(items + last++, "Aged Brie", 2, 0); + init_item(items + last++, "Elixir of the Mongoose", 5, 7); + init_item(items + last++, "Sulfuras, Hand of Ragnaros", 0, 80); + init_item(items + last++, "Backstage passes to a TAFKAL80ETC concert", 15, 20); + init_item(items + last++, "Conjured Mana Cake", 3, 6); + update_quality(items, last); +} + +int +main(int ac, char** av) +{ + return CommandLineTestRunner::RunAllTests(ac, av); +} diff --git a/C/Makefile b/C/Makefile new file mode 100644 index 00000000..3a695892 --- /dev/null +++ b/C/Makefile @@ -0,0 +1,51 @@ +# Makefile for building the kata file with the Google Testing Framework +# +# SYNOPSIS: +# +# make [all] - makes everything. +# make TARGET - makes the given target. +# make clean - removes all files generated by make. + +# Please tweak the following variable definitions as needed by your +# project. + +# Points to the root of CppUTest, relative to where this file is. +# Remember to tweak this if you move this file. +CPPUTEST_HOME = CppUTest + +# Where to find user code. +USER_DIR = . + +# Flags passed to the preprocessor. +CPPFLAGS += -I$(CPPUTEST_HOME)/include + +# Flags passed to the C++ compiler. +CFLAGS += -g -Wall -Wextra + +LD_LIBRARIES = -L$(CPPUTEST_HOME)/lib -lCppUTest + +# All tests produced by this Makefile. Remember to add new tests you +# created to the list. +TESTS = GildedRoseUnitTests + +TEXTTESTS = GildedRoseTextTests + +# House-keeping build targets. + +all : $(TESTS) $(TEXTTESTS) + +GildedRose.o : $(USER_DIR)/GildedRose.c + +GildedRoseUnitTests : $(USER_DIR)/GildedRoseUnitTests.cc GildedRose.o + $(CXX) $(CPPFLAGS) $(CFLAGS) -o $@ $(USER_DIR)/GildedRoseUnitTests.cc GildedRose.o $(LD_LIBRARIES) + +GildedRoseTextTests.o : $(USER_DIR)/GildedRoseTextTests.c + +GildedRoseTextTests : GildedRoseTextTests.o GildedRose.o + $(CC) $^ -o $@ + +clean : + rm -f $(TESTS) $(TEXTTESTS) *.o *~ + +check-syntax: + gcc $(CPPFLAGS) -o /dev/null -S ${CHK_SOURCES} diff --git a/C/README.txt b/C/README.txt new file mode 100644 index 00000000..2bc1f69b --- /dev/null +++ b/C/README.txt @@ -0,0 +1,5 @@ +run-once.sh runs your tests once + +Assumptions: + - make and a C++ compiler (like gcc) is installed on your system and is in the PATH + - The CppUTest framework is in the directory CppUTest diff --git a/C/run-once.sh b/C/run-once.sh new file mode 100755 index 00000000..4f6b2303 --- /dev/null +++ b/C/run-once.sh @@ -0,0 +1,2 @@ +make +./GildedRoseTextTests diff --git a/COBOL/Gnu/.gitignore b/COBOL/Gnu/.gitignore new file mode 100644 index 00000000..140f8cf8 --- /dev/null +++ b/COBOL/Gnu/.gitignore @@ -0,0 +1 @@ +*.so diff --git a/COBOL/Gnu/Add.cbl b/COBOL/Gnu/Add.cbl new file mode 100644 index 00000000..46436285 --- /dev/null +++ b/COBOL/Gnu/Add.cbl @@ -0,0 +1,38 @@ +program-id. Add as "Add". + +environment division. + +input-output section. + +file-control. + select in-items assign 'in-items'. + +data division. +file section. + fd in-items. + 01 item. + 02 sell-in pic s9(2). + 02 quality pic s9(2). + 02 name pic x(50). + +working-storage section. + 01 accept-item. + 02 sell-in pic s9(2). + 02 quality pic s9(2). + 02 name pic x(50). + + +procedure division. + open extend in-items. + display "name" + accept name in accept-item. + display "sell-in" + accept sell-in in accept-item. + display "quality" + accept quality in accept-item. + move accept-item to item. + write item. + close in-items. +goback. + +end program Add. diff --git a/COBOL/Gnu/GildedRose.cbl b/COBOL/Gnu/GildedRose.cbl new file mode 100644 index 00000000..5a234473 --- /dev/null +++ b/COBOL/Gnu/GildedRose.cbl @@ -0,0 +1,77 @@ +program-id. GildedRose as "GildedRose". + +environment division. + +input-output section. + +file-control. + select in-items assign 'in-items'. + select items assign 'items'. + +data division. +file section. + fd in-items. + 01 in-item pic x(54). + fd items. + 01 item. + 02 sell-in pic s9(2). + 02 quality pic s9(2). + 02 name pic x(50). + +working-storage section. +procedure division. + open input in-items output items. +start-lable. + read in-items end go to end-lable. + move in-item to item. + if name not equal "Aged Brie" and name not equal "Backstage passes to a TAFKAL80ETC concert" + if quality > 0 + if name not equal to "Sulfuras, Hand of Ragnaros" + compute quality = quality - 1 + end-if + end-if + else + if quality < 50 + compute quality = quality + 1 + if name equals "Backstage passes to a TAFKAL80ETC concert" + if sell-in < 11 + if quality < 50 + compute quality = quality + 1 + end-if + end-if + if sell-in < 6 + if quality < 50 + compute quality = quality + 1 + end-if + end-if + end-if + end-if + end-if + if name not equal "Sulfuras, Hand of Ragnaros" + compute sell-in = sell-in - 1 + end-if + if sell-in < 0 + if name is not equal to "Aged Brie" + if name is not equal to "Backstage passes to a TAFKAL80ETC concert" + if quality > 0 + if name is equal to "Sulfuras, Hand of Ragnaros" + compute quality = quality - 1 + end-if + end-if + else + compute quality = quality - quality + end-if + else + if quality < 50 + compute quality = quality + 1 + end-if + end-if + end-if + write item. + go to start-lable. +end-lable. + close items. + close in-items. +goback. + +end program GildedRose. diff --git a/COBOL/Gnu/add.sh b/COBOL/Gnu/add.sh new file mode 100755 index 00000000..a75d0f68 --- /dev/null +++ b/COBOL/Gnu/add.sh @@ -0,0 +1,2 @@ +touch in-items +cobcrun Add \ No newline at end of file diff --git a/COBOL/Gnu/build.sh b/COBOL/Gnu/build.sh new file mode 100755 index 00000000..2e4a3e7d --- /dev/null +++ b/COBOL/Gnu/build.sh @@ -0,0 +1 @@ +cobc --free --std=mf -O *.cbl diff --git a/COBOL/Gnu/run.sh b/COBOL/Gnu/run.sh new file mode 100755 index 00000000..c86e9224 --- /dev/null +++ b/COBOL/Gnu/run.sh @@ -0,0 +1 @@ +cobcrun GildedRose diff --git a/COBOL/Gnu/test.sh b/COBOL/Gnu/test.sh new file mode 100755 index 00000000..de74c645 --- /dev/null +++ b/COBOL/Gnu/test.sh @@ -0,0 +1,2 @@ +./build.sh +./run.sh diff --git a/COBOL/mf/src/.cobolProj b/COBOL/mf/src/.cobolProj new file mode 100644 index 00000000..b47b56d0 --- /dev/null +++ b/COBOL/mf/src/.cobolProj @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/COBOL/mf/src/.gitignore b/COBOL/mf/src/.gitignore new file mode 100644 index 00000000..eea4e81d --- /dev/null +++ b/COBOL/mf/src/.gitignore @@ -0,0 +1,2 @@ +.cobolBuild +/Gilded_Rose.bin/ diff --git a/COBOL/mf/src/.project b/COBOL/mf/src/.project new file mode 100644 index 00000000..a702b492 --- /dev/null +++ b/COBOL/mf/src/.project @@ -0,0 +1,17 @@ + + + Gilded Rose + + + + + + com.microfocus.eclipse.project.cobolBuilder + + + + + + com.microfocus.eclipse.project.cobolNature + + diff --git a/COBOL/mf/src/GildedRose.cbl b/COBOL/mf/src/GildedRose.cbl new file mode 100644 index 00000000..28ff10a2 --- /dev/null +++ b/COBOL/mf/src/GildedRose.cbl @@ -0,0 +1,72 @@ +program-id. GildedRose as "GildedRose". + +file-control. + select in-items assign 'in-items'. + select items assign 'items'. + +data division. +file section. + fd in-items. + 01 in-item pic x(58). + fd items. + 01 item. + 02 sell-in pic 9(4). + 02 quality pic 9(4). + 02 name pic x(50). + +working-storage section. +procedure division. + open input in-items output items. +start-lable. + read in-items end go to end-lable. + move in-item to item. + if name not equal "Aged Brie" and name not equal "Backstage passes to a TAFKAL80ETC concert" + if quality greater then 0 + if name not equal to "Sulfuras, Hand of Ragnaros" + compute quality = quality - 1 + end-if + end-if + else + if quality is less then 50 + compute quality = quality + 1 + if name equals "Backstage passes to a TAFKAL80ETC concert" + if sell-in less then 11 + if quality less then 50 + compute quality = quality + 1 + end-if + end-if + if sell-in less then 6 + if quality less then 50 + compute quality = quality + 1 + end-if + end-if + end-if + end-if + end-if + if name not equal "Sulfuras, Hand of Ragnaros" + compute sell-in = sell-in - 1 + end-if + if sell-in is less then 0 + if name is not equal to "Aged Brie" + if name is not equal to "Backstage passes to a TAFKAL80ETC concert" + if quality is greater then 0 + if name is equal to "Sulfuras, Hand of Ragnaros" + compute quality = quality - 1 + end-if + end-if + else + compute quality = quality - quality + end-if + else + if quality is less then 50 + compute quality = quality + 1 + end-if + end-if + end-if + write item. + go to start-lable. +end-lable. + close items. +goback. + +end program GildedRose. diff --git a/COBOL/mf/test/.cobolProj b/COBOL/mf/test/.cobolProj new file mode 100644 index 00000000..53304fe0 --- /dev/null +++ b/COBOL/mf/test/.cobolProj @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/COBOL/mf/test/.gitignore b/COBOL/mf/test/.gitignore new file mode 100644 index 00000000..21774bfb --- /dev/null +++ b/COBOL/mf/test/.gitignore @@ -0,0 +1,3 @@ +/.cobolBuild +/Coverage/ +/Tests.bin/ diff --git a/COBOL/mf/test/.project b/COBOL/mf/test/.project new file mode 100644 index 00000000..2615ee47 --- /dev/null +++ b/COBOL/mf/test/.project @@ -0,0 +1,23 @@ + + + Tests + + + + + + com.microfocus.eclipse.project.cobolBuilder + + + + + com.microfocus.eclipse.mfunit.core.mfUnitBuilder + + + + + + com.microfocus.eclipse.project.cobolNature + com.microfocus.eclipse.mfunit.core.mfUnitNature + + diff --git a/COBOL/mf/test/TestGildedRose.cbl b/COBOL/mf/test/TestGildedRose.cbl new file mode 100644 index 00000000..d9802330 --- /dev/null +++ b/COBOL/mf/test/TestGildedRose.cbl @@ -0,0 +1,67 @@ + *> Test Fixture for GildedRose, GildedRose + + copy "mfunit_prototypes.cpy". + + program-id. TestGildedRose. + + file-control. + select in-items assign 'in-items'. + select items assign 'items'. + + file section. + fd in-items. + 01 in-item. + 02 sell-in pic 9(4). + 02 quality pic 9(4). + 02 name pic x(50). + fd items. + 01 item. + 02 sell-in pic 9(4). + 02 quality pic 9(4). + 02 name pic x(50). + + working-storage section. + copy "mfunit.cpy". + 78 TEST-TESTGILDEDROSE value "TestGildedRose". + 01 pp procedure-pointer. + + *> Program linkage data + + procedure division. + goback returning 0 + . + + entry MFU-TC-PREFIX & TEST-TESTGILDEDROSE. + open output in-items + move "foo" to name in in-item + move 0 to quality in in-item + move 0 to sell-in in in-item + write in-item + close in-items + call "GildedRose" + open input items + read items + if name in item not equal to "fixme" then + call MFU-ASSERT-FAIL-Z using z"item name was not fixme" + close items + goback + . + + $region TestCase Configuration + + entry MFU-TC-SETUP-PREFIX & TEST-TESTGILDEDROSE. + perform InitializeLinkageData + *> Add any other test setup code here + goback returning 0 + . + + InitializeLinkageData section. + *> Load the library that is being tested + set pp to entry "GildedRose" + + exit section + . + + $end-region + + end program. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..c8a521af --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,44 @@ +Contributing to Gilded Rose Refactoring Kata +====== + +More translations are most welcome! I'm very open for pull requests that +translate the starting position into additional languages. However, please +do **not** open a pull request with your solution! It can be a bit confusing since +GitHub encourages you to do so! Please only send me pull requests if you have a +correction or improvement to the starting position. You don't want to spoil the +fun of doing the exercise for other people! + +# Translating this code + +Please note a translation should ideally include: + +- a translation of the production code for 'update_quality' and Item +- one failing unit test complaining that "fixme" != "foo" +- a TextTest fixture, i.e. a command-line program that runs update_quality on the sample data for the number of days specified + +Please don't write too much code in the starting position or add too many unit +tests. The idea with the one failing unit test is to tempt people to work out +how to fix it, discover it wasn't that hard, and now they understand what this +test is doing they realize they can improve it. + +If your programming language doesn't have an easy way to add a command-line +interface, then the TextTest fixture is probably not necessary. + +# Recommended project structure + +Programming languages have a variety of conventions but the starting points try +to maintain order among languages. Ideally, the 'update_quality' and +Item definitions should be in a file named `gilded_rose` with your language's +conventional casing (e.g. snake case) and location (e.g. `src/`). The "fixme" ! += "foo" test should go in a file `gilded_rose_test` in your language's +conventional location (e.g. `test/`). The TextTest fixture and command-line +program, that simulates update_quality over a number of days, should go in +`program` or `texttest_fixture`. If you can define a default for the number of +days in the simulation please choose two days. + +A single sub-directory per language is not enforced. A language may have +more than one popular unit testing framework. In that case, please add +`{language}-{framework}/` and maintain separation between the projects. In other +words, all the components requested should exist in both sub-directories. +Re-using code between the directories would be confusing for those looking for a +starting point. diff --git a/Delphi/.gitattributes b/Delphi/.gitattributes new file mode 100644 index 00000000..f4e939ab --- /dev/null +++ b/Delphi/.gitattributes @@ -0,0 +1,17 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.groupproj text +*.dpr text +*.dproj text +*.pas text +*.dfm text +*.fmx text + +# Declare files that will always have CRLF line endings on checkout. + +# Denote all files that are truly binary and should not be modified. +*.exe binary +*.res binary diff --git a/Delphi/.gitignore b/Delphi/.gitignore new file mode 100644 index 00000000..1098ab8a --- /dev/null +++ b/Delphi/.gitignore @@ -0,0 +1,71 @@ +# Uncomment these types if you want even more clean repository. But be careful. +# It can make harm to an existing project source. Read explanations below. +# +# Resource files are binaries containing manifest, project icon and version info. +# They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. +*.res +# +# Type library file (binary). In old Delphi versions it should be stored. +# Since Delphi 2009 it is produced from .ridl file and can safely be ignored. +#*.tlb +# +# Diagram Portfolio file. Used by the diagram editor up to Delphi 7. +# Uncomment this if you are not using diagrams or use newer Delphi version. +#*.ddp +# +# Visual LiveBindings file. Added in Delphi XE2. +# Uncomment this if you are not using LiveBindings Designer. +#*.vlb +# +# Deployment Manager configuration file for your project. Added in Delphi XE2. +# Uncomment this if it is not mobile development and you do not use remote debug feature. +#*.deployproj +# +# C++ object files produced when C/C++ Output file generation is configured. +# Uncomment this if you are not using external objects (zlib library for example). +#*.obj +# + +# Delphi compiler-generated binaries (safe to delete) +*.exe +*.dll +*.bpl +*.bpi +*.dcp +*.so +*.apk +*.drc +*.map +*.dres +*.rsm +*.tds +*.dcu +*.lib +*.a +*.o +*.ocx + +# Delphi autogenerated files (duplicated info) +*.cfg +*.hpp +*Resource.rc + +# Delphi local files (user-specific info) +*.local +*.identcache +*.projdata +*.tvsconfig +*.dsk + +# Delphi history and backups +__history/ +__recovery/ +*.~* + +# Castalia statistics file (since XE7 Castalia is distributed with Delphi) +*.stat + +# Ignore Build outputs +Build +Win32 +Win64 diff --git a/Delphi/GildedRose.groupproj b/Delphi/GildedRose.groupproj new file mode 100644 index 00000000..d2a77d91 --- /dev/null +++ b/Delphi/GildedRose.groupproj @@ -0,0 +1,48 @@ + + + {3E0B3749-4258-486E-A44B-05088E7E42D6} + + + + + + + + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Delphi/GildedRose.pas b/Delphi/GildedRose.pas new file mode 100644 index 00000000..dacd209f --- /dev/null +++ b/Delphi/GildedRose.pas @@ -0,0 +1,106 @@ +unit GildedRose; + +interface + +uses + Item, + System.Generics.Collections; + +type + TGildedRose = class(TObject) + private + FItems: TObjectList; + public + constructor Create(const AItems: TObjectList); + procedure UpdateQuality; + property Items: TObjectList read FItems; + end; + +implementation + +{ TGildedRose } + +constructor TGildedRose.Create(const AItems: TObjectList); +begin + inherited Create; + FItems := AItems; +end; + +procedure TGildedRose.UpdateQuality; +var + I: Integer; +begin + for I := 0 to Items.Count - 1 do + begin + if (Items[I].Name <> 'Aged Brie') and (Items[I].Name <> 'Backstage passes to a TAFKAL80ETC concert') then + begin + if Items[I].Quality > 0 then + begin + if Items[I].Name <> 'Sulfuras, Hand of Ragnaros' then + begin + Items[I].Quality := Items[I].Quality - 1; + end; + end; + end + else + begin + if Items[I].Quality < 50 then + begin + Items[I].Quality := Items[I].Quality + 1; + if Items[I].Name = 'Backstage passes to a TAFKAL80ETC concert' then + begin + if Items[I].SellIn < 11 then + begin + if Items[I].Quality < 50 then + begin + Items[I].Quality := Items[I].Quality + 1; + end; + end; + + if Items[I].SellIn < 6 then + begin + if Items[I].Quality < 50 then + begin + Items[I].Quality := Items[I].Quality + 1; + end; + end; + end; + end; + end; + + if Items[I].Name <> 'Sulfuras, Hand of Ragnaros' then + begin + Items[I].SellIn := Items[I].SellIn - 1; + end; + + if Items[I].SellIn < 0 then + begin + if Items[I].Name <> 'Aged Brie' then + begin + if Items[I].Name <> 'Backstage passes to a TAFKAL80ETC concert' then + begin + if Items[I].Quality > 0 then + begin + if Items[I].Name <> 'Sulfuras, Hand of Ragnaros' then + begin + Items[I].Quality := Items[I].Quality - 1; + end; + end; + end + else + begin + Items[I].Quality := Items[I].Quality - Items[I].Quality; + end; + end + else + begin + if Items[I].Quality < 50 then + begin + Items[I].Quality := Items[I].Quality + 1; + end; + end; + end; + end; +end; + +end. diff --git a/Delphi/GildedRoseTests.pas b/Delphi/GildedRoseTests.pas new file mode 100644 index 00000000..2e15adde --- /dev/null +++ b/Delphi/GildedRoseTests.pas @@ -0,0 +1,36 @@ +unit GildedRoseTests; + +interface +uses + DUnitX.TestFramework, + GildedRose, + Item, + System.Generics.Collections; + +type + [TestFixture] + TGildedRoseTests = class(TObject) + public + [Test] + procedure UpdateQuality_Never_ChangesTheItemName; + end; + +implementation + +procedure TGildedRoseTests.UpdateQuality_Never_ChangesTheItemName; +var + LItems: TObjectList; + LGildedRose: TGildedRose; +begin + LItems := TObjectList.Create; + LItems.Add(TItem.Create('foo', 0, 0)); + LGildedRose := TGildedRose.Create(LItems); + + LGildedRose.UpdateQuality; + + Assert.AreEqual('fixme', LGildedRose.Items[0].Name); +end; + +initialization + TDUnitX.RegisterTestFixture(TGildedRoseTests); +end. diff --git a/Delphi/Item.pas b/Delphi/Item.pas new file mode 100644 index 00000000..c74f1650 --- /dev/null +++ b/Delphi/Item.pas @@ -0,0 +1,39 @@ +unit Item; + +interface + +type + TItem = class(TObject) + private + FName: string; + FSellIn: Integer; + FQuality: Integer; + public + constructor Create(const AName: string; const ASellIn, AQuality: Integer); + function ToString: string; override; + property Name: string read FName write FName; + property SellIn: Integer read FSellIn write FSellIn; + property Quality: Integer read FQuality write FQuality; + end; + +implementation + +uses + System.SysUtils; + +{ TItem } + +constructor TItem.Create(const AName: string; const ASellIn, AQuality: Integer); +begin + inherited Create; + FName := AName; + FSellIn := ASellIn; + FQuality := AQuality; +end; + +function TItem.ToString: string; +begin + Result := Format('%s, %d, %d', [Name, SellIn, Quality]); +end; + +end. diff --git a/Delphi/README.md b/Delphi/README.md new file mode 100644 index 00000000..7023295c --- /dev/null +++ b/Delphi/README.md @@ -0,0 +1,13 @@ +# Delphi port of the Gilded-Rose Kata +This is a Delphi port of the *Gilded-Rose-Kata*. + +## Version Compatibility +The project files are written in Delphi 10.3.3 therefore require 10.3 or later. +That said, the actual code should be compatible back to Delphi 2010 as the unit +tests are written in [DUnitX](https://github.com/VSoftTechnologies/DUnitX) which +requires Delphi 2010 or later. + +## Building and Running +* Open the project group `GildedRose.groupproj`. +* Build and run the `TextTestFixture.exe` for the text output tests. +* Build and run the `UnitTests.exe` project to execute the unit tests. diff --git a/Delphi/TextTestFixture.dpr b/Delphi/TextTestFixture.dpr new file mode 100644 index 00000000..dd325d39 --- /dev/null +++ b/Delphi/TextTestFixture.dpr @@ -0,0 +1,57 @@ +program TextTestFixture; + +{$APPTYPE CONSOLE} + +{$R *.res} + +uses + System.SysUtils, + System.Generics.Collections, + GildedRose in 'GildedRose.pas', + Item in 'Item.pas'; + +var + Days, ErrorCode, I, J: Integer; + Items: TObjectList; + App: TGildedRose; + +begin + try + Writeln('OMGHAI!'); + + Items := TObjectList.Create; + Items.Add(TItem.Create('+5 Dexterity Vest', 10, 20)); + Items.Add(TItem.Create('Aged Brie', 2, 0)); + Items.Add(TItem.Create('Elixir of the Mongoose', 5, 7)); + Items.Add(TItem.Create('Sulfuras, Hand of Ragnaros', 0, 80)); + Items.Add(TItem.Create('Sulfuras, Hand of Ragnaros', -1, 80)); + Items.Add(TItem.Create('Backstage passes to a TAFKAL80ETC concert', 15, 20)); + Items.Add(TItem.Create('Backstage passes to a TAFKAL80ETC concert', 10, 49)); + Items.Add(TItem.Create('Backstage passes to a TAFKAL80ETC concert', 5, 49)); + // this conjured item does not work properly yet + Items.Add(TItem.Create('Conjured Mana Cake', 3, 6)); + + App := TGildedRose.Create(Items); + + Days := 2; + if ParamCount > 0 then + begin + Val(ParamStr(1), Days, ErrorCode); + Inc(Days); + end; + + for I := 0 to Days - 1 do + begin + Writeln(Format('-------- day %d --------', [I])); + Writeln('name, sellIn, quality'); + for J := 0 to Items.Count - 1 do + Writeln(Items[J].ToString); + Writeln; + + App.UpdateQuality; + end; + except + on E: Exception do + Writeln(E.ClassName, ': ', E.Message); + end; +end. diff --git a/Delphi/TextTestFixture.dproj b/Delphi/TextTestFixture.dproj new file mode 100644 index 00000000..2651f9ee --- /dev/null +++ b/Delphi/TextTestFixture.dproj @@ -0,0 +1,1019 @@ + + + {3166A4F2-2D40-49FF-9A28-E46E603C9C6A} + 18.8 + None + TextTestFixture.dpr + True + Debug + Win32 + 1 + Console + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + TextTestFixture + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;FmxTeeUI;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FMXTee;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;FmxTeeUI;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FMXTee;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;FmxTeeUI;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FMXTee;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;fmx;FireDACIBDriver;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + true + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;fmx;FireDACIBDriver;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + true + + + DBXSqliteDriver;RESTComponents;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;emsclientfiredac;tethering;svnui;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;FireDACIBDriver;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;Tee;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;emsserverresource;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DBXSqliteDriver;RESTComponents;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;emsclientfiredac;tethering;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;FireDACIBDriver;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;Tee;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;emsserverresource;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Application + + + + TextTestFixture.dpr + + + + + + true + + + + + true + + + + + true + + + + + TextTestFixture.exe + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + False + False + False + False + False + True + False + + + 12 + + + + + diff --git a/Delphi/UnitTests.dpr b/Delphi/UnitTests.dpr new file mode 100644 index 00000000..1986cbb8 --- /dev/null +++ b/Delphi/UnitTests.dpr @@ -0,0 +1,59 @@ +program UnitTests; + +{$IFNDEF TESTINSIGHT} +{$APPTYPE CONSOLE} +{$ENDIF}{$STRONGLINKTYPES ON} +uses + System.SysUtils, + {$IFDEF TESTINSIGHT} + TestInsight.DUnitX, + {$ENDIF } + DUnitX.Loggers.Console, + DUnitX.Loggers.Xml.NUnit, + DUnitX.TestFramework, + GildedRoseTests in 'GildedRoseTests.pas'; + +var + runner : ITestRunner; + results : IRunResults; + logger : ITestLogger; + nunitLogger : ITestLogger; +begin +{$IFDEF TESTINSIGHT} + TestInsight.DUnitX.RunRegisteredTests; + exit; +{$ENDIF} + try + //Check command line options, will exit if invalid + TDUnitX.CheckCommandLine; + //Create the test runner + runner := TDUnitX.CreateRunner; + //Tell the runner to use RTTI to find Fixtures + runner.UseRTTI := True; + //tell the runner how we will log things + //Log to the console window + logger := TDUnitXConsoleLogger.Create(true); + runner.AddLogger(logger); + //Generate an NUnit compatible XML File + nunitLogger := TDUnitXXMLNUnitFileLogger.Create(TDUnitX.Options.XMLOutputFile); + runner.AddLogger(nunitLogger); + runner.FailsOnNoAsserts := False; //When true, Assertions must be made during tests; + + //Run tests + results := runner.Execute; + if not results.AllPassed then + System.ExitCode := EXIT_ERRORS; + + {$IFNDEF CI} + //We don't want this happening when running under CI. + if TDUnitX.Options.ExitBehavior = TDUnitXExitBehavior.Pause then + begin + System.Write('Done.. press key to quit.'); + System.Readln; + end; + {$ENDIF} + except + on E: Exception do + System.Writeln(E.ClassName, ': ', E.Message); + end; +end. diff --git a/Delphi/UnitTests.dproj b/Delphi/UnitTests.dproj new file mode 100644 index 00000000..1cd2fa7e --- /dev/null +++ b/Delphi/UnitTests.dproj @@ -0,0 +1,937 @@ + + + {9379A28B-85A1-455B-BE8B-BDE5DD446BDF} + 18.8 + None + UnitTests.dpr + True + Debug + Win32 + 1 + Console + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + $(DUnitX);$(DCC_UnitSearchPath) + UnitTests + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;FmxTeeUI;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FMXTee;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;FmxTeeUI;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FMXTee;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;bindcompfmx;FmxTeeUI;fmx;FireDACIBDriver;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FMXTee;soaprtl;DbxCommonDriver;xmlrtl;soapmidas;DataSnapNativeClient;FireDACDSDriver;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;fmx;FireDACIBDriver;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + + + DBXSqliteDriver;RESTComponents;fmxase;DBXInterBaseDriver;emsclientfiredac;tethering;DataSnapFireDAC;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;fmx;FireDACIBDriver;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;FireDACDSDriver;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + + + DBXSqliteDriver;RESTComponents;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;emsclientfiredac;tethering;svnui;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;FireDACIBDriver;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;Tee;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;emsserverresource;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + + + DBXSqliteDriver;RESTComponents;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;emsclientfiredac;tethering;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;FireDACIBDriver;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;Tee;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;emsserverresource;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Console + + + + UnitTests.dpr + + + + + + true + + + + + true + + + + + true + + + + + UnitTests.exe + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + False + False + False + False + False + True + False + + + 12 + + + + + diff --git a/GildedRoseRequirements.md b/GildedRoseRequirements.md new file mode 100644 index 00000000..36a925ca --- /dev/null +++ b/GildedRoseRequirements.md @@ -0,0 +1,37 @@ +# Gilded Rose Requirements Specification + +Hi and welcome to team Gilded Rose. As you know, we are a small inn with a prime location in a +prominent city ran by a friendly innkeeper named Allison. We also buy and sell only the finest goods. +Unfortunately, our goods are constantly degrading in `Quality` as they approach their sell by date. + +We have a system in place that updates our inventory for us. It was developed by a no-nonsense type named +Leeroy, who has moved on to new adventures. Your task is to add the new feature to our system so that +we can begin selling a new category of items. First an introduction to our system: + +- All `items` have a `SellIn` value which denotes the number of days we have to sell the `items` +- All `items` have a `Quality` value which denotes how valuable the item is +- At the end of each day our system lowers both values for every item + +Pretty simple, right? Well this is where it gets interesting: + +- Once the sell by date has passed, `Quality` degrades twice as fast +- The `Quality` of an item is never negative +- __"Aged Brie"__ actually increases in `Quality` the older it gets +- The `Quality` of an item is never more than `50` +- __"Sulfuras"__, being a legendary item, never has to be sold or decreases in `Quality` +- __"Backstage passes"__, like aged brie, increases in `Quality` as its `SellIn` value approaches; + - `Quality` increases by `2` when there are `10` days or less and by `3` when there are `5` days or less but + - `Quality` drops to `0` after the concert + +We have recently signed a supplier of conjured items. This requires an update to our system: + +- __"Conjured"__ items degrade in `Quality` twice as fast as normal items + +Feel free to make any changes to the `UpdateQuality` method and add any new code as long as everything +still works correctly. However, do not alter the `Item` class or `Items` property as those belong to the +goblin in the corner who will insta-rage and one-shot you as he doesn't believe in shared code +ownership (you can make the `UpdateQuality` method and `Items` property static if you like, we'll cover +for you). + +Just for clarification, an item can never have its `Quality` increase above `50`, however __"Sulfuras"__ is a +legendary item and as such its `Quality` is `80` and it never alters. diff --git a/GildedRoseRequirements_de.md b/GildedRoseRequirements_de.md new file mode 100644 index 00000000..6f3b9855 --- /dev/null +++ b/GildedRoseRequirements_de.md @@ -0,0 +1,31 @@ +# Anforderungsspezifikation für vergoldete Rose (Gilded Rose) + +Hallo und willkommen im Team **Gilded Rose**. Wie Du sicher weißt, sind wir ein kleiner Gasthof in bester Lage in einer bekannten Stadt, der von einem freundlichen Gastwirt namens Allison geführt wird. +Wir kaufen und verkaufen nur die besten Produkte. +Leider verschlechtert sich die Qualität unserer Waren ständig, da sie sich ihrem Mindesthaltbarkeitsdatum nähern. +Wir haben ein System eingerichtet, um den Bestand automatisch aktualisieren zu können. +Es wurde von Leeroy entwickelt, ein vernünftiger Typ, der zu neuen Abenteuern aufgebrochen ist. +Damit wir mit dem Verkauf eines neuen Produkttyps beginnen können, ist es nun Deine Aufgabe, unserem System eine neue Funktion hinzuzufügen. + +Zunächst eine Einführung in unser bestehendes System: +* Alle Artikel (`Item`) haben einen `SellIn`-Wert, der die Anzahl der Tage angibt, die uns verbleiben, um den Artikel zu verkaufen +* Alle Artikel haben einen `Quality`-Wert (Qualität), der angibt, wie wertvoll der Artikel ist +* Am Tagesende senkt unser System für jeden Artikel beide Werte + +Ziemlich einfach, oder? Nicht ganz, denn jetzt wird es interessant: + +* Sobald das "Mindesthaltbarkeitsdatum" überschritten wurde, nimmt die „Qualität“ doppelt so schnell ab +* Die "Qualität" eines Artikels ist nie negativ +* "Alter Brie" (`Aged Brie`) nimmt an Qualität zu, je älter er wird +* Die "Qualität" eines Artikels ist nie höher als 50 +* Der legendäre Artikel "Sulfuras" ändert weder sein "Verkaufsdatum", noch verschlechtert sich seine "Qualität" +* "Backstage-Pässe" (`backstage passes`) werden - wie `Aged Brie` - hochwertiger, solange das "Verkaufsdatum" noch nicht erreicht wurde. + Bei 10 Tagen oder weniger erhöht sich die Qualität um 2, bei 5 Tagen oder weniger um 3, nach dem "Konzert" sinkt sie aber auf 0. + +Kürzlich haben wir einen Lieferanten für "beschworene" (`conjured`) Artikel unter Vertrag genommen. Dies erfordert ein Update unseres Systems: +* "Beschworene" Artikel verlieren doppelt so schnell an Qualität wie normale Artikel + +Solange alles einwandfrei funktioniert, kannst Du beliebige Änderungen an der Methode `updateQuality` vornehmen und so viel Code hinzufügen, wie Du möchtest. Aber Vorsicht: Die `Item`-Klasse oder ihre Eigenschaften darfst Du in keiner Weise ändern, denn diese Klasse gehört dem Kobold in der Ecke, der sofort wütend wird und Dich sofort töten würde, denn er glaubt nicht an die Kultur von gemeinsamem Code (`shared code`). +(Wenn Du möchtest, kannst Du die `updateQuality`-Methode und die `Item`-Eigenschaft statisch machen, das regeln wir dann.) + +Sicherheitshalber noch ein Hinweis: Die Qualität eines Artikels kann nie höher als 50 sein, aber `Sulfuras` ist ein legendärer Artikel und als solcher beträgt seine Qualität 80 und ändert sich auch nie. diff --git a/GildedRoseRequirements_es.md b/GildedRoseRequirements_es.md new file mode 100644 index 00000000..857ffbb8 --- /dev/null +++ b/GildedRoseRequirements_es.md @@ -0,0 +1,45 @@ +# Especificaciones de la Rosa Dorada (Gilded Rose) + +Bienvenido al equipo de **Gilded Rose**. +Como quizá sabes, somos una pequeña posada ubicada estratégicamente en una prestigiosa ciudad, atendida por la amable **Allison**. +También compramos y vendemos mercadería de alta calidad. +Por desgracia, nuestra mercadería va bajando de calidad a medida que se aproxima la fecha de venta. + +Tenemos un sistema instalado que actualiza automáticamente el `inventario`. +Este sistema fue desarrollado por un muchacho con poco sentido común llamado Leeroy, que ahora se dedica a nuevas aventuras. +Tu tarea es agregar una nueva característica al sistema para que podamos comenzar a vender una nueva categoría de items. + +## Descripción preliminar + +Pero primero, vamos a introducir el sistema: + +* Todos los artículos (`Item`) tienen una propiedad `sellIn` que denota el número de días que tenemos para venderlo +* Todos los artículos tienen una propiedad `quality` que denota cúan valioso es el artículo +* Al final de cada día, nuestro sistema decrementa ambos valores para cada artículo mediante el método `updateQuality` + +Bastante simple, ¿no? Bueno, ahora es donde se pone interesante: + +* Una vez que ha pasado la fecha recomendada de venta, la `calidad` se degrada al doble de velocidad +* La `calidad` de un artículo nunca es negativa +* El "Queso Brie envejecido" (`Aged brie`) incrementa su `calidad` a medida que se pone viejo + * Su `calidad` aumenta en `1` unidad cada día + * luego de la `fecha de venta` su `calidad` aumenta `2` unidades por día +* La `calidad` de un artículo nunca es mayor a `50` +* El artículo "Sulfuras" (`Sulfuras`), siendo un artículo legendario, no modifica su `fecha de venta` ni se degrada en `calidad` +* Una "Entrada al Backstage", como el queso brie, incrementa su `calidad` a medida que la `fecha de venta` se aproxima + * si faltan 10 días o menos para el concierto, la `calidad` se incrementa en `2` unidades + * si faltan 5 días o menos, la `calidad` se incrementa en `3` unidades + * luego de la `fecha de venta` la `calidad` cae a `0` + +## El requerimiento + +Hace poco contratamos a un proveedor de artículos *conjurados mágicamente*. +Esto requiere una actualización del sistema: + +* Los artículos `conjurados` degradan su `calidad` al doble de velocidad que los normales + +Siéntete libre de realizar cualquier cambio al mensaje `updateQuality` y agregar el código que sea necesario, mientras que todo siga funcionando correctamente. Sin embargo, **no alteres el objeto `Item` ni sus propiedades** ya que pertenecen al goblin que está en ese rincón, que en un ataque de ira te va a liquidar de un golpe porque no cree en la cultura de código compartido. + +## Notas finales + +Para aclarar: un artículo nunca puede tener una `calidad` superior a `50`, sin embargo las Sulfuras siendo un artículo legendario posee una calidad inmutable de `80`. diff --git a/GildedRoseRequirements_eu.md b/GildedRoseRequirements_eu.md new file mode 100644 index 00000000..8c45119b --- /dev/null +++ b/GildedRoseRequirements_eu.md @@ -0,0 +1,36 @@ +# Urrezko Arrosaren zehaztapenak (Gilded Rose) + +Ongi etorri **Gilded Rose**-eko taldera. +Jakingo duzunez, hiri ospetsu batean estrategikoki kokatutako ostatu txiki bat gara, Allison atseginak zuzendua. Kalitate oneneko salgaiak ere erosten eta saltzen ditugu. Zoritxarrez, gure salgaiak kalitatez jaisten doaz salmenta-data hurbildu ahala. + +Inbentarioa automatikoki eguneratzen duen sistema bat dugu instalaturik. Gaur egun abentura berrietan dabilen Leeroy izeneko mutiko zentzugabe batek garatu zuen sistema hau. Zure zeregina sistemari ezaugarri berri bat gehitzea da, artikulu kategoria berri bat saltzen has gaitezen. + +## Hasierako deskribapena + +Baina lehenik, sistema azalduko dugu: + +* Artikulu guztiek (`Item`) `sellIn` izeneko propietate bat dute berau saltzeko ditugun egunen kopurua adierazten duena. +* Artikulu guztiek `quality` izeneko propietate bat dute artikulu hori zein baliotsua den adierazten duena. +* Egunaren amaieran, gure sistemak artikulu bakoitzerako bi balio horiek gutxitzen ditu `updateQuality` metodoaren bitartez. + +Nahiko sinplea, ezta? Beno, orain da interesgarri jartzen denean: + +* Behin gomendatutako salmenta-data igarota, kalitatea (`quality`) bi aldiz azkarrago degradatzen da. +* Artikulu baten kalitatea (`quality`) ez da inoiz negatiboa. +* “Aged brie”-ren kalitatea (`quality`) hobetu egiten da zahartu ahala unitate `1` gehituz egun bakoitzeko. +* Artikulu baten kalitatea (`quality`) ezin da inoiz `50` baino handiagoa izan. +* "Sulfuras" artikuluak, artikulu legendarioa izanik, ez du salmenta-data aldatzen eta bere kalitatea ez da degradatzen. +* "Backstage Passes"-ak, "Aged brie”-a bezala, kalitatean (`quality`) hobetzen doaz salmenta-data (`sellIn`) hurbildu ahala: + * Salmenta-datarako (`sellIn`) `10` egun edo gutxiago falta badira, kalitatea (`quality`) `2` unitatetan haundituko da. + * Salmenta-datarako (`sellIn`) `5` egun edo gutxiago falta badira, kalitatea (`quality`) `3` unitatetan haundituko da. + * Salmenta-data (`sellIn`) pasa ondoren, kalitatea (`quality`) `0`ra pasako da. + +Duela gutxi, konjuratutako artikulu hornitzaile bat kontratatu genuen. Horretarako, sistema eguneratu behar da: + +* Konjuratutako (`conjured`) artikuluek normalek baino `2` aldiz azkarrago degradatzen dute kalitatea (`quality`). + +Lasai alda dezakez `updateQuality` metodoa beharrezkoa ikusten duzu kodea gehitzeko, beti ere, denak behar bezela funtzionatzen jarraitzen duen bitartean. Baina ezin duzu ordea `Item` objektua ezta bere propietaterik aldatu, txokoan dagoen goblinarenak bait dira eta haserre-krisi batean erasotu egin zaitzake ez bait du kode jabetza partekatuan sinisten. + +## Azken oharrak + +Argitzearren, artikulu batek ezin du inoiz `50` baino kalitate (`quality`) handiagoa izan, "Sulfuras" ordea, artikulu legendario bat izanik, `80`ko kalitate aldaezina dauka. diff --git a/GildedRoseRequirements_fr.md b/GildedRoseRequirements_fr.md new file mode 100644 index 00000000..96ab424c --- /dev/null +++ b/GildedRoseRequirements_fr.md @@ -0,0 +1,41 @@ +# Spécification de la Rose dorée (Gilded Rose) + +Bonjour et bienvenue dans l'équipe de la Rose dorée. + +Comme vous le savez, notre petite taverne située à proximité d'une cité importante est dirigée par l'aubergiste amicale Allison. + +Nous achetons et vendons uniquement les meilleurs produits. +Malheureusement, la qualité de nos marchandises se dégrade constamment à l'approche de leur date de péremption. + +Un système a été mis en place pour mettre à jour notre inventaire. +Il a été développé par Leeroy, une personne pleine de bon sens qui est partie pour de nouvelles aventures. + +Votre mission est d'ajouter une nouvelle fonctionnalité à notre système pour que nous puissions commencer à vendre un nouveau type de produits. + +Mais d'abord, laissez-moi vous présenter notre système : + +- Tous les éléments ont une valeur `sellIn` qui désigne le nombre de jours restant pour vendre l'article. +- Tous les articles ont une valeur `quality` qui dénote combien l'article est précieux. +- À la fin de chaque journée, notre système diminue ces deux valeurs pour chaque produit. + +Plutôt simple, non ? + +Attendez, ça devient intéressant : + +- Une fois que la date de péremption est passée, la qualité se dégrade deux fois plus rapidement. +- La qualité (`quality`) d'un produit ne peut jamais être négative. +- "Aged Brie" augmente sa qualité (`quality`) plus le temps passe. +- La qualité d'un produit n'est jamais de plus de 50. +- "Sulfuras", étant un objet légendaire, n'a pas de date de péremption et ne perd jamais en qualité (`quality`) +- "Backstage passes", comme le "Aged Brie", augmente sa qualité (`quality`) plus le temps passe (`sellIn`) ; La qualité augmente de 2 quand il reste 10 jours ou moins et de 3 quand il reste 5 jours ou moins, mais la qualité tombe à 0 après le concert. + +Nous avons récemment signé un partenariat avec un fournisseur de produit invoqué ("Conjured"). +Cela nécessite une mise à jour de notre système : + +- les éléments "Conjured" voient leur qualité se dégrader de deux fois plus vite que les objets normaux. + +Vous pouvez faire les changements que vous voulez à la méthode `updateQuality` et ajouter autant de code que vous voulez, tant que tout fonctionne correctement. +Cependant, nous devons vous prévenir, vous ne devez en aucun cas modifier la classe `Item` ou ses propriétés car cette classe appartient au gobelin à l'étage qui entrerait dans une rage instantanée et vous tuerait sans délai : il ne croit pas au partage du code. +(Vous pouvez rendre la méthode `updateQuality` statique, ainsi que des propriétés dans la classe `Item` si vous voulez, nous vous couvrirons) + +Juste une précision, un produit ne peut jamais voir sa qualité augmenter au-dessus de 50, cependant "Sulfuras" est un objet légendaire et comme tel sa qualité est de 80 et elle ne change jamais. diff --git a/GildedRoseRequirements_it.md b/GildedRoseRequirements_it.md new file mode 100644 index 00000000..3feecc99 --- /dev/null +++ b/GildedRoseRequirements_it.md @@ -0,0 +1,45 @@ +# Requisiti della rosa dorata (Gilded Rose) + + +Ciao, benvenuto nel team **Rosa dorata**. +Come sapete, siamo una piccola locanda con una posizione privilegiata in una importante città +gestita da un amichevole locandiere di nome Allison. +Compriamo e vendiamo solo i prodotti migliori. + +Sfortunatamente, la qualità dei nostri prodotti diminuisce costantemente man mano che si avvicinano alla data di scadenza. +Disponiamo di un sistema che aggiorna il nostro inventario in automatico. +Il sistema è stato sviluppato da un tipo pratico chiamato Leeroy, che è passato a nuove avventure. + +Il tuo compito è aggiungere una nuova funzionalità al nostro sistema in modo che possiamo iniziare a vendere una nuova categoria di articoli. + +## Decrizione del sistema: + +- Tutti i prodotti (`Item`) hanno una proprietà `sellIn` che indica quanti giorni mancano alla data di scadenza. +- Tutti i prodotti (`Item`) hanno una proprietà `quality` che denota il valore dell'articolo. +- Alla fine di ogni giornata il nostro sistema decrementa entrambe le proprietà per ogni prodotto tramite il metodo `updateQuality` + +Abbastanza semplice, vero? Bene, è da qui che la cosa si fa interessante: + +- Una volta passata la data di scadenza, la proprietà `quality` diminuisce due volte più velocemente +- La proprietà `quality` di un prodotto non può essere mai negativa +- Il prodotto "Brie invecchiato" (`Aged brie`) aumenta di uno la sua `quality` man mano che invecchia +- La `quality` di un prodotto non può mai essere superiore a 50 +- Il prodotto "Sulfuras" (`Sulfuras`), essendo un oggetto leggendario, non modifica mai ne la proprietà `sellIn` ne degrada la proprietà `quality` +- I prodotto "Backstage pass" (`Backstage pass`), come il brie invecchiato (`Aged brie`), aumentano `quality` man mano che il loro valore di `sellIn` si avvicina a 0 + - La proprietà `quality` aumenta di 2 quando mancano 10 giorni o meno e di 3 quando ci sono 5 giorni o meno ma, + - La proprietà `quality` scende a 0 quando il valore di `sellIn` scende a 0. + +## La nuova richiesta: + +Recentemente è stato firmato un contratto con un fornitore di oggetti "oggetti magici" (`conjurados`) +Ciò richiede un aggiornamento del nostro sistema: + +- Gli "oggetti magici" (`conjurados`) diminuiscono di `quality` due volte più velocemente rispetto ai prodotti normali. + +Sentiti libero di apportare qualsiasi modifica al metodo "updateQuality" ed aggiungere codice se necessario, purché tutto continui a funzionare correttamente. +Tuttavia, **non alterare l'oggetto `Item` o le sue proprietà** poiché appartengono al goblin nell'angolo, che in un impeto di rabbia ti colpirà perché non crede nella cultura della condivisione del codice. + +## Note finali: + +- Un prodotto non può mai avere un aumento di qualità `quality` superiore a 50, tuttavia +- il prodotto "Sulfuras" (`Sulfuras`) è un oggetto leggendario e come tale la sua Qualità `quality` è 80 e non si altera mai. diff --git a/GildedRoseRequirements_jp.md b/GildedRoseRequirements_jp.md new file mode 100644 index 00000000..c232dcd6 --- /dev/null +++ b/GildedRoseRequirements_jp.md @@ -0,0 +1,33 @@ +# Gilded Rose 要件仕様書 +こんにちは、チーム・ギルドローズへようこそ。我々はアリソンという気さくな人が経営する、都会の一等地にある小さな宿です。 + +また、私たちは最高級の商品のみを仕入れて販売しています。残念なことに、商品は販売期限が近づくにつれ、品質が低下していきます。 + +私たちには在庫を更新するシステムがあります。これは、新たな冒険へと旅立ったリーロイという無神経な性格の人物によって開発されました。 + +あなたの仕事は、システムに新しい機能を追加して、新しいカテゴリーのアイテムを販売できるようにすることです。 + +最初にシステムの紹介をします。 + +* すべてのアイテムには、アイテムを販売するための残り日数(販売期限)を示すSellIn値があります。 +* すべてのアイテムには、そのアイテムの価値を示すQuality値があります。 +* 毎日の終わりには、私たちのシステムは、両方の項目の値を1小さくします。 + +簡単でしょ?ここからが面白いところです。 + +* 販売するための残り日数が無くなると、Quality値は2小さくなります。 +* Quality値は決してマイナスにはなりません。 +* "Aged Brie"は、日が経つほどQuality値が上がっていきます。 +* Quality値は50以上にはなりません。 +* "Sulfuras"は伝説のアイテムなので、販売されたり、Quality値が低下したりすることはありません。 +* "Backstage passes"は、"Aged Brie"と同様、SellIn値が近づくにつれてQuality値が上昇し、10日以内になると毎日2上がり、5日以内になると毎日3上がりますが、コンサート終了後には0になります。 + +最近、"Conjured"アイテムのサプライヤーと契約しました。そのため、システムの更新が必要です。 + +* "Conjured"アイテムは、通常のアイテムの2倍の速さで品質が劣化します。 + +すべてが正常に動作する限り、UpdateQualityメソッドに変更を加えたり、新しいコードを追加したりすることは自由に行ってください。ただし、ItemクラスやItemsプロパティは変更しないでください。 + +これらは、隅にいるゴブリンのものなので、コードの共有所有権を信じていないので、怒り狂ってあなたを一発で撃ってきます(UpdateQualityメソッドとItemsプロパティを静的にしても構いません。) + +ただし、"Sulfuras "は伝説のアイテムであるため、Quality値は80であり、Quality値が変わることはありません。 diff --git a/GildedRoseRequirements_kr.md b/GildedRoseRequirements_kr.md new file mode 100644 index 00000000..63a6a523 --- /dev/null +++ b/GildedRoseRequirements_kr.md @@ -0,0 +1,37 @@ +# Gilded Rose 요구사항 명세 + +안녕하세요, **Gilded Rose**에 오신 것을 환영합니다. 우리는 도시의 주요 지역에있는 작은 숙소(상점)이며, **Allison**(앨리슨)이라는 상냥한 사람이 운영하고 있습니다. + +우리는 최고의 제품만을 구입하여 판매하고 있습니다. 불행히도, 상품 판매 기한이 가까워 질수록 품질이 저하되어가고 있습니다. + +우리는 재고를 업데이트하는 시스템이 있습니다. 이 시스템은 지금은 새로운 모험을 떠나고 없는 **Leeroy**(리로이)라는 빡빡한 성격의 인물에 의해 개발되었습니다. + +당신이 할 일은 시스템에 새로운 기능을 추가하여, 새로운 카테고리의 상품을 판매할 수 있도록하는 것입니다. + +## 시스템 소개 +먼저 시스템을 소개합니다. + +- 모든 아이템은 `SellIn` 값을 가지며, 이는 아이템을 판매해야하는 (남은) 기간을 나태냅니다. +- 모든 아이템은 `Quality` 값을 가지며, 이것은 아이템의 가치를 나타냅니다. +- 하루가 지날때마다, 시스템은 두 값(`SellIn`, `Quality`)을 *1* 씩 감소시킵니다. + +간단하죠? 흥미로운 부분은 지금부터입니다. + +- 판매하는 나머지 일수가 없어지면, `Quality` 값은 **2배**로 떨어집니다. +- `Quality` 값은 결코 음수가 되지는 않습니다. +- "**Aged Brie**"(오래된 브리치즈)은(는) 시간이 지날수록 `Quality` 값이 올라갑니다. +- `Quality` 값은 50를 초과 할 수 없습니다. +- `Sulfuras`는 전설의 아이템이므로, 반드시 판매될 필요도 없고 `Quality` 값도 떨어지지 않습니다. +- "**Backstage passes**(백스테이지 입장권)"는 "**Aged Brie**"와 유사하게 `SellIn` 값에 가까워 질수록 `Quality` 값이 상승하고, **10일 부터는** 매일 *2* 씩 증가하다, **5일 부터는**이 되면 매일 *3* 씩 증가하지만, 콘서트 종료 후에는 *0*으로 떨어집니다. + +## 시스템 업데이트 요구 사항 + +최근 "**Conjured**"(마법에 걸린) 상품 공급 업체와 계약했습니다. 따라서 시스템의 업데이트가 필요합니다. + +- "**Conjured**" 아이템은 일반 아이템의 2배의 속도로 품질(`Quality`)이 저하됩니다. + +모든 것이 제대로 작동하는 한에서는 `UpdateQuality()` 메서드를 변경하거나 새로운 코드의 추가를 자유롭게 할 수 있습니다. 그러나 `Item` 클래스와 `Items` 속성은 변경하지 마세요. + +이것들은 저기 구석에있는 고블린의 것이고, 그 친구는 코드의 공유 소유권을 믿지 않기 때문에, 미친듯이 화를 내며(insta-rage) 여러분에게 한 방(one-shot)을 날릴 수도 있습니다. (`UpdateQuality()` 메서드와 `Items` 속성을 정적(static)으로 만드는 것은 괜찮습니다. 저희가 책임질게요.) + +다시 한 번 확인하자면, 아이템의 `Quality`는 50 이상으로 증가할 수는 없습니다. 하지만 `Sulfuras`는 전설의 아이템이기 때문에 `Quality` 값은 80이며, 값이 바뀌지 않습니다. diff --git a/GildedRoseRequirements_nl.md b/GildedRoseRequirements_nl.md new file mode 100644 index 00000000..4ade791c --- /dev/null +++ b/GildedRoseRequirements_nl.md @@ -0,0 +1,28 @@ +# Vergulde Roos Requirement Specificaties + +Hoi en welkom bij team Vergulde Roos. Zoals je weet, zijn we een klein herberg met een uitstekende locatie in een prominente stad gerund door een vriendelijke herbergier genaamd Allison. We kopen en verkopen ook alleen de beste goederen. Helaas, onze goederen degraderen constant in kwaliteit `Quality` naarmate ze hun uiterste houdbaarheidsdatum naderen. + +We hebben een systeem dat onze inventaris voor ons bijwerkt. Het is ontwikkeld door een no-nonsense type genaamd Leeroy, die zich op nieuwe avonturen gestort heeft. Jouw taak is om deze nieuwe functie toe te voegen aan ons systeem zodat we een nieuwe categorie items kunnen gaan verkopen. Eerst een introductie tot ons systeem: + +- Alle artikelen `items` hebben een `SellIn` waarde die aangeeft hoeveel dagen we nog hebben om de `items` te verkopen +- Alle `items` hebben een `Quality` (kwaliteit) waarde die aangeeft hoe waardevol het item is +- Aan het einde van elke dag verlagen we beide waarden voor elk item in ons systeem + +Vrij eenvoudig, toch? Nou, hier wordt het interessant: + +- Zodra de uiterste verkoopdatum is verstreken, degradeert `Quality` twee keer zo snel +- De `Quality` van een item is nooit negatief +- Oude Brie __"Aged Brie"__ neemt eigenlijk toe in `Quality` naarmate het ouder wordt +- De `Quality` van een item is nooit meer dan `50` +- __"Sulfuras"__, als legendarisch item, hoeft nooit te worden verkocht of vermindert niet in `Quality` +- __"Backstage passes"__, zoals aged brie, neemt toe in `Quality` naarmate de `SellIn` waarde nadert; + - `Quality` neemt met `2` toe wanneer er `10` dagen of minder zijn en met `3` wanneer er `5` dagen of minder zijn, maar + - `Quality` daalt naar `0` na het concert + +We hebben onlangs een leverancier van betoverde items gecontracteerd. Dit vereist een update van ons systeem: + +- __"Conjured"__ items degraderen in `Quality` twee keer zo snel als normale items + +Voel je vrij om wijzigingen aan te brengen in de `UpdateQuality` methode en voeg nieuwe code toe zolang alles nog steeds correct werkt. Wijzig echter niet de `Item` klasse of `Items` eigenschap aangezien die toebehoren aan de kobold op de hoek die meteen boos wordt en je met één klap uitschakelt omdat hij niet gelooft in gedeeld codebezit (je kunt de `UpdateQuality` methode en `Items` eigenschap wel statisch maken als je wilt, we dekken je wel). + +Voor de duidelijkheid, een item kan zijn `Quality` nooit verhogen boven `50`, echter __"Sulfuras"__ is een legendarisch item en als zodanig is zijn `Quality` `80` en verandert nooit. diff --git a/GildedRoseRequirements_pl.md b/GildedRoseRequirements_pl.md new file mode 100644 index 00000000..63d940ab --- /dev/null +++ b/GildedRoseRequirements_pl.md @@ -0,0 +1,22 @@ +# Specyfikacja wymagań Pozłacanej Róży (Gilded Rose) + + +Cześć i witaj na pokładzie zespołu Pozłacanej Róży. Jak zapewne już wiesz, jesteśmy niewielką karczmą, która znajduje się w głównej części wspaniałego miasta i jest prowadzona przez przyjazną oberżystkę o imieniu Allison. Sprzedajemy i kupujemy tylko najlepsze towary. Niestety, przedmioty te tracą na jakości w miarę jak zbliża się ich termin sprzedaży. Korzystamy z systemu, który automatycznie aktualizuje stan naszego inwentarza. System ten został napisany przez rozsądnego typka o imieniu Leeroy, który postanowił poszukać nowych przygód. Twoim zadaniem jest dodanie nowej funkcjonalności do naszego systemu tak, abyśmy mogli rozpocząć sprzedaż nowego rodzaju przedmiotów. Pozwól, że najpierw zrobię ogólne wprowadzenie do systemu: +- Wszystkie przedmioty (`Item`) posiadają właściwość `SellIn`, która oznacza **liczbę dni pozostałych do upłynięcia terminu sprzedaży** przedmiotu +- Wszystkie przedmioty posiadają właściwość `Quality` (**jakość**), która wpływa na wartość przedmiotu +- Na koniec każdego dnia nasz system obniża wartość obu właściwości dla każdego przedmiotu + +Dość proste, prawda? No cóż, teraz zrobi się bardziej interesująco: +- Po upływie daty sprzedaży, jakość spada dwukrotnie szybciej +- Jakość przedmiotu nigdy nie jest ujemna +- Jakość "Starego Brie" (`Aged Brie`) rośnie wraz z wiekiem +- Jakość przedmiotu nigdy nie przekracza 50 +- Przedmiot legendarny `Sulfuras` nigdy nie musi być sprzedany, ani nie traci na jakości +- "Przepustka za kulisy" (`Backstage passes`), podobnie jak "Stary Brie", zyskują na jakości w miarę zbliżania się terminu sprzedaży; jakość wzrasta o 2, gdy jest 10 dni lub mniej i o 3, gdy jest 5 dni lub mniej, ale spada do 0 po koncercie (gdy `SellIn` < 0) + +Niedawno podpisaliśmy z dostawcą kontrakt na wyczarowane przedmioty. Wymaga to wprowadzenia zmiany do naszego systemu: +- "Wyczarowane" (`Conjured`) przedmioty tracą na jakości dwa razy szybciej niż normalne przedmioty + +Możesz wprowadzać dowolne zmiany w metodzie `UpdateQuality`, a także dodawać nowy kod, o ile wszystko nadal działa prawidłowo. Jednak nie zmieniaj klasy `Item` ani właściwości `Items`, które zostały napisane przez goblina w rogu, gdyż zaatakuje Cię on i zabije jednym strzałem, ponieważ nie wierzy we współdzielony kod (możesz zmienić metodę `UpdateQuality` oraz właściwość `Items` na statyczne, jeśli chcesz - będziemy Cię kryć!). + +Dla jasności: jakość przedmiotu nie może przekroczyć 50, jednak dla przedmiotu legendarnego `Sulfuras` jakość jest stale na poziomie 80 i nigdy nie spada. diff --git a/GildedRoseRequirements_pt-BR.md b/GildedRoseRequirements_pt-BR.md new file mode 100644 index 00000000..4498c1cb --- /dev/null +++ b/GildedRoseRequirements_pt-BR.md @@ -0,0 +1,35 @@ +# Especificações de Requisitos de Gilded Rose + +Bem-vindo ao time Gilded Rose. Como você deve saber, nós somos uma pequena pousada estrategicamente localizada em uma prestigiosa cidade, atendida pelo amigavel atendente Allison. Além de ser uma pousada, nós também compramos e vendemos as mercadorias de melhor qualidade. Infelizmente nossas mercadorias vão perdendo a qualidade conforme chegam próximo sua data de venda. + +Nós temos um sistema instalado que atualiza automaticamente os preços do nosso estoque. Esse sistema foi criado por um rapaz sem noção chamado Leeroy, que agora se dedica à novas aventuras. Seu trabalho será adicionar uma nova funcionalidade para o nosso sistema para que possamos vender uma nova categoria de itens. + +## Descrição preliminar + +Vamos dar uma breve introdução do nosso sistema: + +* Todos os itens (classe `Item`) possuem uma propriedade chamada `SellIn` que informa o número de dias que temos para vende-lo +* Todos os itens possuem uma propriedade chamada `quality` que informa o quão valioso é o item. +* No final do dia, nosso sistema decrementa os valores das propriedades `SellIn` e `quality` de cada um dos itens do estoque através do método `updateQuality`. + +Bastante simples, não é? Bem, agora que as coisas ficam interessantes: + +* Quando a data de venda do item tiver passado, a qualidade (`quality`) do item diminui duas vezes mais rapido. +* A qualidade (`quality`) do item não pode ser negativa +* O "Queijo Brie envelhecido" (`Aged Brie`), aumenta sua qualidade (`quality`) em `1` unidade a medida que envelhece. +* A qualidade (`quality`) de um item não pode ser maior que 50. +* O item "Sulfuras" (`Sulfuras`), por ser um item lendário, não precisa ter uma data de venda (`SellIn`) e sua qualidade (`quality`) não precisa ser diminuida. +* O item "Entrada para os Bastidores" (`Backstage Passes`), assim como o "Queijo Brie envelhecido", aumenta sua qualidade (`quality`) a medida que o dia da venda (`SellIn`) se aproxima; + * A qualidade (`quality`) aumenta em `2` unidades quando a data de venda (`SellIn`) é igual ou menor que `10`. + * A qualidade (`quality`) aumenta em `3` unidades quando a data de venda (`SellIn`) é igual ou menor que `5`. + * A qualidade (`quality`) do item vai direto à `0` quando a data de venda (`SellIn`) tiver passado. + +Nós recentemente assinamos um suprimento de itens Conjurados Magicamente. Isto requer que nós atualizemos nosso sistema: + +* Os itens "Conjurados" (`Conjured`) diminuem a qualidade (`quality`) duas vezes mais rápido que os outros itens. + +Sinta-se livre para fazer qualquer alteração no método `updateQuality` e adicionar código novo contanto que tudo continue funcionando perfeitamente. Entretanto, não altere o código da classe `Item` ou da propriedade `Items` na classe `GildedRose` pois elas pertencem ao Goblin que irá te matar com um golpe pois ele não acredita na cultura de código compartilhado. + +## Notas Finais + +Para esclarecer: Um item não pode ter uma qualidade (`quality`) maior que `50`, entretanto as "Sulfuras" por serem um item lendário vão ter uma qualidade imutavel de `80`. diff --git a/GildedRoseRequirements_ru.txt b/GildedRoseRequirements_ru.txt new file mode 100644 index 00000000..cdb1355b --- /dev/null +++ b/GildedRoseRequirements_ru.txt @@ -0,0 +1,43 @@ +====================================== +Технические требования «Gilded Rose» +====================================== + +Привет и добро пожаловать в команду «Gilded Rose». Как вы знаете, мы небольшая гостиница удобно расположенная +в известном городе под руководством дружественного управляющего по имени Эллисон. Также мы занимаемся покупкой +и продажей только самых лучших товаров. К несчастью, качество наших товаров постоянно ухудшается по мере приближения +к максимальному сроку хранения. Существует информационная система, которая ведет переучет всех товаров. Система +была разработана рубаха-парнем, по имени Leeroy, который отправился за поисками новых приключений. Ваша задача +заключается в том, чтобы добавить новый функционал в нашу систему, чтобы мы могли начать продавать новую категорию +товаров. + +В общих чертах система работает следующим образом: + + - Все товары имеют свойство «sellIn» (срок хранения), которое обозначает количество + дней в течение которых мы должны продать товар; + - Все товары имеют свойство «Quality» (качество), которое обозначает насколько качественным является товар; + - В конце дня наша система снижает значение обоих свойств для каждого товара. + +Довольно просто, не правда ли? Тут-то и начинается самое интересное: + + - После того, как срок храния прошел, качество товара ухудшается в два раза быстрее; + - Качество товара никогда не может быть отрицательным; + - Для товара «Aged Brie» качество увеличивается пропорционально возрасту; + - Качество товара никогда не может быть больше, чем 50; + - «Sulfuras» является легендарным товаром, поэтому у него нет срока хранения и не подвержен ухудшению качества; + - Качество «Backstage passes» также, как и «Aged Brie», увеличивается по мере приближения к сроку хранения. + Качество увеличивается на 2, когда до истечения срока хранения 10 или менее дней и на 3, + если до истечения 5 или менее дней. При этом качество падает до 0 после даты проведения концерта. + +Недавно мы нашли поставщика магических товаров. Для того, чтобы продавать его товары необходимо обновить нашу +систему следующим образом: + + - «Conjured» товары теряют качество в два раза быстрее, чем обычные товары. + +Не стесняйтесь вносить любые изменения в метод «UpdateQuality» и добавлять любой новый код до тех пор, +пока система работает корректно. Тем не менее, не меняйте класс «Item» или его свойства, так как он принадлежит +сидящему в углу гоблину, который очень яростен и поэтому выстрелит в вас поскольку не верит в принцип +совместного владения кодом (вы можете сделать метод «UpdateQuality» и свойства класса «Item» статическими +если хотите, мы вас прикроем). + +Просто для уточнения, товар никогда не может иметь качество выше чем 50, однако легендарный товар «Sulfuras» +имеет качество 80 и оно никогда не меняется. diff --git a/GildedRoseRequirements_th.md b/GildedRoseRequirements_th.md new file mode 100644 index 00000000..267f4b67 --- /dev/null +++ b/GildedRoseRequirements_th.md @@ -0,0 +1,37 @@ +# ข้อกำหนดความต้องการของระบบ Gilded Rose + +ยินดีต้อนรับสู่ทีม Gilded Rose อย่างที่คุณทราบแล้วว่า เราคือโรงแรมขนาดเล็กที่ตั้งอยู่ทำเลทองของประเทศกรุงเทพ +นอกจากนี้เรายังซื้อและขายเฉพาะสินค้าที่ดีที่สุด แต่น่าเสียดายยิ่งใกล้ถึงวันกำหนดขายสินค้า คุณภาพของสินค้าก็จะลดลงอย่างต่อเนื่อง +ตอนนี้เรามีระบบที่ช่วยอัปเดตสินค้าคงคลังให้เรา มันถูกพัฒนาโดยโปรแกรมเมอร์ศิษย์เอกของพระอินทร์ ซึ่งโปรแกรมเมอร์คนนี้ได้ย้ายกลับไปอยู่กับพระอินทร์ +เป็นการถาวรแล้ว เราจึงอยากให้คุณช่วยเพิ่มฟีเจอร์ใหม่เพื่อให้เราสามารถขายสินค้าประเภทใหม่ได้ + +ก่อนอื่นเราขอแนะนำการทำงานของระบบปัจจุบัน: + +- สินค้าแต่ละชิ้นจะต้องขายภายในระยะเวลาที่กำหนดไว้ในค่า "SellIn" (มีหน่วยเป็น*วัน*) +- สินค้าแต่ละชิ้นจะระบุค่า "Quality" ที่สามารถบ่งชี้ถึงมูลค่าของสินค้า +- ระบบจะทำการคำนวนค่า "SellIn" และ "Quality" ของสินค้าทุกชิ้นในช่วงเวลาสุดท้ายของทุกวัน + +ง่ายอะดิ, ใช่ป่ะ? สิ่งที่น่าสนใจมันอยู่ตรงนี้: + +- เมื่อสินค้าเลยกำหนดขายไปแล้ว "Quality" ของสินค้าจะลดลงเป็นสองเท่าจากปกติ +- "Quality" ของสินค้าไม่มีทางติดลบได้ +- คุณภาพของสินค้าประเภท "Aged Brie" จะเพิ่มสูงขึ้นตามระยะเวลา +- "Quality" มีค่าสูงสุดคือ 50 +- สินค้าประเภท "Sulfuras" เป็นสินค้าในตำนานหายาก คุณภาพของสินค้าจะไม่ลดลงและไม่ได้มีไว้เพื่อขาย +- สินค้าประเภท "Backstage passes" คุณภาพของสินค้าเหมือนกันกับสินค้าประเภท aged brie +เพียงแต่หากใกล้ถึงวันแสดง 10 วันก่อนหน้าหรือน้อยกว่าคุณภาพของสินค้าประเภทนี้จะเพิ่มทีละ 2 และถ้าใกล้ถึงวันก่อนวันแสดง 5 วันหรือน้อยกว่าคุณภาพจะเพิ่มทีละ 3 +อย่างไรก็ตามคุณภาพของสินค้าจะกลายเป็น 0 ทันทีหลังการแสดงจบลง + +เมื่อเร็วๆ นี้เราพึ่งได้ลงนามกับผู้ผลิตสินค้าประเภทของขลังและของปลุกเสก +และเราต้องการที่จะเพิ่มความสามารถใหม่เข้าไปในระบบ: + +- คุณภาพสินค้าประเภท "Conjured" จะเสื่อมลงเร็วกว่าสินค้าปกติถึงสองเท่าจากปกติ + +คุณสามารถปรับปรุงแก้ไขได้ทุกอย่างภายในเมธอด UpdateQuality และสามารถเพิ่มโค้ดใหม่ได้เลย +ตราบใดที่ทุกอย่างยังคงทำงานได้ถูกต้อง อย่างไรก็ตามห้ามแก้ไขคลาส Item และคุณสมบัติของคลาส +เพราะมันถูกลงอาคมจากโปรแกรมเมอร์จอมขมังเวทย์ที่ไม่เชื่อในเรื่องการแบ่งปันความเป็นเจ้าของโค้ดร่วมกับผู้อื่น +(ถ้าคุณยังอยากจะแก้ไขทั้งเมธอด UpdateQuality และคุณสมบัติของคลาส Item เราก็จะ +นิมนต์หลวงปู่เค็มมาช่วยคุ้มกันคุณ) + +ย้ำอีกครั้งหนึ่ง, คุณภาพของสินค้ามีค่าสูงสุดคือ 50 +อย่างไรก็ตามสินค้าประเภท "Sulfuras" เป็นสินค้าหายากในตำนานมีค่า Quality เป็น 80 เสมอไม่เปลี่ยนแปลง. diff --git a/GildedRoseRequirements_zh.txt b/GildedRoseRequirements_zh.txt new file mode 100644 index 00000000..f513770e --- /dev/null +++ b/GildedRoseRequirements_zh.txt @@ -0,0 +1,32 @@ +====================================== +Gilded Rose 需求描述 +====================================== + + +欢迎来到镶金玫瑰(Gilded Rose)团队。如你所知,我们是主城(暴风城)中的一个小旅店,店主非常友好,名叫Allison。我们也售卖最好的物品。不幸的是,物品品质会随着销售期限的接近而不断下降。 +我们有一个系统来更新库存信息。系统是由一个火车王(魔兽世界中导致团灭的猪队友)Leeroy所开发的,他已经不在这了。 +你的任务是添加新功能,这样我们就可以售卖新的物品。 + +先介绍一下我们的系统: + + - 每种物品都具备一个销售期限`SellIn`,表示我们要在多少天之前把物品卖出去 + - 每种的物品都具备品质值`Quality`,表示物品的品质 + - 每天结束时,系统会降低每种物品的这两个数值 + +很简单吧?这还有些更有意思的: + + - 一旦销售期限过期,品质`Quality`会以双倍速度加速下降 + - 物品的品质`Quality`永远不会为负值 + - "Aged Brie"(陈年布利奶酪)的品质`Quality`会随着时间推移而提高 + - 物品的品质`Quality`永远不会超过50 + - 传奇物品"Sulfuras"(萨弗拉斯—炎魔拉格纳罗斯之手)永不过期,也不会降低品质`Quality` + - "Backstage passes"(后台通行证)与"Aged Brie"(陈年布利奶酪)类似,其品质`Quality`会随着时间推移而提高;当还剩10天或更少的时候,品质`Quality`每天提高2;当还剩5天或更少的时候,品质`Quality`每天提高3;但一旦过期,品质就会降为0 + + +我们最近签约了一个召唤物品供应商。这需要对我们的系统进行升级: + + - "Conjured"(召唤物品)的品质`Quality`下降速度比正常物品快一倍 + +请随意对**UpdateQuality()**函数进行修改和添加新代码,只要系统还能正常工作。然而,不要修改Item类或其属性,因为那属于角落里的地精,他会非常愤怒地爆你头,因为他不相信代码共享所有制(如果你愿意,你可以将UpdateQuality方法和Items属性改为静态的,我们会掩护你的)。 + +再次澄清,每种物品的品质不会超过50,然而"Sulfuras"(萨弗拉斯—炎魔拉格纳罗斯之手)是一个传奇物品,因此它的品质是80且永远不变。 diff --git a/Groovy/.gitignore b/Groovy/.gitignore new file mode 100644 index 00000000..f0a72837 --- /dev/null +++ b/Groovy/.gitignore @@ -0,0 +1,96 @@ + +# Created by https://www.gitignore.io/api/groovy,intellij,eclipse,vim + +#!! ERROR: groovy is undefined. Use list command to see defined gitignore types !!# + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/ + +## File-based project format: +*.iws +*.iml + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Gradle +.gradle +build/ + +### Eclipse ### + +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + + +### Vim ### +# swap +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +# session +Session.vim +# temporary +.netrwhist +*~ +# auto-generated tag files +tags diff --git a/Groovy/.idea/compiler.xml b/Groovy/.idea/compiler.xml new file mode 100644 index 00000000..96cc43ef --- /dev/null +++ b/Groovy/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Groovy/.idea/copyright/profiles_settings.xml b/Groovy/.idea/copyright/profiles_settings.xml new file mode 100644 index 00000000..e7bedf33 --- /dev/null +++ b/Groovy/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Groovy/.idea/misc.xml b/Groovy/.idea/misc.xml new file mode 100644 index 00000000..c6d8fb73 --- /dev/null +++ b/Groovy/.idea/misc.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Groovy/.idea/modules.xml b/Groovy/.idea/modules.xml new file mode 100644 index 00000000..c1a39855 --- /dev/null +++ b/Groovy/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Groovy/README.txt b/Groovy/README.txt new file mode 100644 index 00000000..664827d7 --- /dev/null +++ b/Groovy/README.txt @@ -0,0 +1,17 @@ +Welcome to the Groovy Gilded Rose +================================= + +to run the test, you can either: +- run them from your favorite IDE + - make sure you have installed language support for Groovy + - IntelliJ: + - open project + - choose this folder (Groovy) + - Eclipse: + - new Groovy Project + - choose this folder (Groovy) as the project folder + - add JUnit to build path +- run the test from the src/ folder in your shell: + - $ cd src/ + - $ groovy com/gildedrose/GildedRoseTest.groovy + diff --git a/Groovy/build.gradle b/Groovy/build.gradle new file mode 100644 index 00000000..75332f79 --- /dev/null +++ b/Groovy/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'groovy' +} + +repositories { + mavenCentral() +} + +sourceCompatibility = '1.8' +targetCompatibility = '1.8' +group='com.gildedrose' + +dependencies { + implementation 'org.codehaus.groovy:groovy:3.0.12' + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.6.2' +} + +test { + useJUnitPlatform() +} diff --git a/Groovy/gradle/wrapper/gradle-wrapper.properties b/Groovy/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..05679dc3 --- /dev/null +++ b/Groovy/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Groovy/gradlew b/Groovy/gradlew new file mode 100755 index 00000000..744e882e --- /dev/null +++ b/Groovy/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## 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='"-Xmx64m" "-Xms64m"' + +# 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 + ;; + MSYS* | 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 or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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" + +exec "$JAVACMD" "$@" diff --git a/Groovy/gradlew.bat b/Groovy/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/Groovy/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@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 Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@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="-Xmx64m" "-Xms64m" + +@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 execute + +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 execute + +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 + +: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 %* + +: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/Groovy/src/main/groovy/com/gildedrose/GildedRose.groovy b/Groovy/src/main/groovy/com/gildedrose/GildedRose.groovy new file mode 100644 index 00000000..128f17df --- /dev/null +++ b/Groovy/src/main/groovy/com/gildedrose/GildedRose.groovy @@ -0,0 +1,62 @@ +package com.gildedrose + +class GildedRose { + Item[] items + + GildedRose(Item[] items) { + this.items = items + } + + void updateQuality() { + for (int i = 0; i < items.length; i++) { + if (!items[i].name.equals("Aged Brie") + && !items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].quality > 0) { + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].quality = items[i].quality - 1 + } + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1 + + if (items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].sellIn < 11) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1 + } + } + + if (items[i].sellIn < 6) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1 + } + } + } + } + } + + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].sellIn = items[i].sellIn - 1 + } + + if (items[i].sellIn < 0) { + if (!items[i].name.equals("Aged Brie")) { + if (!items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].quality > 0) { + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].quality = items[i].quality - 1 + } + } + } else { + items[i].quality = items[i].quality - items[i].quality + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1 + } + } + } + } + } +} diff --git a/Groovy/src/main/groovy/com/gildedrose/Item.groovy b/Groovy/src/main/groovy/com/gildedrose/Item.groovy new file mode 100644 index 00000000..bc40e275 --- /dev/null +++ b/Groovy/src/main/groovy/com/gildedrose/Item.groovy @@ -0,0 +1,21 @@ +package com.gildedrose + +class Item { + + String name + + int sellIn + + int quality + + Item(String name, int sellIn, int quality) { + this.name = name + this.sellIn = sellIn + this.quality = quality + } + + @Override + String toString() { + return this.name + ", " + this.sellIn + ", " + this.quality + } +} diff --git a/Groovy/src/test/groovy/com/gildedrose/GildedRoseTest.groovy b/Groovy/src/test/groovy/com/gildedrose/GildedRoseTest.groovy new file mode 100644 index 00000000..4b3c3b2b --- /dev/null +++ b/Groovy/src/test/groovy/com/gildedrose/GildedRoseTest.groovy @@ -0,0 +1,15 @@ +package com.gildedrose + +import org.junit.jupiter.api.Test + +class GildedRoseTest { + + @Test + void "foo"() { + def items = [ new Item("foo", 0, 0) ] as Item[] + def app = new GildedRose(items) + app.updateQuality() + assert "fixme" == app.items[0].name + } + +} diff --git a/Groovy/src/test/groovy/com/gildedrose/TexttestFixture.groovy b/Groovy/src/test/groovy/com/gildedrose/TexttestFixture.groovy new file mode 100644 index 00000000..8984af77 --- /dev/null +++ b/Groovy/src/test/groovy/com/gildedrose/TexttestFixture.groovy @@ -0,0 +1,32 @@ +package com.gildedrose + +println("OMGHAI!") + +Item[] items = [ + new Item("+5 Dexterity Vest", 10, 20), + new Item("Aged Brie", 2, 0), + new Item("Elixir of the Mongoose", 5, 7), + new Item("Sulfuras, Hand of Ragnaros", 0, 80), + new Item("Sulfuras, Hand of Ragnaros", -1, 80), + new Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this conjured item does not work properly yet + new Item("Conjured Mana Cake", 3, 6)] as Item[] + +GildedRose app = new GildedRose(items) + +int days = 2 +if (args.length > 0) { + days = Integer.parseInt(args[0]) + 1 +} + +for (int i = 0; i < days; i++) { + println("-------- day " + i + " --------") + println("name, sellIn, quality") + for (Item item in items) { + println(item) + } + println "" + app.updateQuality() +} diff --git a/Java-Approvals/.editorconfig b/Java-Approvals/.editorconfig new file mode 100644 index 00000000..2f1b8410 --- /dev/null +++ b/Java-Approvals/.editorconfig @@ -0,0 +1,6 @@ +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/Java-Approvals/.gitignore b/Java-Approvals/.gitignore new file mode 100644 index 00000000..6b8ed5ae --- /dev/null +++ b/Java-Approvals/.gitignore @@ -0,0 +1,13 @@ +.idea/ +*.iml +target/ + +.classpath +.project +bin/ +.settings/ + +# Gradle +.gradle +/build/ + diff --git a/Java-Approvals/.mvn/wrapper/MavenWrapperDownloader.java b/Java-Approvals/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..b901097f --- /dev/null +++ b/Java-Approvals/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/Java-Approvals/.mvn/wrapper/maven-wrapper.jar b/Java-Approvals/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..c1dd12f1 Binary files /dev/null and b/Java-Approvals/.mvn/wrapper/maven-wrapper.jar differ diff --git a/Java-Approvals/.mvn/wrapper/maven-wrapper.properties b/Java-Approvals/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..e83fa695 --- /dev/null +++ b/Java-Approvals/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/Java-Approvals/README.md b/Java-Approvals/README.md new file mode 100644 index 00000000..cfc16fa0 --- /dev/null +++ b/Java-Approvals/README.md @@ -0,0 +1,10 @@ +# Gilded Rose in Java with Approval Tests + +This folder has a unit test that uses [Approvals](https://github.com/approvals/approvaltests.java) + +There are two test cases here with different styles: + +* "foo" is more similar to the unit test from the 'Java' version +* "thirtyDays" is more similar to the TextTest from the 'Java' version + +I suggest choosing one style to develop and deleting the other. diff --git a/Java-Approvals/build.gradle b/Java-Approvals/build.gradle new file mode 100644 index 00000000..21bbe647 --- /dev/null +++ b/Java-Approvals/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.6.2' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.6.2' + testImplementation 'com.approvaltests:approvaltests:12.3.1' +} + +group = 'com.gildedrose' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '1.8' + +test { + useJUnitPlatform() +} diff --git a/Java-Approvals/gradle/wrapper/gradle-wrapper.jar b/Java-Approvals/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..cc4fdc29 Binary files /dev/null and b/Java-Approvals/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Java-Approvals/gradle/wrapper/gradle-wrapper.properties b/Java-Approvals/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..622ab64a --- /dev/null +++ b/Java-Approvals/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Java-Approvals/gradlew b/Java-Approvals/gradlew new file mode 100755 index 00000000..2fe81a7d --- /dev/null +++ b/Java-Approvals/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## 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='"-Xmx64m" "-Xms64m"' + +# 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 or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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" + +exec "$JAVACMD" "$@" diff --git a/Java-Approvals/gradlew.bat b/Java-Approvals/gradlew.bat new file mode 100644 index 00000000..9618d8d9 --- /dev/null +++ b/Java-Approvals/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@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="-Xmx64m" "-Xms64m" + +@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/Java-Approvals/mvnw b/Java-Approvals/mvnw new file mode 100755 index 00000000..5643201c --- /dev/null +++ b/Java-Approvals/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + 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 + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Java-Approvals/mvnw.cmd b/Java-Approvals/mvnw.cmd new file mode 100644 index 00000000..23b7079a --- /dev/null +++ b/Java-Approvals/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/Java-Approvals/pom.xml b/Java-Approvals/pom.xml new file mode 100644 index 00000000..adb47d25 --- /dev/null +++ b/Java-Approvals/pom.xml @@ -0,0 +1,54 @@ + + 4.0.0 + + com.gildedrose + gilded-rose-kata + 0.0.1-SNAPSHOT + + + 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-Approvals/settings.gradle b/Java-Approvals/settings.gradle new file mode 100644 index 00000000..7ce136a1 --- /dev/null +++ b/Java-Approvals/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'gilded-rose-kata' diff --git a/Java-Approvals/src/main/java/com/gildedrose/GildedRose.java b/Java-Approvals/src/main/java/com/gildedrose/GildedRose.java new file mode 100644 index 00000000..e6feb751 --- /dev/null +++ b/Java-Approvals/src/main/java/com/gildedrose/GildedRose.java @@ -0,0 +1,62 @@ +package com.gildedrose; + +class GildedRose { + Item[] items; + + public GildedRose(Item[] items) { + this.items = items; + } + + public void updateQuality() { + for (int i = 0; i < items.length; i++) { + if (!items[i].name.equals("Aged Brie") + && !items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].quality > 0) { + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].quality = items[i].quality - 1; + } + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + + if (items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].sellIn < 11) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + + if (items[i].sellIn < 6) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + } + } + } + + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].sellIn = items[i].sellIn - 1; + } + + if (items[i].sellIn < 0) { + if (!items[i].name.equals("Aged Brie")) { + if (!items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].quality > 0) { + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].quality = items[i].quality - 1; + } + } + } else { + items[i].quality = items[i].quality - items[i].quality; + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + } + } + } +} \ No newline at end of file diff --git a/Java-Approvals/src/main/java/com/gildedrose/Item.java b/Java-Approvals/src/main/java/com/gildedrose/Item.java new file mode 100644 index 00000000..465729ec --- /dev/null +++ b/Java-Approvals/src/main/java/com/gildedrose/Item.java @@ -0,0 +1,21 @@ +package com.gildedrose; + +public class Item { + + public String name; + + public int sellIn; + + public int quality; + + public Item(String name, int sellIn, int quality) { + this.name = name; + this.sellIn = sellIn; + this.quality = quality; + } + + @Override + public String toString() { + return this.name + ", " + this.sellIn + ", " + this.quality; + } +} diff --git a/Java-Approvals/src/main/java/com/gildedrose/Program.java b/Java-Approvals/src/main/java/com/gildedrose/Program.java new file mode 100644 index 00000000..0d437106 --- /dev/null +++ b/Java-Approvals/src/main/java/com/gildedrose/Program.java @@ -0,0 +1,32 @@ +package com.gildedrose; + +public class Program { + + public static void main(String... args) { + System.out.println("OMGHAI!"); + + Item[] items = new Item[] { + new Item("+5 Dexterity Vest", 10, 20), + new Item("Aged Brie", 2, 0), + new Item("Elixir of the Mongoose", 5, 7), + new Item("Sulfuras, Hand of Ragnaros", 0, 80), + new Item("Sulfuras, Hand of Ragnaros", -1, 80), + new Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this conjured item does not work properly yet + new Item("Conjured Mana Cake", 3, 6) }; + + GildedRose app = new GildedRose(items); + + for (int i = 0; i < 31; i++) { + System.out.println("-------- day " + i + " --------"); + System.out.println("name, sellIn, quality"); + for (int j = 0; j < items.length; j++) { + System.out.println(items[j]); + } + System.out.println(""); + app.updateQuality(); + } + } +} diff --git a/Java-Approvals/src/test/java/com/gildedrose/GildedRoseApprovalTest.java b/Java-Approvals/src/test/java/com/gildedrose/GildedRoseApprovalTest.java new file mode 100644 index 00000000..bffec76b --- /dev/null +++ b/Java-Approvals/src/test/java/com/gildedrose/GildedRoseApprovalTest.java @@ -0,0 +1,38 @@ +package com.gildedrose; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import org.approvaltests.Approvals; +import org.approvaltests.reporters.DiffReporter; +import org.approvaltests.reporters.UseReporter; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Text; + +@UseReporter(DiffReporter.class) +public class GildedRoseApprovalTest { + + @Test + public void foo() { + + Item[] items = new Item[] { new Item("foo", 0, 0) }; + GildedRose app = new GildedRose(items); + app.updateQuality(); + + Approvals.verifyAll("Items", items); + } + + @Test + public void thirtyDays() { + + ByteArrayOutputStream fakeoutput = new ByteArrayOutputStream(); + System.setOut(new PrintStream(fakeoutput)); + System.setIn(new ByteArrayInputStream("a\n".getBytes())); + + Program.main(); + String output = fakeoutput.toString(); + + Approvals.verify(output); + } +} diff --git a/Java-Cucumber/.gitignore b/Java-Cucumber/.gitignore new file mode 100644 index 00000000..250a30ad --- /dev/null +++ b/Java-Cucumber/.gitignore @@ -0,0 +1,4 @@ +.idea/ +.gradle/ +build/ +target/ diff --git a/Java-Cucumber/.mvn/wrapper/MavenWrapperDownloader.java b/Java-Cucumber/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..b901097f --- /dev/null +++ b/Java-Cucumber/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/Java-Cucumber/.mvn/wrapper/maven-wrapper.jar b/Java-Cucumber/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..c1dd12f1 Binary files /dev/null and b/Java-Cucumber/.mvn/wrapper/maven-wrapper.jar differ diff --git a/Java-Cucumber/.mvn/wrapper/maven-wrapper.properties b/Java-Cucumber/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..e83fa695 --- /dev/null +++ b/Java-Cucumber/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/Java-Cucumber/README.md b/Java-Cucumber/README.md new file mode 100644 index 00000000..24d68214 --- /dev/null +++ b/Java-Cucumber/README.md @@ -0,0 +1,7 @@ +### How to do the BDD + +1. Write a test scenario in Feature file: **src/test/resources/GildedRose.feature** +2. Modify the StepDefinitions file to match the Feature description in: **src/test/java/com/gildedrose/StepDefinitions.java** +3. Run: **./gradlew cucumber** from project dir + +Note: Please check https://cucumber.io for syntax references. \ No newline at end of file diff --git a/Java-Cucumber/build.gradle b/Java-Cucumber/build.gradle new file mode 100644 index 00000000..08369578 --- /dev/null +++ b/Java-Cucumber/build.gradle @@ -0,0 +1,44 @@ +apply plugin: "groovy" +apply plugin: "java" + +repositories { + mavenCentral() +} + +sourceCompatibility = '1.8' +targetCompatibility = '1.8' + +dependencies { + implementation 'org.codehaus.groovy:groovy:3.0.8' + testImplementation 'io.cucumber:cucumber-java:6.10.4' + testImplementation 'io.cucumber:cucumber-junit:6.10.4' + testImplementation 'junit:junit:4.13.2' +} + +configurations { + cucumberRuntime { + extendsFrom testImplementation + } +} + +test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } +} + +task cucumber() { + dependsOn assemble, testClasses + doLast { + javaexec { + main = "io.cucumber.core.cli.Main" + classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output + args = [ + '--plugin', 'pretty', + '--plugin', 'html:target/cucumber-report.html', + '--glue', 'com.gildedrose', + 'src/test/resources'] + } + } +} diff --git a/Java-Cucumber/gradle/wrapper/gradle-wrapper.jar b/Java-Cucumber/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..94114481 Binary files /dev/null and b/Java-Cucumber/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Java-Cucumber/gradle/wrapper/gradle-wrapper.properties b/Java-Cucumber/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..0dddb9d5 --- /dev/null +++ b/Java-Cucumber/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Jul 05 09:24:35 CEST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip diff --git a/Java-Cucumber/gradlew b/Java-Cucumber/gradlew new file mode 100755 index 00000000..9d82f789 --- /dev/null +++ b/Java-Cucumber/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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 +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# 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 + +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" ] ; 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 + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/Java-Cucumber/gradlew.bat b/Java-Cucumber/gradlew.bat new file mode 100644 index 00000000..8a0b282a --- /dev/null +++ b/Java-Cucumber/gradlew.bat @@ -0,0 +1,90 @@ +@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 + +@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= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@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 Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_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=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +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/Java-Cucumber/mvnw b/Java-Cucumber/mvnw new file mode 100755 index 00000000..5643201c --- /dev/null +++ b/Java-Cucumber/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + 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 + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Java-Cucumber/mvnw.cmd b/Java-Cucumber/mvnw.cmd new file mode 100644 index 00000000..23b7079a --- /dev/null +++ b/Java-Cucumber/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/Java-Cucumber/pom.xml b/Java-Cucumber/pom.xml new file mode 100644 index 00000000..06f5e96f --- /dev/null +++ b/Java-Cucumber/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + com.gildedrose + gilded-rose-kata + 0.0.1-SNAPSHOT + + + 1.8 + 4.13.2 + 6.10.4 + 3.1 + 3.0.0-M4 + UTF-8 + + + + + junit + junit + ${junit.version} + test + + + io.cucumber + cucumber-java + ${cucumber.java.version} + test + + + io.cucumber + cucumber-junit + ${cucumber.java.version} + test + + + + + + + maven-compiler-plugin + ${maven.maven-compiler-plugin.version} + + ${java.version} + ${java.version} + + + + maven-surefire-plugin + ${maven.maven-surefire-plugin.version} + + + + + diff --git a/Java-Cucumber/src/main/java/com/gildedrose/GildedRose.java b/Java-Cucumber/src/main/java/com/gildedrose/GildedRose.java new file mode 100644 index 00000000..87a3b926 --- /dev/null +++ b/Java-Cucumber/src/main/java/com/gildedrose/GildedRose.java @@ -0,0 +1,62 @@ +package com.gildedrose; + +class GildedRose { + Item[] items; + + public GildedRose(Item[] items) { + this.items = items; + } + + public void updateQuality() { + for (int i = 0; i < items.length; i++) { + if (!items[i].name.equals("Aged Brie") + && !items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].quality > 0) { + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].quality = items[i].quality - 1; + } + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + + if (items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].sellIn < 11) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + + if (items[i].sellIn < 6) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + } + } + } + + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].sellIn = items[i].sellIn - 1; + } + + if (items[i].sellIn < 0) { + if (!items[i].name.equals("Aged Brie")) { + if (!items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].quality > 0) { + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].quality = items[i].quality - 1; + } + } + } else { + items[i].quality = items[i].quality - items[i].quality; + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + } + } + } +} diff --git a/Java-Cucumber/src/main/java/com/gildedrose/Item.java b/Java-Cucumber/src/main/java/com/gildedrose/Item.java new file mode 100644 index 00000000..465729ec --- /dev/null +++ b/Java-Cucumber/src/main/java/com/gildedrose/Item.java @@ -0,0 +1,21 @@ +package com.gildedrose; + +public class Item { + + public String name; + + public int sellIn; + + public int quality; + + public Item(String name, int sellIn, int quality) { + this.name = name; + this.sellIn = sellIn; + this.quality = quality; + } + + @Override + public String toString() { + return this.name + ", " + this.sellIn + ", " + this.quality; + } +} diff --git a/Java-Cucumber/src/main/java/com/gildedrose/TexttestFixture.java b/Java-Cucumber/src/main/java/com/gildedrose/TexttestFixture.java new file mode 100644 index 00000000..d059c88f --- /dev/null +++ b/Java-Cucumber/src/main/java/com/gildedrose/TexttestFixture.java @@ -0,0 +1,37 @@ +package com.gildedrose; + +public class TexttestFixture { + public static void main(String[] args) { + System.out.println("OMGHAI!"); + + Item[] items = new Item[] { + new Item("+5 Dexterity Vest", 10, 20), // + new Item("Aged Brie", 2, 0), // + new Item("Elixir of the Mongoose", 5, 7), // + new Item("Sulfuras, Hand of Ragnaros", 0, 80), // + new Item("Sulfuras, Hand of Ragnaros", -1, 80), + new Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this conjured item does not work properly yet + new Item("Conjured Mana Cake", 3, 6) }; + + GildedRose app = new GildedRose(items); + + int days = 2; + if (args.length > 0) { + days = Integer.parseInt(args[0]) + 1; + } + + for (int i = 0; i < days; i++) { + System.out.println("-------- day " + i + " --------"); + System.out.println("name, sellIn, quality"); + for (Item item : items) { + System.out.println(item); + } + System.out.println(); + app.updateQuality(); + } + } + +} diff --git a/Java-Cucumber/src/test/java/com/gildedrose/RunCucumberTest.java b/Java-Cucumber/src/test/java/com/gildedrose/RunCucumberTest.java new file mode 100644 index 00000000..e955f030 --- /dev/null +++ b/Java-Cucumber/src/test/java/com/gildedrose/RunCucumberTest.java @@ -0,0 +1,11 @@ +package com.gildedrose; + +import io.cucumber.junit.Cucumber; +import io.cucumber.junit.CucumberOptions; +import org.junit.runner.RunWith; + +@RunWith(Cucumber.class) +@CucumberOptions() +public class RunCucumberTest { +} + diff --git a/Java-Cucumber/src/test/java/com/gildedrose/StepDefinitions.java b/Java-Cucumber/src/test/java/com/gildedrose/StepDefinitions.java new file mode 100644 index 00000000..2d79ec51 --- /dev/null +++ b/Java-Cucumber/src/test/java/com/gildedrose/StepDefinitions.java @@ -0,0 +1,29 @@ +package com.gildedrose; + +import static org.junit.Assert.*; + +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; + +public class StepDefinitions { + private Item[] items = new Item[1]; + private GildedRose app; + + @Given("The item as {string}") + public void initial_sellin_is_and_quality_is(String name) { + items[0] = new Item(name, 0, 0); + app = new GildedRose(items); + } + + @When("I update the quality") + public void i_update_the_quality() { + app.updateQuality(); + } + + @Then("I should get item as {string}") + public void i_should_get_sellin_as_and_quality_as(String expected) { + assertEquals(expected, app.items[0].name); + } +} + diff --git a/Java-Cucumber/src/test/resources/com/gildedrose/GildedRose.feature b/Java-Cucumber/src/test/resources/com/gildedrose/GildedRose.feature new file mode 100644 index 00000000..72f0910c --- /dev/null +++ b/Java-Cucumber/src/test/resources/com/gildedrose/GildedRose.feature @@ -0,0 +1,7 @@ +Feature: Gilded Rose quality + I want to know if the quality is updated properly + + Scenario: Checking foo + Given The item as "fixme" + When I update the quality + Then I should get item as "foo" diff --git a/Java-Cucumber/src/test/resources/cucumber.properties b/Java-Cucumber/src/test/resources/cucumber.properties new file mode 100644 index 00000000..b48dd63b --- /dev/null +++ b/Java-Cucumber/src/test/resources/cucumber.properties @@ -0,0 +1 @@ +cucumber.publish.quiet=true diff --git a/Java-Spock/.gitignore b/Java-Spock/.gitignore new file mode 100644 index 00000000..cd3d2f4b --- /dev/null +++ b/Java-Spock/.gitignore @@ -0,0 +1,3 @@ +.idea/ +.gradle/ +build/ \ No newline at end of file diff --git a/Java-Spock/build.gradle b/Java-Spock/build.gradle new file mode 100644 index 00000000..a0a10999 --- /dev/null +++ b/Java-Spock/build.gradle @@ -0,0 +1,21 @@ +apply plugin: "groovy" + +repositories { + mavenCentral() +} + +sourceCompatibility = '1.8' +targetCompatibility = '1.8' + +dependencies { + implementation 'org.codehaus.groovy:groovy:3.0.8' + testImplementation platform('org.spockframework:spock-bom:2.0-groovy-3.0') + testImplementation 'org.spockframework:spock-core' +} + +test { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed" + } +} diff --git a/Java-Spock/gradle/wrapper/gradle-wrapper.jar b/Java-Spock/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..94114481 Binary files /dev/null and b/Java-Spock/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Java-Spock/gradle/wrapper/gradle-wrapper.properties b/Java-Spock/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..0dddb9d5 --- /dev/null +++ b/Java-Spock/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Jul 05 09:24:35 CEST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip diff --git a/Java-Spock/gradlew b/Java-Spock/gradlew new file mode 100644 index 00000000..9d82f789 --- /dev/null +++ b/Java-Spock/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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 +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# 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 + +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" ] ; 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 + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/Java-Spock/gradlew.bat b/Java-Spock/gradlew.bat new file mode 100644 index 00000000..8a0b282a --- /dev/null +++ b/Java-Spock/gradlew.bat @@ -0,0 +1,90 @@ +@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 + +@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= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@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 Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_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=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +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/Java-Spock/src/main/java/com/gildedrose/GildedRose.java b/Java-Spock/src/main/java/com/gildedrose/GildedRose.java new file mode 100644 index 00000000..87a3b926 --- /dev/null +++ b/Java-Spock/src/main/java/com/gildedrose/GildedRose.java @@ -0,0 +1,62 @@ +package com.gildedrose; + +class GildedRose { + Item[] items; + + public GildedRose(Item[] items) { + this.items = items; + } + + public void updateQuality() { + for (int i = 0; i < items.length; i++) { + if (!items[i].name.equals("Aged Brie") + && !items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].quality > 0) { + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].quality = items[i].quality - 1; + } + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + + if (items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].sellIn < 11) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + + if (items[i].sellIn < 6) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + } + } + } + + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].sellIn = items[i].sellIn - 1; + } + + if (items[i].sellIn < 0) { + if (!items[i].name.equals("Aged Brie")) { + if (!items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].quality > 0) { + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].quality = items[i].quality - 1; + } + } + } else { + items[i].quality = items[i].quality - items[i].quality; + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + } + } + } +} diff --git a/Java-Spock/src/main/java/com/gildedrose/Item.java b/Java-Spock/src/main/java/com/gildedrose/Item.java new file mode 100644 index 00000000..465729ec --- /dev/null +++ b/Java-Spock/src/main/java/com/gildedrose/Item.java @@ -0,0 +1,21 @@ +package com.gildedrose; + +public class Item { + + public String name; + + public int sellIn; + + public int quality; + + public Item(String name, int sellIn, int quality) { + this.name = name; + this.sellIn = sellIn; + this.quality = quality; + } + + @Override + public String toString() { + return this.name + ", " + this.sellIn + ", " + this.quality; + } +} diff --git a/Java-Spock/src/main/java/com/gildedrose/TexttestFixture.java b/Java-Spock/src/main/java/com/gildedrose/TexttestFixture.java new file mode 100644 index 00000000..d059c88f --- /dev/null +++ b/Java-Spock/src/main/java/com/gildedrose/TexttestFixture.java @@ -0,0 +1,37 @@ +package com.gildedrose; + +public class TexttestFixture { + public static void main(String[] args) { + System.out.println("OMGHAI!"); + + Item[] items = new Item[] { + new Item("+5 Dexterity Vest", 10, 20), // + new Item("Aged Brie", 2, 0), // + new Item("Elixir of the Mongoose", 5, 7), // + new Item("Sulfuras, Hand of Ragnaros", 0, 80), // + new Item("Sulfuras, Hand of Ragnaros", -1, 80), + new Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this conjured item does not work properly yet + new Item("Conjured Mana Cake", 3, 6) }; + + GildedRose app = new GildedRose(items); + + int days = 2; + if (args.length > 0) { + days = Integer.parseInt(args[0]) + 1; + } + + for (int i = 0; i < days; i++) { + System.out.println("-------- day " + i + " --------"); + System.out.println("name, sellIn, quality"); + for (Item item : items) { + System.out.println(item); + } + System.out.println(); + app.updateQuality(); + } + } + +} diff --git a/Java-Spock/src/test/groovy/com/gildedrose/GildedRoseSpec.groovy b/Java-Spock/src/test/groovy/com/gildedrose/GildedRoseSpec.groovy new file mode 100644 index 00000000..04276c03 --- /dev/null +++ b/Java-Spock/src/test/groovy/com/gildedrose/GildedRoseSpec.groovy @@ -0,0 +1,25 @@ +package com.gildedrose + +import spock.lang.Specification + +/** + * Spock unit tests. + */ +class GildedRoseSpec extends Specification { + + def "should update quality correctly"() { + + given: "some items" + Item[] items = [new Item("foo", 0, 0)]; + + and: "the application with these items" + GildedRose app = new GildedRose(items); + + when: "updating quality" + app.updateQuality(); + + then: "the quality is correct" + app.items[0].name == "fixme" + } + +} diff --git a/Java/.editorconfig b/Java/.editorconfig new file mode 100644 index 00000000..2f1b8410 --- /dev/null +++ b/Java/.editorconfig @@ -0,0 +1,6 @@ +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/Java/.gitignore b/Java/.gitignore new file mode 100644 index 00000000..31689f2e --- /dev/null +++ b/Java/.gitignore @@ -0,0 +1,13 @@ +.idea/ +*.iml +target/ + +.classpath +.project +bin/ +.settings/ + +# Gradle +.gradle +/build/ + diff --git a/Java/.mvn/wrapper/MavenWrapperDownloader.java b/Java/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..b901097f --- /dev/null +++ b/Java/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/Java/.mvn/wrapper/maven-wrapper.jar b/Java/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..c1dd12f1 Binary files /dev/null and b/Java/.mvn/wrapper/maven-wrapper.jar differ diff --git a/Java/.mvn/wrapper/maven-wrapper.properties b/Java/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..e83fa695 --- /dev/null +++ b/Java/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/Java/README.md b/Java/README.md new file mode 100644 index 00000000..081385cf --- /dev/null +++ b/Java/README.md @@ -0,0 +1,30 @@ +# Gilded Rose starting position in Java + +## Run the TextTest Fixture from Command-Line + +``` +./gradlew -q text +``` + +### Specify Number of Days + +For e.g. 10 days: + +``` +./gradlew -q text --args 10 +``` + +You should make sure the gradle commands shown above work when you execute them in a terminal before trying to use TextTest (see below). + + +## Run the TextTest approval test that comes with this project + +There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. What's unusual for the Java version is there are two executables listed in [config.gr](../texttests/config.gr) for Java. The first uses Gradle wrapped in a python script. Uncomment these lines to use it: + + executable:${TEXTTEST_HOME}/Java/texttest_rig.py + interpreter:python + +The other relies on your CLASSPATH being set correctly in [environment.gr](../texttests/environment.gr). Uncomment these lines to use it instead: + + executable:com.gildedrose.TexttestFixture + interpreter:java diff --git a/Java/build.gradle b/Java/build.gradle new file mode 100644 index 00000000..616cec6a --- /dev/null +++ b/Java/build.gradle @@ -0,0 +1,27 @@ +plugins { + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.6.2' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.6.2' +} + +group = 'com.gildedrose' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '1.8' + +test { + useJUnitPlatform() +} + +task texttest(type: JavaExec) { + main = "com.gildedrose.TexttestFixture" + classpath = sourceSets.test.runtimeClasspath + args "30" +} diff --git a/Java/gradle.properties b/Java/gradle.properties new file mode 100644 index 00000000..8e7aecc2 --- /dev/null +++ b/Java/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx1536M --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED diff --git a/Java/gradle/wrapper/gradle-wrapper.jar b/Java/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..cc4fdc29 Binary files /dev/null and b/Java/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Java/gradle/wrapper/gradle-wrapper.properties b/Java/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..622ab64a --- /dev/null +++ b/Java/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Java/gradlew b/Java/gradlew new file mode 100755 index 00000000..2fe81a7d --- /dev/null +++ b/Java/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## 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='"-Xmx64m" "-Xms64m"' + +# 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 or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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" + +exec "$JAVACMD" "$@" diff --git a/Java/gradlew.bat b/Java/gradlew.bat new file mode 100644 index 00000000..9618d8d9 --- /dev/null +++ b/Java/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@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="-Xmx64m" "-Xms64m" + +@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/Java/mvnw b/Java/mvnw new file mode 100755 index 00000000..5643201c --- /dev/null +++ b/Java/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + 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 + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/Java/mvnw.cmd b/Java/mvnw.cmd new file mode 100644 index 00000000..23b7079a --- /dev/null +++ b/Java/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/Java/pom.xml b/Java/pom.xml new file mode 100644 index 00000000..3bd6aff8 --- /dev/null +++ b/Java/pom.xml @@ -0,0 +1,46 @@ + + + + 4.0.0 + + com.gildedrose + gilded-rose-kata + 0.0.1-SNAPSHOT + + + 1.8 + 5.8.2 + 3.1 + 3.0.0-M4 + UTF-8 + + + + + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + test + + + + + + + maven-compiler-plugin + ${maven.maven-compiler-plugin.version} + + ${java.version} + ${java.version} + + + + maven-surefire-plugin + ${maven.maven-surefire-plugin.version} + + + + + diff --git a/Java/settings.gradle b/Java/settings.gradle new file mode 100644 index 00000000..7ce136a1 --- /dev/null +++ b/Java/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'gilded-rose-kata' diff --git a/Java/src/main/java/com/gildedrose/GildedRose.java b/Java/src/main/java/com/gildedrose/GildedRose.java new file mode 100644 index 00000000..87a3b926 --- /dev/null +++ b/Java/src/main/java/com/gildedrose/GildedRose.java @@ -0,0 +1,62 @@ +package com.gildedrose; + +class GildedRose { + Item[] items; + + public GildedRose(Item[] items) { + this.items = items; + } + + public void updateQuality() { + for (int i = 0; i < items.length; i++) { + if (!items[i].name.equals("Aged Brie") + && !items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].quality > 0) { + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].quality = items[i].quality - 1; + } + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + + if (items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].sellIn < 11) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + + if (items[i].sellIn < 6) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + } + } + } + + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].sellIn = items[i].sellIn - 1; + } + + if (items[i].sellIn < 0) { + if (!items[i].name.equals("Aged Brie")) { + if (!items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items[i].quality > 0) { + if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) { + items[i].quality = items[i].quality - 1; + } + } + } else { + items[i].quality = items[i].quality - items[i].quality; + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + } + } + } +} diff --git a/Java/src/main/java/com/gildedrose/Item.java b/Java/src/main/java/com/gildedrose/Item.java new file mode 100644 index 00000000..465729ec --- /dev/null +++ b/Java/src/main/java/com/gildedrose/Item.java @@ -0,0 +1,21 @@ +package com.gildedrose; + +public class Item { + + public String name; + + public int sellIn; + + public int quality; + + public Item(String name, int sellIn, int quality) { + this.name = name; + this.sellIn = sellIn; + this.quality = quality; + } + + @Override + public String toString() { + return this.name + ", " + this.sellIn + ", " + this.quality; + } +} diff --git a/Java/src/test/java/com/gildedrose/GildedRoseTest.java b/Java/src/test/java/com/gildedrose/GildedRoseTest.java new file mode 100644 index 00000000..8ae29eec --- /dev/null +++ b/Java/src/test/java/com/gildedrose/GildedRoseTest.java @@ -0,0 +1,17 @@ +package com.gildedrose; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class GildedRoseTest { + + @Test + void foo() { + Item[] items = new Item[] { new Item("foo", 0, 0) }; + GildedRose app = new GildedRose(items); + app.updateQuality(); + assertEquals("fixme", app.items[0].name); + } + +} diff --git a/Java/src/test/java/com/gildedrose/TexttestFixture.java b/Java/src/test/java/com/gildedrose/TexttestFixture.java new file mode 100644 index 00000000..d059c88f --- /dev/null +++ b/Java/src/test/java/com/gildedrose/TexttestFixture.java @@ -0,0 +1,37 @@ +package com.gildedrose; + +public class TexttestFixture { + public static void main(String[] args) { + System.out.println("OMGHAI!"); + + Item[] items = new Item[] { + new Item("+5 Dexterity Vest", 10, 20), // + new Item("Aged Brie", 2, 0), // + new Item("Elixir of the Mongoose", 5, 7), // + new Item("Sulfuras, Hand of Ragnaros", 0, 80), // + new Item("Sulfuras, Hand of Ragnaros", -1, 80), + new Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this conjured item does not work properly yet + new Item("Conjured Mana Cake", 3, 6) }; + + GildedRose app = new GildedRose(items); + + int days = 2; + if (args.length > 0) { + days = Integer.parseInt(args[0]) + 1; + } + + for (int i = 0; i < days; i++) { + System.out.println("-------- day " + i + " --------"); + System.out.println("name, sellIn, quality"); + for (Item item : items) { + System.out.println(item); + } + System.out.println(); + app.updateQuality(); + } + } + +} diff --git a/Java/texttest_rig.py b/Java/texttest_rig.py new file mode 100644 index 00000000..98f4760a --- /dev/null +++ b/Java/texttest_rig.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +""" +This script uses Gradle to execute the TexttestFixture. +It is designed to be used by TextTest and specified in the file 'texttests/config.gr' in this repo. +It is more convenient for TextTest to use since Gradle needs +several arguments in addition to the one the TextTest fixture needs. +""" +import os +import subprocess +import sys + +args = " ".join(sys.argv[1:]) +TEXTTEST_HOME = os.environ.get("TEXTTEST_HOME", os.getcwd()) +subprocess.run(f"{TEXTTEST_HOME}/Java/gradlew -p {TEXTTEST_HOME}/Java -q text --args {args}", shell=True) diff --git a/Kotlin/.gitignore b/Kotlin/.gitignore new file mode 100644 index 00000000..784e1ee7 --- /dev/null +++ b/Kotlin/.gitignore @@ -0,0 +1,25 @@ +.idea/ +out/ +*.class + +*.iml + +# Created by https://www.gitignore.io/api/gradle + +### Gradle ### +.gradle +**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# End of https://www.gitignore.io/api/gradle diff --git a/Kotlin/README.md b/Kotlin/README.md new file mode 100644 index 00000000..deaecf00 --- /dev/null +++ b/Kotlin/README.md @@ -0,0 +1,23 @@ +# Gilded Rose starting position in Kotlin + +## Run the Text Fixture from Command-Line + +``` +./gradlew -q text +``` + +### Specify Number of Days + +For e.g. 10 days: + +``` +./gradlew run --args 10 +``` + +You should make sure the gradle commands shown above work when you execute them in a terminal before trying to use TextTest (see below). + + +## Run the TextTest approval test that comes with this project + +There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. What's unusual for the Java version is there are two executables listed in [config.gr](../texttests/config.gr) for Java. One uses Gradle wrapped in a python script, the other relies on your CLASSPATH being set correctly in [environment.gr](../texttests/environment.gr). + diff --git a/Kotlin/build.gradle.kts b/Kotlin/build.gradle.kts new file mode 100644 index 00000000..4d392304 --- /dev/null +++ b/Kotlin/build.gradle.kts @@ -0,0 +1,39 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("jvm") version "1.9.10" + application +} + +group = "com.gildedrose" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + implementation(kotlin("stdlib")) + testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") +} + +tasks.test { + useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + } +} + +// config JVM target to 1.8 for kotlin compilation tasks +tasks.withType().configureEach { + kotlinOptions.jvmTarget = "1.8" +} + +// config java extension to same target version, to avoid build failure on Gradle 8.x +java { + targetCompatibility = JavaVersion.VERSION_1_8 +} + +application { + mainClass.set("com.gildedrose.TexttestFixtureKt") +} diff --git a/Kotlin/gradle/wrapper/gradle-wrapper.jar b/Kotlin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..7f93135c Binary files /dev/null and b/Kotlin/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Kotlin/gradle/wrapper/gradle-wrapper.properties b/Kotlin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..3fa8f862 --- /dev/null +++ b/Kotlin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Kotlin/gradlew b/Kotlin/gradlew new file mode 100755 index 00000000..1aa94a42 --- /dev/null +++ b/Kotlin/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# 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 ;; #( + MSYS* | 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 + if ! command -v java >/dev/null 2>&1 + then + 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 +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Kotlin/gradlew.bat b/Kotlin/gradlew.bat new file mode 100644 index 00000000..6689b85b --- /dev/null +++ b/Kotlin/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@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=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@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="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +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 execute + +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 + +: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 %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 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! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Kotlin/src/main/kotlin/com/gildedrose/GildedRose.kt b/Kotlin/src/main/kotlin/com/gildedrose/GildedRose.kt new file mode 100644 index 00000000..7ede7cee --- /dev/null +++ b/Kotlin/src/main/kotlin/com/gildedrose/GildedRose.kt @@ -0,0 +1,58 @@ +package com.gildedrose + +class GildedRose(var items: List) { + + fun updateQuality() { + for (i in items.indices) { + if (items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert") { + if (items[i].quality > 0) { + if (items[i].name != "Sulfuras, Hand of Ragnaros") { + items[i].quality = items[i].quality - 1 + } + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1 + + if (items[i].name == "Backstage passes to a TAFKAL80ETC concert") { + if (items[i].sellIn < 11) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1 + } + } + + if (items[i].sellIn < 6) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1 + } + } + } + } + } + + if (items[i].name != "Sulfuras, Hand of Ragnaros") { + items[i].sellIn = items[i].sellIn - 1 + } + + if (items[i].sellIn < 0) { + if (items[i].name != "Aged Brie") { + if (items[i].name != "Backstage passes to a TAFKAL80ETC concert") { + if (items[i].quality > 0) { + if (items[i].name != "Sulfuras, Hand of Ragnaros") { + items[i].quality = items[i].quality - 1 + } + } + } else { + items[i].quality = items[i].quality - items[i].quality + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1 + } + } + } + } + } + +} + diff --git a/Kotlin/src/main/kotlin/com/gildedrose/Item.kt b/Kotlin/src/main/kotlin/com/gildedrose/Item.kt new file mode 100644 index 00000000..eaf62a40 --- /dev/null +++ b/Kotlin/src/main/kotlin/com/gildedrose/Item.kt @@ -0,0 +1,7 @@ +package com.gildedrose + +open class Item(var name: String, var sellIn: Int, var quality: Int) { + override fun toString(): String { + return this.name + ", " + this.sellIn + ", " + this.quality + } +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/gildedrose/TexttestFixture.kt b/Kotlin/src/main/kotlin/com/gildedrose/TexttestFixture.kt new file mode 100644 index 00000000..72f27b9e --- /dev/null +++ b/Kotlin/src/main/kotlin/com/gildedrose/TexttestFixture.kt @@ -0,0 +1,36 @@ +package com.gildedrose + +fun main(args: Array) { + + println("OMGHAI!") + + val items = listOf( + Item("+5 Dexterity Vest", 10, 20), // + Item("Aged Brie", 2, 0), // + Item("Elixir of the Mongoose", 5, 7), // + Item("Sulfuras, Hand of Ragnaros", 0, 80), // + Item("Sulfuras, Hand of Ragnaros", -1, 80), + Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this conjured item does not work properly yet + Item("Conjured Mana Cake", 3, 6) + ) + + val app = GildedRose(items) + + var days = 2 + if (args.size > 0) { + days = Integer.parseInt(args[0]) + 1 + } + + for (i in 0..days - 1) { + println("-------- day $i --------") + println("name, sellIn, quality") + for (item in items) { + println(item) + } + println() + app.updateQuality() + } +} diff --git a/Kotlin/src/test/kotlin/com/gildedrose/GildedRoseTest.kt b/Kotlin/src/test/kotlin/com/gildedrose/GildedRoseTest.kt new file mode 100644 index 00000000..c5216d8a --- /dev/null +++ b/Kotlin/src/test/kotlin/com/gildedrose/GildedRoseTest.kt @@ -0,0 +1,19 @@ +package com.gildedrose + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +internal class GildedRoseTest { + + @Test + fun foo() { + val items = listOf(Item("foo", 0, 0)) + val app = GildedRose(items) + app.updateQuality() + assertEquals("fixme", app.items[0].name) + + } + +} + + diff --git a/Kotlin/texttest_rig.py b/Kotlin/texttest_rig.py new file mode 100644 index 00000000..b75e019d --- /dev/null +++ b/Kotlin/texttest_rig.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +""" +This script uses Gradle to execute the TexttestFixture. +It is designed to be used by TextTest and specified in the file 'texttests/config.gr' in this repo. +It is more convenient for TextTest to use since Gradle needs +several arguments in addition to the one the TextTest fixture needs. +""" +import os +import subprocess +import sys + +args = " ".join(sys.argv[1:]) +TEXTTEST_HOME = os.environ.get("TEXTTEST_HOME", os.getcwd()) +subprocess.run(f"{TEXTTEST_HOME}/Kotlin/gradlew -p {TEXTTEST_HOME}/Kotlin -q run --args {args}", shell=True) diff --git a/Matlab/.gitignore b/Matlab/.gitignore new file mode 100644 index 00000000..21229f76 --- /dev/null +++ b/Matlab/.gitignore @@ -0,0 +1 @@ +*.asv \ No newline at end of file diff --git a/Matlab/GildedRose.m b/Matlab/GildedRose.m new file mode 100644 index 00000000..2b607cbf --- /dev/null +++ b/Matlab/GildedRose.m @@ -0,0 +1,64 @@ +classdef GildedRose + + properties + items + end + + methods + function obj = GildedRose(items) + obj.items = items; + end + + function update_quality(obj) + for i = 1:length(obj.items) + item = obj.items(i); + if item.name ~= "Aged Brie" && item.name ~= "Backstage passes to a TAFKAL80ETC concert" + if item.quality > 0 + if item.name ~= "Sulfuras, Hand of Ragnaros" + item.quality = item.quality - 1; + end + end + else + if item.quality < 50 + item.quality = item.quality + 1; + if item.name == "Backstage passes to a TAFKAL80ETC concert" + if item.sell_in < 11 + if item.quality < 50 + item.quality = item.quality + 1; + end + end + if item.sell_in < 6 + if item.quality < 50 + item.quality = item.quality + 1; + end + end + end + end + end + if item.name ~= "Sulfuras, Hand of Ragnaros" + item.sell_in = item.sell_in - 1; + end + if item.sell_in < 0 + if item.name ~= "Aged Brie" + if item.name ~= "Backstage passes to a TAFKAL80ETC concert" + if item.quality > 0 + if item.name ~= "Sulfuras, Hand of Ragnaros" + item.quality = item.quality - 1; + end + end + else + item.quality = item.quality - item.quality; + end + else + if item.quality < 50 + item.quality = item.quality + 1; + end + end + end + end + end + end +end + + + diff --git a/Matlab/Item.m b/Matlab/Item.m new file mode 100644 index 00000000..917c2293 --- /dev/null +++ b/Matlab/Item.m @@ -0,0 +1,23 @@ +classdef Item < handle + % Do not edit or the goblin will one-shot you ! + + properties + name + sell_in + quality + end + + methods + function obj = Item(name, sell_in, quality) + obj.name = name; + obj.sell_in = sell_in; + obj.quality = quality; + end + + function disp(obj) + fprintf("%s, %d, %d\n", obj.name, obj.sell_in, obj.quality) + end + + end +end + diff --git a/Matlab/TestGildedRose.m b/Matlab/TestGildedRose.m new file mode 100644 index 00000000..2037ed91 --- /dev/null +++ b/Matlab/TestGildedRose.m @@ -0,0 +1,12 @@ +classdef TestGildedRose < matlab.unittest.TestCase + + methods(Test) + % Test methods + function test_standard_item(tc) + items = [Item("foo", 4, 5)]; + gilded_rose = GildedRose(items); + gilded_rose.update_quality(); + tc.verifyEqual(items(1).name, "Fixme") + end + end +end \ No newline at end of file diff --git a/Matlab/TextTest.m b/Matlab/TextTest.m new file mode 100644 index 00000000..cab6ea7d --- /dev/null +++ b/Matlab/TextTest.m @@ -0,0 +1,31 @@ +function TextTest(nDays) + +if nargin < 1 + nDays = 2; +end + +items = [ + Item("+5 Dexterity Vest", 10, 20); + Item("Aged Brie", 2, 0); + Item("Elixir of the Mongoose", 5, 7); + Item("Sulfuras, Hand of Ragnaros", 0, 80); + Item("Sulfuras, Hand of Ragnaros", -1, 80); + Item("Backstage passes to a TAFKAL80ETC concert", 15, 20); + Item("Backstage passes to a TAFKAL80ETC concert", 10, 49); + Item("Backstage passes to a TAFKAL80ETC concert", 5, 49); + Item("Conjured Mana Cake", 3, 6)]; + + +for day = 1:nDays + fprintf("-------- day %d --------\n", day) + disp("name, sellIn, quality") + for i = 1:length(items) + disp(items(i)) + end + disp(" ") + GildedRose(items).update_quality() +end + +end + + diff --git a/R/.project b/R/.project new file mode 100644 index 00000000..62d08220 --- /dev/null +++ b/R/.project @@ -0,0 +1,18 @@ + + + GildedRose.R + + + + + + de.walware.statet.r.builders.RSupport + + + + + + de.walware.statet.base.StatetNature + de.walware.statet.r.RNature + + diff --git a/R/gilded_rose.R b/R/gilded_rose.R new file mode 100644 index 00000000..3d639dda --- /dev/null +++ b/R/gilded_rose.R @@ -0,0 +1,56 @@ +source('item.R') + +update_quality <- function(items) { + lapply(items, + function(item) { + + if (item$name != "Aged Brie" && item$name != "Backstage passes to a TAFKAL80ETC concert") { + if (item$quality > 0) { + if (item$name != "Sulfuras, Hand of Ragnaros") { + item$quality <- item$quality - 1 + } + } + } else { + if (item$quality < 50) { + item$quality <- item$quality + 1 + if (item$name == "Backstage passes to a TAFKAL80ETC concert") { + if (item$sell_in < 11) { + if (item$quality < 50) { + item$quality = item$quality + 1 + } + } + if (item$sell_in < 6) { + if (item$quality < 50) { + item$quality = item$quality + 1 + } + } + } + } + } + + if (item$name != "Sulfuras, Hand of Ragnaros") { + item$sell_in <- item$sell_in - 1 + } + + if (item$sell_in < 0) { + if (item$name != "Aged Brie") { + if (item$name != "Backstage passes to a TAFKAL80ETC concert") { + if (item$quality > 0) { + if (item$name != "Sulfuras, Hand of Ragnaros") { + item$quality <- item$quality - 1 + } + } + } else { + item$quality <- item$quality - item$quality + } + } else { + if (item$quality < 50) { + item$quality <- item$quality + 1 + } + } + } + + item + } + ) +} diff --git a/R/item.R b/R/item.R new file mode 100644 index 00000000..51cc90dc --- /dev/null +++ b/R/item.R @@ -0,0 +1,13 @@ +item <- function(name, sell_in, quality) { + newItem <- list(name=name, sell_in=sell_in, quality=quality) + class(newItem) <- 'item' + newItem +} + +as.character.item <- function(item) { + paste(item$name, ", ", item$sell_in, ", ", item$quality, sep='') +} + +print.item <- function(item) { + print.default(as.character(item)) +} diff --git a/R/runit_gilded_rose.R b/R/runit_gilded_rose.R new file mode 100644 index 00000000..a0004b7c --- /dev/null +++ b/R/runit_gilded_rose.R @@ -0,0 +1,7 @@ +source('gilded_rose.R') + +test.foo <- function() { + items <- list( item('foo', 0, 0) ) + items <- update_quality(items) + checkEquals('fixme', items[[1]]$name); +} diff --git a/R/test_setup.R b/R/test_setup.R new file mode 100644 index 00000000..fc62610e --- /dev/null +++ b/R/test_setup.R @@ -0,0 +1,7 @@ +# A little helper script to get the testing infrastructure started + +# install.packages("RUnit") +require(RUnit) + +# execute single test file +runTestFile("runit_gilded_rose.R") diff --git a/R/texttest_fixture.R b/R/texttest_fixture.R new file mode 100644 index 00000000..ec635e6e --- /dev/null +++ b/R/texttest_fixture.R @@ -0,0 +1,33 @@ +rm(list=ls()) + +source('gilded_rose.R') + +writeLines('OMGHAI!') + +items <- list( + item('+5 Dexterity Vest', 10, 20), + item('Aged Brie', 2, 0), + item('Elixir of the Mongoose', 5, 7), + item('Sulfuras, Hand of Ragnaros', 0, 80), + item('Sulfuras, Hand of Ragnaros', -1, 80), + item('Backstage passes to a TAFKAL80ETC concert', 15, 20), + item('Backstage passes to a TAFKAL80ETC concert', 10, 49), + item('Backstage passes to a TAFKAL80ETC concert', 5, 49), + # This Conjured item does not work properly yet + item('Conjured Mana Cake', 3, 6) +) + +days <- 2 +for (day in 0:days) { + writeLines(paste('-------- day ', day, ' --------', sep='')) + writeLines('name, sellIn, quality') + lapply(items, + function(item) { + writeLines(as.character(item)) + } + ) + writeLines('') + items <- update_quality(items) +} + +rm('day', 'days', 'items') diff --git a/README.md b/README.md index 7504b97c..2784ce9b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +<<<<<<< HEAD # Gilded Rose starting position in Python For exercise instructions see [top level README](../README.md) @@ -27,3 +28,58 @@ There are instructions in the [TextTest Readme](../texttests/README.md) for sett executable:${TEXTTEST_HOME}/python/texttest_fixture.py interpreter:python +======= +_Support this and all my katas via [Patreon](https://www.patreon.com/EmilyBache)_ + +# Gilded Rose Refactoring Kata + +You can find out more about this exercise in my YouTube video [Why Developers LOVE The Gilded Rose Kata](https://youtu.be/Mt4XpGxigT4). I also have a video of a worked solution in Java - [Gilded Rose Kata, Hands-on](https://youtu.be/OdnV8hc9L7I) + +I use this kata as part of my work as a technical coach. I wrote a lot about the coaching method I use in this book [Technical Agile Coaching with the Samman method](https://leanpub.com/techagilecoach). A while back I wrote this article ["Writing Good Tests for the Gilded Rose Kata"](http://coding-is-like-cooking.info/2013/03/writing-good-tests-for-the-gilded-rose-kata/) about how you could use this kata in a [coding dojo](https://leanpub.com/codingdojohandbook). + + +## How to use this Kata + +The simplest way is to just clone the code and start hacking away improving the design. You'll want to look at the ["Gilded Rose Requirements"](https://github.com/emilybache/GildedRose-Refactoring-Kata/blob/main/GildedRoseRequirements.md) which explains what the code is for. I strongly advise you that you'll also need some tests if you want to make sure you don't break the code while you refactor. + +You could write some unit tests yourself, using the requirements to identify suitable test cases. I've provided a failing unit test in a popular test framework as a starting point for most languages. + +Alternatively, use the Approval tests provided in this repository. (Read more about that in the section "Text-based Approval Testing"). + +The idea of the exercise is to do some deliberate practice, and improve your skills at designing test cases and refactoring. The idea is not to re-write the code from scratch, but rather to practice taking small steps, running the tests often, and incrementally improving the design. + +### Gilded Rose Requirements in other languages + +- [English](GildedRoseRequirements.md) +- [Español](GildedRoseRequirements_es.md) +- [Français](GildedRoseRequirements_fr.md) +- [Italiano](GildedRoseRequirements_it.md) +- [日本語](GildedRoseRequirements_jp.md) +- [Português](GildedRoseRequirements_pt-BR.md) +- [Русский](GildedRoseRequirements_ru.txt) +- [ไทย](GildedRoseRequirements_th.md) +- [中文](GildedRoseRequirements_zh.txt) +- [한국어](GildedRoseRequirements_kr.md) +- [German](GildedRoseRequirements_de.md) +- [Euskara](GildedRoseRequirements_eu.md) + +## Text-Based Approval Testing + +Most language versions of this code have a [TextTest](https://texttest.org) fixture for Approval testing. For information about this, see the [TextTests README](https://github.com/emilybache/GildedRose-Refactoring-Kata/tree/main/texttests) + +## History of the exercise + +This Kata was originally created by Terry Hughes (http://twitter.com/TerryHughes). It is already on GitHub [here](https://github.com/NotMyself/GildedRose). See also [Bobby Johnson's description of the kata](https://iamnotmyself.com/refactor-this-the-gilded-rose-kata/). + +I translated the original C# into a few other languages, (with a little help from my friends!), and slightly changed the starting position. This means I've actually done a small amount of refactoring already compared with the original form of the kata, and made it easier to get going with writing tests by giving you one failing unit test to start with. I also added test fixtures for Text-Based approval testing with TextTest (see [the TextTests](https://github.com/emilybache/GildedRose-Refactoring-Kata/tree/main/texttests)) + +As Bobby Johnson points out in his article ["Why Most Solutions to Gilded Rose Miss The Bigger Picture"](https://iamnotmyself.com/why-most-solutions-to-gilded-rose-miss-the-bigger-picture/), it'll actually give you +better practice at handling a legacy code situation if you do this Kata in the original C#. However, I think this kata +is also really useful for practicing writing good tests using different frameworks and approaches, and the small changes I've made help with that. I think it's also interesting to compare what the refactored code and tests look like in different programming languages. + +## Contributing + +Contributions are encouraged! You could add a translations of the specification +in another language or a new starting point for your favorite programming +language. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more details. +>>>>>>> e2abba77cb5a395702f237e428b639f2129b1f07 diff --git a/Smalltalk/GildedRose.st b/Smalltalk/GildedRose.st new file mode 100644 index 00000000..863e99e2 --- /dev/null +++ b/Smalltalk/GildedRose.st @@ -0,0 +1,160 @@ +Object subclass: #GildedRose + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'GildedRose'! +!GildedRose commentStamp: 'AndreasLeidig 4/21/2012 15:38' prior: 0! +This Kata was originally created by Terry Hughes (http://twitter.com/#!!/TerryHughes). It is already on GitHub as "GildedRose", a sample project for C#. I could have forked it again, but I thought other language users might not want to download a whole C# project environment. In this repository are starting code samples for Java, Python, Ruby, C# and C++. + +See also http://iamnotmyself.com/2011/02/13/refactor-this-the-gilded-rose-kata/ + +==================== +How to use this Kata +==================== + +The simplest way is to just clone the code and start hacking away improving the design. You'll want to look at the "Gilded Rose Background Reading" (below) which explains what the code is for. I strongly advise you that you'll also need some tests if you want to make sure you don't break the code while you refactor. + +You could write some unit tests yourself, using the Kata Background Reading (below) to identify suitable test cases. I've provided a failing unit test in a popular test framework as a starting point for most languages. + +Alternatively, use the "Text-Based" tests provided in this repository. (Read more about that in the next section) + +Whichever testing approach you choose, the idea of the exercise is to do some deliberate practice, and improve your Refactoring skills. The idea is not to re-write the code from scratch, but rather to practice taking small steps, running the tests often, and incrementally improving the design. + +================== +Text-Based Testing +================== + +This is a testing approach which is very useful when refactoring legacy code. The basic idea is to create tests that use the text which the code produces. Before you change the code, you run it, and save the output as a "Golden Copy". Then after you change the code, you run it again, and compare the output against the Golden Copy. Any differences, and the test fails. + +It's basically the same idea as "assertEquals(expected, actual)" in a unit test, except the text you are comparing is typically much longer, and the "expected" value is saved from actual output, rather than being defined in advance. + +Typically a piece of legacy code may not produce suitable textual output from the start, so you may need to modify it before you can write your first text-based test. That could involve inserting log statements into the code, or just writing a "main" method that executes the code and prints out what the result is afterwards. It's this latter approach we are using here to test GildedRose. + +The Text-Based tests in this repository are designed to be used with the tool "TextTest" (http://texttest.org). This tool helps you to organize and run text-based tests. There is more information in the README file in the "texttests" subdirectory. + +=================================== +Gilded Rose Kata Background Reading +=================================== + +Hi and welcome to team Gilded Rose. As you know, we are a small inn with a prime location in a prominent city ran by a friendly innkeeper named Allison. We also buy and sell only the finest goods. Unfortunately, our goods are constantly degrading in quality as they approach their sell by date. We have a system in place that updates our inventory for us. It was developed by a no-nonsense type named Leeroy, who has moved on to new adventures. Your task is to add the new feature to our system so that we can begin selling a new category of items. First an introduction to our system: + + - All items have a SellIn value which denotes the number of days we have to sell the item + - All items have a Quality value which denotes how valuable the item is + - At the end of each day our system lowers both values for every item + +Pretty simple, right? Well this is where it gets interesting: + + - Once the sell by date has passed, Quality degrades twice as fast + - The Quality of an item is never negative + - "Aged Brie" actually increases in Quality the older it gets + - The Quality of an item is never more than 50 + - "Sulfuras", being a legendary item, never has to be sold or decreases in Quality + - "Backstage passes", like aged brie, increases in Quality as it's SellIn value approaches; Quality increases by 2 when there are 10 days or less and by 3 when there are 5 days or less but Quality drops to 0 after the concert + +We have recently signed a supplier of conjured items. This requires an update to our system: + + - "Conjured" items degrade in Quality twice as fast as normal items + +Feel free to make any changes to the UpdateQuality method and add any new code as long as everything still works correctly. However, do not alter the Item class or Items property as those belong to the goblin in the corner who will insta-rage and one-shot you as he doesn't believe in shared code ownership (you can make the UpdateQuality method and Items property static if you like, we'll cover for you). + +Just for clarification, an item can never have its Quality increase above 50, however "Sulfuras" is a legendary item and as such its Quality is 80 and it never alters.! + + +!GildedRose methodsFor: 'API' stamp: 'AndreasLeidig 4/21/2012 17:02'! +updateQualityFor: items + items + do: [:item | + (item name ~= 'Aged Brie' + and: [item name ~= 'Backstage passes to a TAFKAL80ETC concert']) + ifTrue: [item quality > 0 + ifTrue: [item name ~= 'Sulfuras, Hand of Ragnaros' + ifTrue: [item quality: item quality - 1]]] + ifFalse: [item quality < 50 + ifTrue: [item quality: item quality + 1. + item name = 'Backstage passes to a TAFKAL80ETC concert' + ifTrue: [item sellIn < 11 + ifTrue: [item quality < 50 + ifTrue: [item quality: item quality + 1]]. + item sellIn < 6 + ifTrue: [item quality < 50 + ifTrue: [item quality: item quality + 1]]]]]. + item name ~= 'Sulfuras, Hand of Ragnaros' + ifTrue: [item sellIn: item sellIn - 1]. + item sellIn < 0 + ifTrue: [item name ~= 'Aged Brie' + ifTrue: [item name ~= 'Backstage passes to a TAFKAL80ETC concert' + ifTrue: [item quality > 0 + ifTrue: [item name ~= 'Sulfuras, Hand of Ragnaros' + ifTrue: [item quality: item quality - 1]]] + ifFalse: [item quality: item quality - item quality]] + ifFalse: [item quality < 50 + ifTrue: [item quality: item quality + 1]]]]! ! + +"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! + +GildedRose class + instanceVariableNames: ''! + +!GildedRose class methodsFor: 'run Example' stamp: 'AndreasLeidig 4/21/2012 20:26'! +runExamples + "GildedRose runExamples" + | items gildedRose | + items := OrderedCollection new + add: (Item new name: '+5 Dexterity Vest'; sellIn: 10; quality: 20; yourself); + add: (Item new name: 'Aged Brie'; sellIn: 2; quality: 0; yourself); + add: (Item new name: 'Elixir of the Mongoose'; sellIn: 5; quality: 7; yourself); + add: (Item new name: 'Sulfuras, Hand of Ragnaros'; sellIn: 0; quality: 80; yourself); + add: (Item new name: 'Sulfuras, Hand of Ragnaros'; sellIn: -1; quality: 80; yourself); + add: (Item new name: 'Backstage passes to a TAFKAL80ETC concert'; sellIn: 15; quality: 20; yourself); + add: (Item new name: 'Backstage passes to a TAFKAL80ETC concert'; sellIn: 10; quality: 49; yourself); + add: (Item new name: 'Backstage passes to a TAFKAL80ETC concert'; sellIn: 5; quality: 49; yourself); + add: (Item new name: 'Conjured Mana Cake'; sellIn: 3; quality: 6; yourself); "this conjured item does not work properly yet" + yourself. + + gildedRose := GildedRose new. + Transcript show: 'OMGHAI!!'; + cr. + 0 to: 30 do: [:idx | + Transcript show: '-------- day ' , idx printString , ' --------'; + cr; + show: 'name, sellIn, quality'; + cr. + items + do: [:item | + Transcript show: item name , ', ' , item sellIn printString , ', ' , item quality printString; + cr]. + Transcript cr. + gildedRose updateQualityFor: items]. + Transcript cr! ! + + +Object subclass: #Item + instanceVariableNames: 'name sellIn quality' + classVariableNames: '' + poolDictionaries: '' + category: 'GildedRose'! + +!Item methodsFor: 'accessing' stamp: 'AndreasLeidig 4/21/2012 15:40'! +name + ^name! ! + +!Item methodsFor: 'accessing' stamp: 'AndreasLeidig 4/21/2012 15:41'! +name: aString + name := aString! ! + +!Item methodsFor: 'accessing' stamp: 'AndreasLeidig 4/21/2012 15:41'! +quality + ^quality! ! + +!Item methodsFor: 'accessing' stamp: 'AndreasLeidig 4/21/2012 15:42'! +quality: aNumber + quality := aNumber! ! + +!Item methodsFor: 'accessing' stamp: 'AndreasLeidig 4/21/2012 15:41'! +sellIn + ^sellIn! ! + +!Item methodsFor: 'accessing' stamp: 'AndreasLeidig 4/21/2012 15:42'! +sellIn: aNumber + sellIn := aNumber + ! ! diff --git a/TypeScript/.editorconfig b/TypeScript/.editorconfig new file mode 100644 index 00000000..4a7ea303 --- /dev/null +++ b/TypeScript/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/TypeScript/.gitignore b/TypeScript/.gitignore new file mode 100644 index 00000000..566b078b --- /dev/null +++ b/TypeScript/.gitignore @@ -0,0 +1,11 @@ +.DS_Store +.idea +node_modules +typings +app/**/*.js +app/**/*.js.map +test/**/*.js +test/**/*.js.map +coverage +.nyc_output +package-lock.json diff --git a/TypeScript/.mocharc.js b/TypeScript/.mocharc.js new file mode 100644 index 00000000..9ae39f77 --- /dev/null +++ b/TypeScript/.mocharc.js @@ -0,0 +1,11 @@ +"use strict"; + +module.exports = { + require: [ + "ts-node/register", + "tsconfig-paths/register", + "source-map-support/register" + ], + recursive: true, + spec: "test/mocha/*.spec.ts" +} diff --git a/TypeScript/.nycrc.js b/TypeScript/.nycrc.js new file mode 100644 index 00000000..3b19d56e --- /dev/null +++ b/TypeScript/.nycrc.js @@ -0,0 +1,17 @@ +module.exports = { + extension: [ + ".ts" + ], + exclude: [ + "**/*.d.ts", + "test/**", + "/*.js" + ], + require: [ + "ts-node/register" + ], + reporter: [ + "html", + "text" + ] +} diff --git a/TypeScript/README.md b/TypeScript/README.md new file mode 100644 index 00000000..ab907807 --- /dev/null +++ b/TypeScript/README.md @@ -0,0 +1,57 @@ +# Gilded Rose + +This is the Gilded Rose kata in TypeScript. + +## Getting started + +Install dependencies + +```sh +npm install +``` + +## Run the unit tests from the Command-Line + +There are two unit test frameworks to choose from, Jest and Mocha. + +```sh +npm run test:jest +``` + +To run all tests in watch mode + +```sh +npm run test:jest:watch +``` + +Mocha + +```sh +npm run test:mocha +``` + + +## Run the TextTest fixture from the Command-Line + +_You may need to install `ts-node`_ + +```sh +npx ts-node test/golden-master-text-test.ts +``` + +Or with number of days as args: +```sh +npx ts-node test/golden-master-text-test.ts 10 +``` + +You should make sure the command shown above works when you execute it in a terminal before trying to use TextTest (see below). + + +## Run the TextTest approval test that comes with this project + +There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. You will need to specify the Python executable and interpreter in [config.gr](../texttests/config.gr). Uncomment these lines: + + executable:${TEXTTEST_HOME}/python/texttest_fixture.py + interpreter:python + + diff --git a/TypeScript/app/gilded-rose.ts b/TypeScript/app/gilded-rose.ts new file mode 100644 index 00000000..db58d678 --- /dev/null +++ b/TypeScript/app/gilded-rose.ts @@ -0,0 +1,69 @@ +export class Item { + name: string; + sellIn: number; + quality: number; + + constructor(name, sellIn, quality) { + this.name = name; + this.sellIn = sellIn; + this.quality = quality; + } +} + +export class GildedRose { + items: Array; + + constructor(items = [] as Array) { + this.items = items; + } + + updateQuality() { + for (let i = 0; i < this.items.length; i++) { + if (this.items[i].name != 'Aged Brie' && this.items[i].name != 'Backstage passes to a TAFKAL80ETC concert') { + if (this.items[i].quality > 0) { + if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') { + this.items[i].quality = this.items[i].quality - 1 + } + } + } else { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1 + if (this.items[i].name == 'Backstage passes to a TAFKAL80ETC concert') { + if (this.items[i].sellIn < 11) { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1 + } + } + if (this.items[i].sellIn < 6) { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1 + } + } + } + } + } + if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') { + this.items[i].sellIn = this.items[i].sellIn - 1; + } + if (this.items[i].sellIn < 0) { + if (this.items[i].name != 'Aged Brie') { + if (this.items[i].name != 'Backstage passes to a TAFKAL80ETC concert') { + if (this.items[i].quality > 0) { + if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') { + this.items[i].quality = this.items[i].quality - 1 + } + } + } else { + this.items[i].quality = this.items[i].quality - this.items[i].quality + } + } else { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1 + } + } + } + } + + return this.items; + } +} diff --git a/TypeScript/jest.config.ts b/TypeScript/jest.config.ts new file mode 100644 index 00000000..caffa5b4 --- /dev/null +++ b/TypeScript/jest.config.ts @@ -0,0 +1,13 @@ +import { pathsToModuleNameMapper } from "ts-jest"; +import { compilerOptions } from './tsconfig.json' + +export default { + roots: ['/app', '/test/jest'], + collectCoverage: true, + coverageDirectory: 'coverage', + coverageProvider: 'v8', + transform: { + '^.+\\.tsx?$': 'ts-jest', + }, + moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '/' } ), +}; diff --git a/TypeScript/package.json b/TypeScript/package.json new file mode 100644 index 00000000..c7c597e3 --- /dev/null +++ b/TypeScript/package.json @@ -0,0 +1,35 @@ +{ + "name": "gilded-rose-kata", + "version": "1.0.0", + "description": "Gilded Rose kata in TypeScript", + "scripts": { + "precompile": "rimraf app/**/*.js test/**/*.js", + "compile": "tsc", + "pretest": "rimraf app/**/*.js test/**/*.js", + "test:jest": "jest", + "test:jest:watch": "jest --watchAll", + "test:mocha": "nyc mocha", + "test:vitest": "vitest --coverage" + }, + "license": "MIT", + "private": true, + "devDependencies": { + "@types/chai": "^4.2.22", + "@types/jest": "^29.4.0", + "@types/mocha": "^10.0.1", + "@types/node": "^18.14.0", + "@vitest/coverage-istanbul": "^0.28.5", + "chai": "^4.3.4", + "jest": "^29.4.3", + "mocha": "^10.2.0", + "nyc": "~15.1.0", + "rimraf": "^4.1.2", + "source-map-support": "^0.5.20", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.1.2", + "typescript": "^4.4.4", + "vite-tsconfig-paths": "^4.0.5", + "vitest": "^0.28.5" + } +} diff --git a/TypeScript/test/golden-master-text-test.ts b/TypeScript/test/golden-master-text-test.ts new file mode 100644 index 00000000..378f78d5 --- /dev/null +++ b/TypeScript/test/golden-master-text-test.ts @@ -0,0 +1,34 @@ +import { Item, GildedRose } from '../app/gilded-rose'; + +console.log("OMGHAI!") + +const items = [ + new Item("+5 Dexterity Vest", 10, 20), // + new Item("Aged Brie", 2, 0), // + new Item("Elixir of the Mongoose", 5, 7), // + new Item("Sulfuras, Hand of Ragnaros", 0, 80), // + new Item("Sulfuras, Hand of Ragnaros", -1, 80), + new Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this conjured item does not work properly yet + new Item("Conjured Mana Cake", 3, 6)]; + + +const gildedRose = new GildedRose(items); + +let days: number = 2; +if (process.argv.length > 2) { + days = +process.argv[2]; + } + +for (let i = 0; i < days + 1; i++) { + console.log("-------- day " + i + " --------"); + console.log("name, sellIn, quality"); + items.forEach(element => { + console.log(element.name + ', ' + element.sellIn + ', ' + element.quality); + + }); + console.log(); + gildedRose.updateQuality(); +} diff --git a/TypeScript/test/jest/approvals.spec.ts b/TypeScript/test/jest/approvals.spec.ts new file mode 100644 index 00000000..bb7f45f9 --- /dev/null +++ b/TypeScript/test/jest/approvals.spec.ts @@ -0,0 +1,54 @@ +import { Item, GildedRose } from '@/gilded-rose'; + +/** + * This unit test uses [Jest Snapshot](https://goo.gl/fbAQLP). + * + * There are two test cases here with different styles: + *
  • "foo" is more similar to the unit test from the 'Java' version + *
  • "thirtyDays" is more similar to the TextTest from the 'Java' version + * + * I suggest choosing one style to develop and deleting the other. + */ + +describe('Gilded Rose Approval', () => { + + let gameConsoleOutput: string; + let originalConsoleLog: (message: any) => void; + let originalProcessArgv: string[] + + function gameConsoleLog(msg: string) { + if (msg) { + gameConsoleOutput += msg; + } + gameConsoleOutput += "\n"; + } + + beforeEach(() => { + // prepare capturing console.log to our own gameConsoleLog. + gameConsoleOutput = ""; + originalConsoleLog = console.log; + console.log = gameConsoleLog; + originalProcessArgv = process.argv; + }); + + afterEach(() => { + // reset original console.log + console.log = originalConsoleLog; + process.argv = originalProcessArgv; + }); + + it('should foo', () => { + const gildedRose = new GildedRose([new Item('foo', 0, 0)]); + const items = gildedRose.updateQuality(); + + expect(items).toMatchSnapshot(); + }); + + it('should thirtyDays', () => { + process.argv = ["", " { + it('should foo', () => { + const gildedRose = new GildedRose([new Item('foo', 0, 0)]); + const items = gildedRose.updateQuality(); + expect(items[0].name).toBe('fixme'); + }); +}); diff --git a/TypeScript/test/mocha/gilded-rose.spec.ts b/TypeScript/test/mocha/gilded-rose.spec.ts new file mode 100644 index 00000000..02cd24c5 --- /dev/null +++ b/TypeScript/test/mocha/gilded-rose.spec.ts @@ -0,0 +1,10 @@ +import { expect } from 'chai'; +import { Item, GildedRose } from '@/gilded-rose'; + +describe('Gilded Rose', () => { + it('should foo', () => { + const gildedRose = new GildedRose([new Item('foo', 0, 0)]); + const items = gildedRose.updateQuality(); + expect(items[0].name).to.equal('fixme'); + }); +}); diff --git a/TypeScript/test/vitest/gilded-rose.spec.ts b/TypeScript/test/vitest/gilded-rose.spec.ts new file mode 100644 index 00000000..65330750 --- /dev/null +++ b/TypeScript/test/vitest/gilded-rose.spec.ts @@ -0,0 +1,9 @@ +import { Item, GildedRose } from '@/gilded-rose'; + +describe('Gilded Rose', () => { + it('should foo', () => { + const gildedRose = new GildedRose([new Item('foo', 0, 0)]); + const items = gildedRose.updateQuality(); + expect(items[0].name).toBe('fixme'); + }); +}); diff --git a/TypeScript/texttest_rig.py b/TypeScript/texttest_rig.py new file mode 100644 index 00000000..0cd59ad1 --- /dev/null +++ b/TypeScript/texttest_rig.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +""" +This script uses npx to execute the TextTest Fixture for TypeScript. +It is designed to be used by TextTest and specified in the file 'texttests/config.gr' in this repo. +It is more convenient for TextTest to use since npx needs +several arguments in addition to the one the TextTest fixture needs. +""" +import os +import subprocess +import sys + +args = " ".join(sys.argv[1:]) +TEXTTEST_HOME = os.environ.get("TEXTTEST_HOME", os.getcwd()) +subprocess.run(f"npx ts-node {TEXTTEST_HOME}/TypeScript/test/golden-master-text-test.ts {args}", shell=True) diff --git a/TypeScript/tsconfig.json b/TypeScript/tsconfig.json new file mode 100644 index 00000000..d2922795 --- /dev/null +++ b/TypeScript/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "strict": true, + "noImplicitAny": false, + "sourceMap": true, + "baseUrl": "./", + "resolveJsonModule": true, + "esModuleInterop": true, + "paths": { + "@/*": [ + "app/*" + ] + } + }, + "exclude": [ + "node_modules" + ] +} diff --git a/TypeScript/vitest.config.ts b/TypeScript/vitest.config.ts new file mode 100644 index 00000000..73488c68 --- /dev/null +++ b/TypeScript/vitest.config.ts @@ -0,0 +1,14 @@ +import {defineConfig} from 'vitest/config'; +import tsconfigPaths from 'vite-tsconfig-paths'; + +export default defineConfig({ + plugins: [tsconfigPaths()], + test: { + globals: true, + include: ['test/vitest/**/*.{spec,test}.{js,ts}'], + coverage: { + provider: 'istanbul', + reporter: ['text', 'html'] + } + }, +}); diff --git a/abap/README.md b/abap/README.md new file mode 100644 index 00000000..ebd18544 --- /dev/null +++ b/abap/README.md @@ -0,0 +1,16 @@ +# Gilded Rose Refactoring Kata in [ABAP](http://scn.sap.com/community/abap/) + +## Prerequisite + +Access to SAP NetWeaver server with at least ABAP 7.40 + +## Installation + +Assuming you have a proper developer key set up, run SE38 +* create a new Module Pool (type M) program as a Local Object +* paste the raw code from [YY_PAO_GILDED_ROSE.abap](https://raw.githubusercontent.com/brehberg/GildedRose-Refactoring-Kata/master/abap/YY_PAO_GILDED_ROSE.abap) +* save (Ctrl-S) and activate (Ctrl-F3) the program + +## Running Tests + +From the menus choose Program -> Execute -> Unit Tests (Ctrl+Shift+F10) diff --git a/abap/YY_PAO_GILDED_ROSE.abap b/abap/YY_PAO_GILDED_ROSE.abap new file mode 100644 index 00000000..40e8f985 --- /dev/null +++ b/abap/YY_PAO_GILDED_ROSE.abap @@ -0,0 +1,242 @@ +*&---------------------------------------------------------------------* +*& Gilded Rose Requirements Specification +*&---------------------------------------------------------------------* +*& +*& Hi and welcome to team Gilded Rose. As you know, we are a small inn with +*& a prime location in a prominent city ran by a friendly innkeeper named +*& Allison. We also buy and sell only the finest goods. Unfortunately, our +*& goods are constantly degrading in quality as they approach their sell by +*& date. We have a system in place that updates our inventory for us. It +*& was developed by a no-nonsense type named Leeroy, who has moved on to +*& new adventures. Your task is to add the new feature to our system so that +*& we can begin selling a new category of items. +*& +*& First an introduction to our system: +*& +*& - All items have a Sell In value which denotes the number of +*& days we have to sell the item +*& - All items have a Quality value which denotes how valuable the item is +*& - At the end of each day our system lowers both values for every item +*& +*& Seems pretty simple, right? Well this is where it gets interesting: +*& +*& - Once the sell by date has passed, Quality degrades twice as fast +*& - The Quality of an item is never negative +*& - "Aged Brie" actually increases in Quality the older it gets +*& - The Quality of an item is never more than 50 +*& - "Sulfuras", being a legendary item, never has to be sold or +*& decreases in Quality +*& - "Backstage passes", like aged brie, increases in Quality as its +*& Sell In value approaches; Quality increases by 2 when there +*& are 10 days or less and by 3 when there are 5 days or less +*& but Quality drops to 0 after the concert +*& +*& We have recently signed a supplier of conjured items. This requires an +*& update to our system: +*& +*& - "Conjured" items degrade in Quality twice as fast as normal items +*& +*& Feel free to make any changes to the Update Quality method and add any new +*& code as long as everything still works correctly. However, do not alter +*& the Item class directly or Items table attribute as those belong to the +*& goblin in the corner who will insta-rage and one-shot you as he doesn't +*& believe in shared code ownership (you can make the Update Quality method +*& and Items property static if you must, we'll cover for you). +*& +*& Just for clarification, an item can never have its Quality increase +*& above 50, however "Sulfuras" is a legendary item and as such its Quality +*& is 80 and it never alters. + +PROGRAM yy_pao_gilded_rose. + + +*& Production Code - Class Library +CLASS lcl_item DEFINITION DEFERRED. + +CLASS lcl_gilded_rose DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + tt_items TYPE STANDARD TABLE OF REF TO lcl_item WITH EMPTY KEY. + METHODS: + constructor + IMPORTING it_items TYPE tt_items, + update_quality. + + PRIVATE SECTION. + DATA: + mt_items TYPE tt_items. +ENDCLASS. + +CLASS lcl_item DEFINITION FINAL. + PUBLIC SECTION. + METHODS: + constructor + IMPORTING iv_name TYPE string + iv_sell_in TYPE i + iv_quality TYPE i, + description + RETURNING VALUE(rv_string) TYPE string. + DATA: + mv_name TYPE string, + mv_sell_in TYPE i, + mv_quality TYPE i. +ENDCLASS. + +CLASS lcl_gilded_rose IMPLEMENTATION. + + METHOD constructor. + mt_items = it_items. + ENDMETHOD. + + METHOD update_quality. + + LOOP AT mt_items INTO DATA(lo_item). + IF lo_item->mv_name <> |Aged Brie| AND + lo_item->mv_name <> |Backstage passes to a TAFKAL80ETC concert|. + IF lo_item->mv_quality > 0. + IF lo_item->mv_name <> |Sulfuras, Hand of Ragnaros|. + lo_item->mv_quality = lo_item->mv_quality - 1. + ENDIF. + ENDIF. + ELSE. + IF lo_item->mv_quality < 50. + lo_item->mv_quality = lo_item->mv_quality + 1. + + IF lo_item->mv_name = |Backstage passes to a TAFKAL80ETC concert|. + IF lo_item->mv_sell_in < 11. + IF lo_item->mv_quality < 50. + lo_item->mv_quality = lo_item->mv_quality + 1. + ENDIF. + ENDIF. + + IF lo_item->mv_sell_in < 6. + IF lo_item->mv_quality < 50. + lo_item->mv_quality = lo_item->mv_quality + 1. + ENDIF. + ENDIF. + ENDIF. + ENDIF. + ENDIF. + + IF lo_item->mv_name <> |Sulfuras, Hand of Ragnaros|. + lo_item->mv_sell_in = lo_item->mv_sell_in - 1. + ENDIF. + + IF lo_item->mv_sell_in < 0. + IF lo_item->mv_name <> |Aged Brie|. + IF lo_item->mv_name <> |Backstage passes to a TAFKAL80ETC concert|. + IF lo_item->mv_quality > 0. + IF lo_item->mv_name <> |Sulfuras, Hand of Ragnaros|. + lo_item->mv_quality = lo_item->mv_quality - 1. + ENDIF. + ENDIF. + ELSE. + lo_item->mv_quality = lo_item->mv_quality - lo_item->mv_quality. + ENDIF. + ELSE. + IF lo_item->mv_quality < 50. + lo_item->mv_quality = lo_item->mv_quality + 1. + ENDIF. + ENDIF. + ENDIF. + ENDLOOP. + + ENDMETHOD. + +ENDCLASS. + +CLASS lcl_item IMPLEMENTATION. + + METHOD constructor. + mv_name = iv_name. + mv_sell_in = iv_sell_in. + mv_quality = iv_quality. + ENDMETHOD. + + METHOD description. + rv_string = |{ mv_name }, { mv_sell_in }, { mv_quality }|. + ENDMETHOD. + +ENDCLASS. + + +*& Test Code - Executable Text Test Fixture +CLASS lth_texttest_fixture DEFINITION FINAL. + PUBLIC SECTION. + CLASS-METHODS main. +ENDCLASS. + +CLASS lth_texttest_fixture IMPLEMENTATION. + METHOD main. + DATA(lo_out) = cl_demo_output=>new( )->write_text( |OMGHAI!| ). + + DATA(lt_items) = VALUE lcl_gilded_rose=>tt_items( + ( NEW #( iv_name = |+5 Dexterity Vest| + iv_sell_in = 10 + iv_quality = 20 ) ) + ( NEW #( iv_name = |Aged Brie| + iv_sell_in = 2 + iv_quality = 0 ) ) + ( NEW #( iv_name = |Elixir of the Mongoose| + iv_sell_in = 5 + iv_quality = 7 ) ) + ( NEW #( iv_name = |Sulfuras, Hand of Ragnaros| + iv_sell_in = 0 + iv_quality = 80 ) ) + ( NEW #( iv_name = |Backstage passes to a TAFKAL80ETC concert| + iv_sell_in = 15 + iv_quality = 20 ) ) + ( NEW #( iv_name = |Backstage passes to a TAFKAL80ETC concert| + iv_sell_in = 10 + iv_quality = 49 ) ) + ( NEW #( iv_name = |Backstage passes to a TAFKAL80ETC concert| + iv_sell_in = 5 + iv_quality = 49 ) ) + "This conjured item does not work properly yet + ( NEW #( iv_name = |Conjured Mana Cake| + iv_sell_in = 3 + iv_quality = 6 ) ) ). + + DATA(lo_app) = NEW lcl_gilded_rose( it_items = lt_items ). + + DATA(lv_days) = 2. + cl_demo_input=>request( EXPORTING text = |Number of Days?| + CHANGING field = lv_days ). + + DO lv_days TIMES. + lo_out->next_section( |-------- day { sy-index } --------| ). + lo_out->write_text( |Name, Sell_In, Quality| ). + LOOP AT lt_items INTO DATA(lo_item). + lo_out->write_text( lo_item->description( ) ). + ENDLOOP. + lo_app->update_quality( ). + ENDDO. + + lo_out->display( ). + ENDMETHOD. +ENDCLASS. + + +*& Test Code - Currently Broken +CLASS ltc_gilded_rose DEFINITION FINAL FOR TESTING RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS: + foo FOR TESTING. +ENDCLASS. + +CLASS ltc_gilded_rose IMPLEMENTATION. + + METHOD foo. + DATA(lt_items) = VALUE lcl_gilded_rose=>tt_items( ( NEW #( iv_name = |foo| + iv_sell_in = 0 + iv_quality = 0 ) ) ). + + DATA(lo_app) = NEW lcl_gilded_rose( it_items = lt_items ). + lo_app->update_quality( ). + + cl_abap_unit_assert=>assert_equals( + act = CAST lcl_item( lt_items[ 1 ] )->mv_name + exp = |fixme| ). + ENDMETHOD. + +ENDCLASS. diff --git a/bash/README.md b/bash/README.md new file mode 100644 index 00000000..825e530c --- /dev/null +++ b/bash/README.md @@ -0,0 +1,40 @@ +# Requirements + +`bash` and friends (`diff`, `grep`, `cat`) + +# (Failing) Unit Test + +```shell +./unit_test.sh +``` + +# Texttest Fixture + +```shell +./texttest_fixture.sh +``` + +Specify days: + +```shell +./texttest_fixture.sh 30 +``` + +Verify againt `ThirtyDays/stdout.gr` + +```shell +./verify.sh +``` + +## BTW + +BTW, the script is a pure "function", so this works: + +```shell +$ echo -e 'Aged Brie|3|5\nOther Item|4|5' | +> ./gilded_rose.sh | +> ./gilded_rose.sh | +> ./gilded_rose.sh +Aged Brie|0|8 +Other Item|1|2 +``` diff --git a/bash/gilded_rose.sh b/bash/gilded_rose.sh new file mode 100755 index 00000000..4b723399 --- /dev/null +++ b/bash/gilded_rose.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +update_quality() { + local IFS='|' + + while read -r name sell_in quality; do + if [[ $name != "Aged Brie" && $name != "Backstage passes to a TAFKAL80ETC concert" ]]; then + if ((quality > 0)); then + if [[ $name != "Sulfuras, Hand of Ragnaros" ]]; then + quality=$((quality - 1)) + fi + fi + else + if ((quality < 50)); then + quality=$((quality + 1)) + + if [[ $name == "Backstage passes to a TAFKAL80ETC concert" ]]; then + if ((sell_in < 11)); then + if ((quality < 50)); then + quality=$((quality + 1)) + fi + fi + if ((sell_in < 6)); then + if ((quality < 50)); then + quality=$((quality + 1)) + fi + fi + fi + fi + fi + + if [[ $name != "Sulfuras, Hand of Ragnaros" ]]; then + sell_in=$((sell_in - 1)) + fi + + if ((sell_in < 0)); then + if [[ $name != "Aged Brie" ]]; then + if [[ $name != "Backstage passes to a TAFKAL80ETC concert" ]]; then + if ((quality > 0)); then + if [[ $name != "Sulfuras, Hand of Ragnaros" ]]; then + quality=$((quality - 1)) + fi + fi + else + quality=$((quality - quality)) + fi + else + if ((quality < 50)); then + quality=$((quality + 1)) + fi + fi + fi + + echo "$name|$sell_in|$quality" + done +} + +update_quality diff --git a/bash/texttest_fixture.sh b/bash/texttest_fixture.sh new file mode 100755 index 00000000..7018f127 --- /dev/null +++ b/bash/texttest_fixture.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +GILDED_ROSE_SCRIPT="./gilded_rose.sh" + +# Define initial items as a multi-line string +initial_items="+5 Dexterity Vest|10|20 +Aged Brie|2|0 +Elixir of the Mongoose|5|7 +Sulfuras, Hand of Ragnaros|0|80 +Sulfuras, Hand of Ragnaros|-1|80 +Backstage passes to a TAFKAL80ETC concert|15|20 +Backstage passes to a TAFKAL80ETC concert|10|49 +Backstage passes to a TAFKAL80ETC concert|5|49 +Conjured Mana Cake|3|6" + +simulate_days() { + local days=$1 + local items="$2" + + for ((day = 0; day <= days; day++)); do + echo "-------- day $day --------" + echo "name, sellIn, quality" + + echo "$items" | sed 's/|/, /g' + + # Update the items for the next day + items=$(echo "$items" | bash "$GILDED_ROSE_SCRIPT") + + echo "" + done +} + +echo OMGHAI! + +DAYS_TO_SIMULATE=${1:-2} + +simulate_days $DAYS_TO_SIMULATE "$initial_items" diff --git a/bash/unit_test.sh b/bash/unit_test.sh new file mode 100755 index 00000000..34cf29d8 --- /dev/null +++ b/bash/unit_test.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +GILDED_ROSE_SCRIPT="./gilded_rose.sh" + +get_name() { + grep -o '^[^|]*' +} + +assert_equals() { + local expected="$1" + diff -u - <(echo "$expected") +} + +test_foo() { + echo "foo|0|0" | + bash "$GILDED_ROSE_SCRIPT" | + get_name | + assert_equals "fixme" +} + +test_foo diff --git a/bash/verify.sh b/bash/verify.sh new file mode 100755 index 00000000..ad9d95e5 --- /dev/null +++ b/bash/verify.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +./texttest_fixture.sh 30 | + diff -u - ../texttests/ThirtyDays/stdout.gr && + echo "✅ looks good" || + (echo "❌ failed" && exit 1) diff --git a/c99/CMakeLists.txt b/c99/CMakeLists.txt new file mode 100644 index 00000000..cafa2ef7 --- /dev/null +++ b/c99/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 2.8.4) +project(GildedRose_c99) + +enable_testing() + +include(ExternalProject) +ExternalProject_Add(unity + GIT_REPOSITORY https://github.com/ThrowTheSwitch/Unity.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/unity-src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/unity-build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/unity-src/src/unity.c + DEPENDS unity + COMMAND "") + + +add_executable( GildedRose_Unity + GildedRose.c + ${CMAKE_CURRENT_BINARY_DIR}/unity-src/src/unity.c + test_unity_gildedrose.c +) +target_include_directories(GildedRose_Unity PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/unity-src/src/) +set_property(TARGET GildedRose_Unity PROPERTY C_STANDARD 99) +add_dependencies(GildedRose_Unity unity) + +add_test(NAME GildedRose_Unity COMMAND GildedRose_Unity) diff --git a/c99/GildedRose.c b/c99/GildedRose.c new file mode 100644 index 00000000..afb97bbe --- /dev/null +++ b/c99/GildedRose.c @@ -0,0 +1,90 @@ +#include +#include "GildedRose.h" + +Item* +init_item(Item* item, const char *name, int sellIn, int quality) +{ + item->sellIn = sellIn; + item->quality = quality; + item->name = strdup(name); + + return item; +} + +void update_quality(Item items[], int size) +{ + int i; + + for (i = 0; i < size; i++) + { + if (strcmp(items[i].name, "Aged Brie") && strcmp(items[i].name, "Backstage passes to a TAFKAL80ETC concert")) + { + if (items[i].quality > 0) + { + if (strcmp(items[i].name, "Sulfuras, Hand of Ragnaros")) + { + items[i].quality = items[i].quality - 1; + } + } + } + else + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + + if (!strcmp(items[i].name, "Backstage passes to a TAFKAL80ETC concert")) + { + if (items[i].sellIn < 11) + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + + if (items[i].sellIn < 6) + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + } + } + } + + if (strcmp(items[i].name, "Sulfuras, Hand of Ragnaros")) + { + items[i].sellIn = items[i].sellIn - 1; + } + + if (items[i].sellIn < 0) + { + if (strcmp(items[i].name, "Aged Brie")) + { + if (strcmp(items[i].name, "Backstage passes to a TAFKAL80ETC concert")) + { + if (items[i].quality > 0) + { + if (strcmp(items[i].name, "Sulfuras, Hand of Ragnaros")) + { + items[i].quality = items[i].quality - 1; + } + } + } + else + { + items[i].quality = items[i].quality - items[i].quality; + } + } + else + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + } + } +} diff --git a/c99/GildedRose.h b/c99/GildedRose.h new file mode 100644 index 00000000..78d54a08 --- /dev/null +++ b/c99/GildedRose.h @@ -0,0 +1,9 @@ +typedef struct +{ + char *name; + int sellIn; + int quality; +} Item; + +extern Item* init_item(Item* item, const char *name, int sellIn, int quality); +extern void update_quality(Item items[], int size); diff --git a/c99/GildedRoseTextTests.c b/c99/GildedRoseTextTests.c new file mode 100644 index 00000000..d200ca0c --- /dev/null +++ b/c99/GildedRoseTextTests.c @@ -0,0 +1,43 @@ +#include +#include "GildedRose.h" + +int +print_item(Item *item) +{ + return printf("%s, %d, %d\n", item->name, item->sellIn, item->quality); +} + +int main() +{ + Item items[9]; + int last = 0; + int day; + int index; + + init_item(items + last++, "+5 Dexterity Vest", 10, 20); + init_item(items + last++, "Aged Brie", 2, 0); + init_item(items + last++, "Elixir of the Mongoose", 5, 7); + init_item(items + last++, "Sulfuras, Hand of Ragnaros", 0, 80); + init_item(items + last++, "Sulfuras, Hand of Ragnaros", -1, 80); + init_item(items + last++, "Backstage passes to a TAFKAL80ETC concert", 15, 20); + init_item(items + last++, "Backstage passes to a TAFKAL80ETC concert", 10, 49); + init_item(items + last++, "Backstage passes to a TAFKAL80ETC concert", 5, 49); + // this Conjured item doesn't yet work properly + init_item(items + last++, "Conjured Mana Cake", 3, 6); + + puts("OMGHAI!"); + + for (day = 0; day <= 30; day++) + { + printf("-------- day %d --------\n", day); + printf("name, sellIn, quality\n"); + for(index = 0; index < last; index++) { + print_item(items + index); + } + + printf("\n"); + + update_quality(items, last); + } + return 0; +} diff --git a/c99/Makefile b/c99/Makefile new file mode 100644 index 00000000..113d1552 --- /dev/null +++ b/c99/Makefile @@ -0,0 +1,51 @@ +# Makefile for building the kata file with the Google Testing Framework +# +# SYNOPSIS: +# +# make [all] - makes everything, runs tests +# make TARGET - makes the given target. +# make clean - removes all files generated by make. +# make memtest - run memory leak analysis + +# The _POSIX_C_SOURCE definition prevents the compiler from throwing warnings +CFLAGS = `pkg-config --cflags check` -g --std=c99 -D_POSIX_C_SOURCE=200809L +LIBS = `pkg-config --libs check` + +# All files that should be part of your test should start with 'test_' +TEST_SRC = $(wildcard test_*.c) +TEST_BASE = $(basename ${TEST_SRC}) +TEST_OBJECTS = $(addsuffix .o, ${TEST_BASE}) + +# All files that should be part of your main program should start with 'gilded_' +PROG_SRC = $(wildcard gilded_*.c) +PROG_BASE = $(basename ${PROG_SRC}) +PROG_OBJECTS = $(addsuffix .o, ${PROG_BASE}) + +OBJECT_UNDER_TEST = GildedRose.o ${PROG_OBJECTS} + +# This is the test application. You can run this program to see your test output +TEST_APP = test_gildedrose + + +# This will generate output for several products over a course of several days. +# You can run this application to build golden rule tests +GOLDEN_APP = golden_rose + +all: ${TEST_APP} ${GOLDEN_APP} + ./${TEST_APP} + +${TEST_APP}: ${TEST_OBJECTS} ${OBJECT_UNDER_TEST} + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) + +${GOLDEN_APP}: GildedRoseTextTests.o ${OBJECT_UNDER_TEST} + $(CC) $(CFLAGS) -o $@ $^ + +# If you're not on a mac, you should run memtest (in fact, consider adding it to the 'all' target). +# If you're on a mac, complain to apple for breaking an important development tool. +memtest: ${TEST_APP} + valgrind --leak-check=full --error-exitcode=1 ./${TEST_APP} --nofork + +clean: + rm -f *.o + rm -f ${TEST_APP} + rm -f ${GOLDEN_APP} diff --git a/c99/README.md b/c99/README.md new file mode 100644 index 00000000..ec2cd02e --- /dev/null +++ b/c99/README.md @@ -0,0 +1,41 @@ +# Gilded Rose, C99 Edition + +The command "make" will build and run your tests, as well as build the program +golden_rose, which can serve as the basis for a golden-rule test. + + +## Assumptions + - gnu make and a C compiler (like gcc) is installed on your system and is in the PATH + - The check unit testing library is installed on your system (https://libcheck.github.io/check/) + - pkg-config is installed on your system + +## Usage + - Run `make` to build the program and run all tests + - Files which contain tests should be named `test_*.c` They will automatically + be included in your test suite. + - `GildedRose.h` should not be modified. The Goblin threat is real. + - New program logic may be included in files named `gilded_*.c` which will + automatically be included in both your tests and the final program. + +## Golden Rule tests + - The program `golden_rose` will generate text output. If you capture this + output after your first `make` you can use this as a reference for a golden + rule test. + - You can test your work against this reference by directing the output of your + current golden_rose to a file and using the `diff` utility to compare that + to the reference file you created above. + - To avoid the Goblin threat you can use `git diff GildedRose.h`, which should + have no output if you have left the precious Item structure unchanged. + +## Notes + - This project is tweaked to run on Linux systems, and will mostly work on Macs. + With some changes to the Makefile it can be made to run on BSD systems with + BSD make. An adventurous person could also get it to run on Windows. + - If you are working on a Macintosh computer you cannot run the memtest target, + because valgrind and OS X don't play nice any more. If you want to use the + memory checker OS X does run docker as a first class citizen. + - If you don't have pkg-config on your system, the only changes you'll need to + make are for the requirements of the check library. Mostly you need to + set the appropriate flags for threaded binaries, which may include some + special linker flags. The libcheck documentation will cover what you need + if you want to undertake this change. diff --git a/c99/run-once-cmake.sh b/c99/run-once-cmake.sh new file mode 100755 index 00000000..74e2c0e0 --- /dev/null +++ b/c99/run-once-cmake.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +if [[ ! -d build ]]; then + mkdir -p build +fi + +cd build +cmake .. -DCMAKE_BUILD_TYPE=DEBUG && cmake --build . && ctest --output-on-failure diff --git a/c99/test_main.c b/c99/test_main.c new file mode 100644 index 00000000..0d5a0de0 --- /dev/null +++ b/c99/test_main.c @@ -0,0 +1,31 @@ +#include +#include +#include + +Suite *suite_rose(void); + +int main(int argc, char **argv) +{ + Suite *s; + SRunner *runner; + int number_fails; + int forkme = 1; + + if (argc > 1 && strcmp(argv[1], "--nofork") == 0) { + forkme = 0; + } + + s = suite_rose(); + runner = srunner_create(s); + + if (0 == forkme) { + srunner_set_fork_status(runner, CK_NOFORK); + } + + srunner_run_all(runner, CK_NORMAL); + number_fails = srunner_ntests_failed(runner); + + srunner_free(runner); + + return number_fails; +} diff --git a/c99/test_rose.c b/c99/test_rose.c new file mode 100644 index 00000000..30cc7544 --- /dev/null +++ b/c99/test_rose.c @@ -0,0 +1,34 @@ +#include +#include "GildedRose.h" + + + +START_TEST(roseFoo) +{ + Item items[1]; + init_item(items, "foo", 0, 0); + update_quality(items, 1); + + ck_assert_str_eq("fixme", items[0].name); +} +END_TEST + +TCase *tcase_rose(void) +{ + TCase *tc; + + tc = tcase_create("gilded-rose"); + tcase_add_test(tc, roseFoo); + + return tc; +} + +Suite *suite_rose(void) +{ + Suite *s; + + s = suite_create("characterization-tests"); + suite_add_tcase(s, tcase_rose()); + + return s; +} diff --git a/c99/test_unity_gildedrose.c b/c99/test_unity_gildedrose.c new file mode 100644 index 00000000..5e0d8b98 --- /dev/null +++ b/c99/test_unity_gildedrose.c @@ -0,0 +1,18 @@ +#include "unity.h" +#include "GildedRose.h" + +void test_NameOfItem(void) +{ + Item items[1]; + init_item(items, "Foo", 0, 0); + update_quality(items, 1); + TEST_ASSERT_EQUAL_STRING( "fixme", items[0].name ); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_NameOfItem); + return UNITY_END(); +} + diff --git a/c_cmocka/.gitignore b/c_cmocka/.gitignore new file mode 100644 index 00000000..5de8153c --- /dev/null +++ b/c_cmocka/.gitignore @@ -0,0 +1,13 @@ +# Allow in source build +[Bb]uild*/ +# Ignore qt files when importing CMakeLists +*.users +# Ignore CLion project files +*.idea +# For macOs users +.DS_Store + +/build_meson/ +/subprojects/Catch2-*/ +/subprojects/packagecache/ +/cmake-build-*/ diff --git a/c_cmocka/CMakeLists.txt b/c_cmocka/CMakeLists.txt new file mode 100644 index 00000000..b694a976 --- /dev/null +++ b/c_cmocka/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +project(GildedRose VERSION 1.0) + +set(CMAKE_C_STANDARD 11) + +add_subdirectory(src) +add_subdirectory(test-cmocka) + + +add_executable(main GildedRoseTextTests.c) +target_link_libraries(main src) + diff --git a/c_cmocka/GildedRoseTextTests.c b/c_cmocka/GildedRoseTextTests.c new file mode 100644 index 00000000..9e79a033 --- /dev/null +++ b/c_cmocka/GildedRoseTextTests.c @@ -0,0 +1,50 @@ +#include +#include +#include "GildedRose.h" + +int +print_item(Item *item) +{ + return printf("%s, %d, %d\n", item->name, item->sellIn, item->quality); +} + +int main(int argc, char **argv) +{ + int days = 2; + if (argc > 1) { + char *a = argv[1]; + days = atoi(a); + } + + Item items[9]; + int last = 0; + int day; + int index; + + init_item(items + last++, "+5 Dexterity Vest", 10, 20); + init_item(items + last++, "Aged Brie", 2, 0); + init_item(items + last++, "Elixir of the Mongoose", 5, 7); + init_item(items + last++, "Sulfuras, Hand of Ragnaros", 0, 80); + init_item(items + last++, "Sulfuras, Hand of Ragnaros", -1, 80); + init_item(items + last++, "Backstage passes to a TAFKAL80ETC concert", 15, 20); + init_item(items + last++, "Backstage passes to a TAFKAL80ETC concert", 10, 49); + init_item(items + last++, "Backstage passes to a TAFKAL80ETC concert", 5, 49); + // this Conjured item doesn't yet work properly + init_item(items + last++, "Conjured Mana Cake", 3, 6); + + puts("OMGHAI!"); + + for (day = 0; day <= days; day++) + { + printf("-------- day %d --------\n", day); + printf("name, sellIn, quality\n"); + for(index = 0; index < last; index++) { + print_item(items + index); + } + + printf("\n"); + + update_quality(items, last); + } + return 0; +} diff --git a/c_cmocka/README.md b/c_cmocka/README.md new file mode 100644 index 00000000..0a872b74 --- /dev/null +++ b/c_cmocka/README.md @@ -0,0 +1,20 @@ +# Gilded Rose starting position in C with CMocka + +Use CMake to build it. Run the target "sample_test" to run the sample unit test using the cmocka framework. + +## Run the TextTest fixture on the command line + +When you build this project this executable should be created: + + c_cmocka/cmake-build-debug/main + +Execute it on the command line with an argument for the number of days: + + c_cmocka/cmake-build-debug/main 10 + +## Run the TextTest approval test that comes with this project + +There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. You will need to specify the C executable in [config.gr](../texttests/config.gr). Uncomment this line to use it: + + executable:${TEXTTEST_HOME}/c_cmocka/cmake-build-debug/main + diff --git a/c_cmocka/src/CMakeLists.txt b/c_cmocka/src/CMakeLists.txt new file mode 100644 index 00000000..ed464f5a --- /dev/null +++ b/c_cmocka/src/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SRC_LIB_NAME src) +set(SOURCES GildedRose.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_cmocka/src/GildedRose.c b/c_cmocka/src/GildedRose.c new file mode 100644 index 00000000..afb97bbe --- /dev/null +++ b/c_cmocka/src/GildedRose.c @@ -0,0 +1,90 @@ +#include +#include "GildedRose.h" + +Item* +init_item(Item* item, const char *name, int sellIn, int quality) +{ + item->sellIn = sellIn; + item->quality = quality; + item->name = strdup(name); + + return item; +} + +void update_quality(Item items[], int size) +{ + int i; + + for (i = 0; i < size; i++) + { + if (strcmp(items[i].name, "Aged Brie") && strcmp(items[i].name, "Backstage passes to a TAFKAL80ETC concert")) + { + if (items[i].quality > 0) + { + if (strcmp(items[i].name, "Sulfuras, Hand of Ragnaros")) + { + items[i].quality = items[i].quality - 1; + } + } + } + else + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + + if (!strcmp(items[i].name, "Backstage passes to a TAFKAL80ETC concert")) + { + if (items[i].sellIn < 11) + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + + if (items[i].sellIn < 6) + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + } + } + } + + if (strcmp(items[i].name, "Sulfuras, Hand of Ragnaros")) + { + items[i].sellIn = items[i].sellIn - 1; + } + + if (items[i].sellIn < 0) + { + if (strcmp(items[i].name, "Aged Brie")) + { + if (strcmp(items[i].name, "Backstage passes to a TAFKAL80ETC concert")) + { + if (items[i].quality > 0) + { + if (strcmp(items[i].name, "Sulfuras, Hand of Ragnaros")) + { + items[i].quality = items[i].quality - 1; + } + } + } + else + { + items[i].quality = items[i].quality - items[i].quality; + } + } + else + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + } + } +} diff --git a/c_cmocka/src/GildedRose.h b/c_cmocka/src/GildedRose.h new file mode 100644 index 00000000..78d54a08 --- /dev/null +++ b/c_cmocka/src/GildedRose.h @@ -0,0 +1,9 @@ +typedef struct +{ + char *name; + int sellIn; + int quality; +} Item; + +extern Item* init_item(Item* item, const char *name, int sellIn, int quality); +extern void update_quality(Item items[], int size); diff --git a/c_cmocka/test-cmocka/CMakeLists.txt b/c_cmocka/test-cmocka/CMakeLists.txt new file mode 100644 index 00000000..7f0429c7 --- /dev/null +++ b/c_cmocka/test-cmocka/CMakeLists.txt @@ -0,0 +1,44 @@ +set(TEST_NAME test-cmocka) + +include(FetchContent) + +FetchContent_Declare( + cmocka + GIT_REPOSITORY https://git.cryptomilk.org/projects/cmocka.git + GIT_TAG cmocka-1.1.5 + GIT_SHALLOW 1 +) + +set(WITH_STATIC_LIB ON CACHE BOOL "CMocka: Build with a static library" FORCE) +set(WITH_CMOCKERY_SUPPORT OFF CACHE BOOL "CMocka: Install a cmockery header" FORCE) +set(WITH_EXAMPLES OFF CACHE BOOL "CMocka: Build examples" FORCE) +set(UNIT_TESTING OFF CACHE BOOL "CMocka: Build with unit testing" FORCE) +set(PICKY_DEVELOPER OFF CACHE BOOL "CMocka: Build with picky developer flags" FORCE) + +FetchContent_MakeAvailable(cmocka) + +function(ADD_CMOCKA_TEST_ENVIRONMENT _TARGET_NAME) + if (WIN32 OR CYGWIN OR MINGW OR MSVC) + file(TO_NATIVE_PATH "${cmocka-library_BINARY_DIR}" CMOCKA_DLL_PATH) + + if (TARGET_SYSTEM_EMULATOR) + set(DLL_PATH_ENV "WINEPATH=${CMOCKA_DLL_PATH};$ENV{WINEPATH}") + else () + set(DLL_PATH_ENV "PATH=${CMOCKA_DLL_PATH}\\${CMAKE_BUILD_TYPE};$ENV{PATH}") + endif () + # + # IMPORTANT NOTE: The set_tests_properties(), below, internally + # stores its name/value pairs with a semicolon delimiter. + # because of this we must protect the semicolons in the path + # + string(REPLACE ";" "\\;" DLL_PATH_ENV "${DLL_PATH_ENV}") + + set_tests_properties(${_TARGET_NAME} + PROPERTIES + ENVIRONMENT + "${DLL_PATH_ENV}") + endif () +endfunction() + + +add_subdirectory(sample_test) diff --git a/c_cmocka/test-cmocka/sample_test/CMakeLists.txt b/c_cmocka/test-cmocka/sample_test/CMakeLists.txt new file mode 100644 index 00000000..297eefbf --- /dev/null +++ b/c_cmocka/test-cmocka/sample_test/CMakeLists.txt @@ -0,0 +1,6 @@ +project(sample-cmocka C) + +add_cmocka_test(sample_test + SOURCES sample_test.c + LINK_LIBRARIES src cmocka-static) +add_cmocka_test_environment(add_test) diff --git a/c_cmocka/test-cmocka/sample_test/sample_test.c b/c_cmocka/test-cmocka/sample_test/sample_test.c new file mode 100644 index 00000000..92525d16 --- /dev/null +++ b/c_cmocka/test-cmocka/sample_test/sample_test.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include "cmocka.h" + +#include "GildedRose.h" + +static void test_rose(void **state) +{ + Item items[1]; + init_item(items, "foo", 0, 0); + update_quality(items, 1); + + assert_string_equal("fixme", items[0].name); +} + +int main(void) { + const struct CMUnitTest tests[] = + { + cmocka_unit_test(test_rose), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/clojure/.gitignore b/clojure/.gitignore new file mode 100644 index 00000000..bc5c9ebb --- /dev/null +++ b/clojure/.gitignore @@ -0,0 +1,2 @@ +.cpcache +actual.txt diff --git a/clojure/README.md b/clojure/README.md new file mode 100644 index 00000000..b361d584 --- /dev/null +++ b/clojure/README.md @@ -0,0 +1,34 @@ +# Gilded Rose + +This is the Gilded Rose kata in Clojure. + +## Getting started + +You'll need the [clojure command-line tool](https://clojure.org/guides/install_clojure). + +## Running app + +Produces output suitable for texttests: + +```sh +clojure -M:main +``` + +You can specify the number of days as args: +```sh +clojure -M:main 31 +``` + +## Running tests + +To run all tests + +```sh +clojure -M:test +``` + +To run all tests in watch mode + +```sh +clojure -M:test --watch +``` diff --git a/clojure/deps.edn b/clojure/deps.edn new file mode 100644 index 00000000..14e676d7 --- /dev/null +++ b/clojure/deps.edn @@ -0,0 +1,10 @@ +{:paths ["src" "resources"] + :deps {org.clojure/clojure {:mvn/version "1.11.1"}} + :aliases + {:main + {:main-opts ["-m" "gilded.main"]} + :test + {:extra-paths ["test"] + :extra-deps {lambdaisland/kaocha {:mvn/version "1.70.1086"} + org.slf4j/slf4j-nop {:mvn/version "2.0.1"}} + :main-opts ["-m" "kaocha.runner"]}}} diff --git a/clojure/src/gilded/core.clj b/clojure/src/gilded/core.clj new file mode 100644 index 00000000..bde0343c --- /dev/null +++ b/clojure/src/gilded/core.clj @@ -0,0 +1,46 @@ +(ns gilded.core) + +(defn make-store [items] + (assert (vector? items)) + (->> items + (map (fn [item] (atom item))) + vec)) + +(defn item-seq [store] + (->> store + (map deref))) +;; --- + +(defn update-quality! [store] + (doseq [item store] + (if (and (not (= (:name @item) + "Aged Brie")) + (not (= (:name @item) + "Backstage passes to a TAFKAL80ETC concert"))) + + (when (> (:quality @item) 0) + (when (not (= (:name @item) "Sulfuras, Hand of Ragnaros")) + (swap! item update :quality #(- % 1)))) + + (when (< (:quality @item) 50) + (swap! item update :quality #(+ % 1)) + (when (= (:name @item) "Backstage passes to a TAFKAL80ETC concert") + (when (< (:sell-in @item) 11) + (when (< (:quality @item) 50) + (swap! item update :quality #(+ % 1)))) + (when (< (:sell-in @item) 6) + (when (< (:quality @item) 50) + (swap! item update :quality #(+ % 1))))))) + + (when (not (= (:name @item) "Sulfuras, Hand of Ragnaros")) + (swap! item update :sell-in #(- % 1))) + + (when (< (:sell-in @item) 0) + (if (not (= (:name @item) "Aged Brie")) + (if (not (= (:name @item) "Backstage passes to a TAFKAL80ETC concert")) + (when (> (:quality @item) 0) + (when (not (= (:name @item) "Sulfuras, Hand of Ragnaros")) + (swap! item update :quality #(- % 1)))) + (swap! item update :quality #(- % %))) + (when (< (:quality @item) 50) + (swap! item update :quality #(+ % 1))))))) diff --git a/clojure/src/gilded/main.clj b/clojure/src/gilded/main.clj new file mode 100644 index 00000000..fdfd7590 --- /dev/null +++ b/clojure/src/gilded/main.clj @@ -0,0 +1,32 @@ +(ns gilded.main + (:require [gilded.core :as x])) + +(defn item->str [item] + (str (:name item) ", " (:sell-in item) ", " (:quality item))) + +(defn print-store [store] + (println "name, sellIn, quality") + (doseq [item (x/item-seq store)] + (println (item->str item))) + (println)) + +(def fixture + [{:name "+5 Dexterity Vest", :quality 20, :sell-in 10} + {:name "Aged Brie", :quality 0, :sell-in 2} + {:name "Elixir of the Mongoose", :quality 7, :sell-in 5} + {:name "Sulfuras, Hand of Ragnaros", :quality 80, :sell-in 0} + {:name "Sulfuras, Hand of Ragnaros", :quality 80, :sell-in -1} + {:name "Backstage passes to a TAFKAL80ETC concert", :quality 20, :sell-in 15} + {:name "Backstage passes to a TAFKAL80ETC concert", :quality 49, :sell-in 10} + {:name "Backstage passes to a TAFKAL80ETC concert", :quality 49, :sell-in 5} + {:name "Conjured Mana Cake", :quality 6, :sell-in 3}]) + +(defn -main [& args] + (let [n-days (if (nil? (first args)) + 2 + (Long/parseLong (first args))) + store (x/make-store fixture)] + (dotimes [day n-days] + (println "-------- day" day "--------") + (print-store store) + (x/update-quality! store)))) diff --git a/clojure/test/gilded/core_test.clj b/clojure/test/gilded/core_test.clj new file mode 100644 index 00000000..773132e6 --- /dev/null +++ b/clojure/test/gilded/core_test.clj @@ -0,0 +1,12 @@ +(ns gilded.core-test + (:require [clojure.test :refer [deftest is]] + [gilded.core :as x])) + +(def fixture + [{:name "foo", :quality 20, :sell-in 10}]) + +(deftest simple-test + (let [store (x/make-store fixture) + _ (x/update-quality! store) + item (first (x/item-seq store))] + (is (= "fixme" (:name item))))) diff --git a/clojure/tests.edn b/clojure/tests.edn new file mode 100644 index 00000000..b1a48be5 --- /dev/null +++ b/clojure/tests.edn @@ -0,0 +1,2 @@ +#kaocha/v1 +{} diff --git a/common-lisp-parachute/README.md b/common-lisp-parachute/README.md new file mode 100644 index 00000000..9cbf15bb --- /dev/null +++ b/common-lisp-parachute/README.md @@ -0,0 +1,57 @@ +# gilded rose + +The requirements of gilded rose can be found here: +https://github.com/emilybache/GildedRose-Refactoring-Kata/blob/main/GildedRoseRequirements.txt + +# 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 gilded-rose.asd file which defines the system (project) and its dependencies. +The quicklisp-folder is usually created in your home-directory. + +## Working with the project + +Now you can load the project with +> (ql:quickload "gilded-rose") +in the common lisp implementation of your choice and run the tests with +> (asdf:test-system "gilded-rose") +. + +If you just want to run the tests +> (asdf:test-system "gilded-rose") +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 ') + +## Running the texttest-fixture + +If you don't want to work with the unit-tests you can test your program with the texttest-fixture. +After loading the project in the common lisp implementation of your choice with +> (ql:quickload "gilded-rose") +you can run the texttest-fixture with +> (gilded-rose::run-gilded-rose ) +where is the number of days you want to simulate. diff --git a/common-lisp-parachute/gilded-rose.asd b/common-lisp-parachute/gilded-rose.asd new file mode 100644 index 00000000..5985f942 --- /dev/null +++ b/common-lisp-parachute/gilded-rose.asd @@ -0,0 +1,20 @@ +;;;; gilded-rose.asd + +(defsystem "gilded-rose" + :description "Gilded Rose is a small inn selling the finest goods." + :author "Leeroy " + :version "1.0.0" + :pathname "source/" + :components ((:file "package") + (:file "gilded-rose" :depends-on ("package"))) + :in-order-to ((test-op (test-op "gilded-rose/tests")))) + +(defsystem "gilded-rose/tests" + :description "Unit tests for gilded-rose-package" + :author "Leeroy " + :version "1.0.0" + :depends-on ("gilded-rose" "parachute" "cl-mock") + :pathname "tests/" + :components ((:file "package") + (:file "tests" :depends-on ("package"))) + :perform (test-op (o c) (symbol-call :parachute :test :gilded-rose-tests))) diff --git a/common-lisp-parachute/source/gilded-rose.lisp b/common-lisp-parachute/source/gilded-rose.lisp new file mode 100644 index 00000000..9e523cc6 --- /dev/null +++ b/common-lisp-parachute/source/gilded-rose.lisp @@ -0,0 +1,90 @@ +;; https://github.com/emilybache/GildedRose-Refactoring-Kata + +;; Common Lisp version: Rainer Joswig, joswig@lisp.de, 2016 +;; Minor modifications (independent of CL impl.): Manfred Bergmann, 2022 +;; Adaption to asdf: Nico Simoski, 2023 + +;;; ================================================================ +;;; Code + +(in-package :gilded-rose) + +;;; Class ITEM + +(defclass item () + ((name :initarg :name :type string) + (sell-in :initarg :sell-in :type integer) + (quality :initarg :quality :type integer))) + +(defmethod to-string ((i item)) + (with-slots (name quality sell-in) i + (format nil "~a, ~a, ~a" name sell-in quality))) + +;;; Class gilded-rose + +(defclass gilded-rose () + ((items :initarg :items))) + +(defmethod update-quality ((gr gilded-rose)) + (with-slots (items) gr + (dotimes (i (length items)) + (with-slots (name quality sell-in) + (elt items i) + (if (and (not (equalp name "Aged Brie")) + (not (equalp name "Backstage passes to a TAFKAL80ETC concert"))) + (if (> quality 0) + (if (not (equalp name "Sulfuras, Hand of Ragnaros")) + (setf quality (- quality 1)))) + (when (< quality 50) + (setf quality (+ quality 1)) + (when (equalp name "Backstage passes to a TAFKAL80ETC concert") + (if (< sell-in 11) + (if (< quality 50) + (setf quality (+ quality 1)))) + (if (< sell-in 6) + (if (< quality 50) + (setf quality (+ quality 1))))))) + + (if (not (equalp name "Sulfuras, Hand of Ragnaros")) + (setf sell-in (- sell-in 1))) + + (if (< sell-in 0) + (if (not (equalp name "Aged Brie")) + (if (not (equalp name "Backstage passes to a TAFKAL80ETC concert")) + (if (> quality 0) + (if (not (equalp name "Sulfuras, Hand of Ragnaros")) + (setf quality (- quality 1)))) + (setf quality (- quality quality))) + (if (< quality 50) + (setf quality (+ quality 1))))))))) + +;;; Example + +(defun run-gilded-rose (days) + (write-line "OMGHAI!") + (let* ((descriptions '(("+5 Dexterity Vest" 10 20) + ("Aged Brie" 2 0) + ("Elixir of the Mongoose" 5 7) + ("Sulfuras, Hand of Ragnaros" 0 80) + ("Sulfuras, Hand of Ragnaros" -1 80) + ("Backstage passes to a TAFKAL80ETC concert" 15 20) + ("Backstage passes to a TAFKAL80ETC concert" 10 49) + ("Backstage passes to a TAFKAL80ETC concert" 5 49) + ;; this conjured item does not work properly yet + ("Conjured Mana Cake" 3 6))) + (items (loop :for (name sell-in quality) :in descriptions + :collect (make-instance 'item + :name name + :sell-in sell-in + :quality quality))) + (app (make-instance 'gilded-rose :items items))) + (dotimes (i days) + (format t "-------- day ~a --------~%" i) + (format t "name, sell-in, quality~%") + (dolist (item items) + (write-line (to-string item))) + (terpri) + (update-quality app)))) + +;;; ================================================================ +;;; EOF diff --git a/common-lisp-parachute/source/package.lisp b/common-lisp-parachute/source/package.lisp new file mode 100644 index 00000000..c94fed86 --- /dev/null +++ b/common-lisp-parachute/source/package.lisp @@ -0,0 +1,14 @@ +;;;; package.lisp + +(defpackage :gilded-rose + (:use :cl) + (:export :run-gilded-rose + :gilded-rose + :update-quality + :items + :item + :name + :sell-in + :quality)) + + diff --git a/common-lisp-parachute/tests/package.lisp b/common-lisp-parachute/tests/package.lisp new file mode 100644 index 00000000..ae158148 --- /dev/null +++ b/common-lisp-parachute/tests/package.lisp @@ -0,0 +1,12 @@ +;;;; package.lisp + +(defpackage :gilded-rose-tests + (:use :cl :gilded-rose) + (:import-from :parachute + :define-test + :is) + (:import-from :cl-mock + :with-mocks + :answer + :call-previous + :invocations)) diff --git a/common-lisp-parachute/tests/tests.lisp b/common-lisp-parachute/tests/tests.lisp new file mode 100644 index 00000000..8cdc59a3 --- /dev/null +++ b/common-lisp-parachute/tests/tests.lisp @@ -0,0 +1,12 @@ +(in-package :gilded-rose-tests) + +(define-test gilded-rose-testsuite) + +(define-test "Test foo." + :parent gilded-rose-testsuite + (let* ((an-item (make-instance 'item :name "foo" :sell-in 0 :quality 0)) + (some-items (list an-item)) + (my-app (make-instance 'gilded-rose :items some-items))) + (update-quality my-app) + (is equal (slot-value (first (slot-value my-app 'items)) 'name) "fixme"))) + diff --git a/commonlisp/gilded-rose-functional.lisp b/commonlisp/gilded-rose-functional.lisp new file mode 100644 index 00000000..10cd83e8 --- /dev/null +++ b/commonlisp/gilded-rose-functional.lisp @@ -0,0 +1,129 @@ +;; Hi and welcome to team Gilded Rose. As you know, we are a small inn +;; with a prime location in a prominent city ran by a friendly +;; innkeeper named Allison. We also buy and sell only the finest goods. +;; Unfortunately, our goods are constantly degrading in quality as they +;; approach their sell by date. We have a system in place that updates +;; our inventory for us. It was developed by a no-nonsense type named +;; Leeroy, who has moved on to new adventures. Your task is to add the +;; new feature to our system so that we can begin selling a new +;; category of items. + +;; First an introduction to our system: +;; All items have a SellIn value which denotes the number of days we have to sell the item +;; All items have a Quality value which denotes how valuable the item is +;; At the end of each day our system lowers both values for every item +;; Pretty simple, right? Well this is where it gets interesting: +;; Once the sell by date has passed, Quality degrades twice as fast +;; The Quality of an item is never negative +;; "Aged Brie" actually increases in Quality the older it gets +;; The Quality of an item is never more than 50 +;; "Sulfuras", being a legendary item, never has to be sold or decreases in Quality +;; "Backstage passes", like aged brie, increases in Quality as it's +;; SellIn value approaches; Quality increases by 2 when there are 10 +;; days or less and by 3 when there are 5 days or less but Quality +;; drops to 0 after the concert + +;; We have recently signed a supplier of conjured items. This requires an update to our system: +;; "Conjured" items degrade in Quality twice as fast as normal items +;; Feel free to make any changes to the UpdateQuality method and add +;; any new code as long as everything still works correctly. However, +;; do not alter the Item class or Items property as those belong to the +;; goblin in the corner who will insta-rage and one-shot you as he +;; doesn't believe in shared code ownership (you can make the +;; UpdateQuality method and Items property static if you like, we'll +;; cover for you). +;; Just for clarification, an item can never have its Quality increase +;; above 50, however "Sulfuras" is a legendary item and as such its +;; Quality is 80 and it never alters. + +;; https://github.com/emilybache/GildedRose-Refactoring-Kata + +;; Common Lisp functional version: Manfred Bergmann, 2022 + +;;; ================================================================ +;;; Code + +(defpackage :gilded-rose + (:use :cl)) + +(in-package :gilded-rose) + + +;;; Class ITEM + +(defstruct item + (name "" :type string :read-only t) + (sell-in 0 :type integer) + (quality 0 :type integer)) + +(defun update-quality (items) + (mapcar #'update-item items)) + +(defun update-item (item) + (let ((new-item (copy-structure item))) + (with-slots (name quality sell-in) new-item + (if (and (not (equalp name "Aged Brie")) + (not (equalp name "Backstage passes to a TAFKAL80ETC concert"))) + (if (> quality 0) + (if (not (equalp name "Sulfuras, Hand of Ragnaros")) + (setf quality (- quality 1)))) + (when (< quality 50) + (setf quality (+ quality 1)) + (when (equalp name "Backstage passes to a TAFKAL80ETC concert") + (if (< sell-in 11) + (if (< quality 50) + (setf quality (+ quality 1)))) + (if (< sell-in 6) + (if (< quality 50) + (setf quality (+ quality 1))))))) + + (if (not (equalp name "Sulfuras, Hand of Ragnaros")) + (setf sell-in (- sell-in 1))) + + (if (< sell-in 0) + (if (not (equalp name "Aged Brie")) + (if (not (equalp name "Backstage passes to a TAFKAL80ETC concert")) + (if (> quality 0) + (if (not (equalp name "Sulfuras, Hand of Ragnaros")) + (setf quality (- quality 1)))) + (setf quality (- quality quality))) + (if (< quality 50) + (setf quality (+ quality 1)))))) + new-item)) + +;;; Example + +(defun to-string (item) + (with-slots (name quality sell-in) item + (format nil "~a, ~a, ~a" name sell-in quality))) + +(defun print-day (day items) + (format t "-------- day ~a --------~%" day) + (format t "name, sell-in, quality~%") + (dolist (item items) + (write-line (to-string item))) + (terpri)) + +(defun run-gilded-rose (days) + (write-line "OMGHAI!") + (let* ((descriptions '(("+5 Dexterity Vest" 10 20) + ("Aged Brie" 2 0) + ("Elixir of the Mongoose" 5 7) + ("Sulfuras, Hand of Ragnaros" 0 80) + ("Sulfuras, Hand of Ragnaros" -1 80) + ("Backstage passes to a TAFKAL80ETC concert" 15 20) + ("Backstage passes to a TAFKAL80ETC concert" 10 49) + ("Backstage passes to a TAFKAL80ETC concert" 5 49) + ;; this conjured item does not work properly yet + ("Conjured Mana Cake" 3 6))) + (items (loop :for (name sell-in quality) :in descriptions + :collect (make-item :name name + :sell-in sell-in + :quality quality)))) + (loop :for day :upto days + :with the-items = items + :do (progn + (print-day day the-items) + (setf the-items (update-quality the-items)))))) + +;;; ================================================================ diff --git a/commonlisp/gilded-rose.lisp b/commonlisp/gilded-rose.lisp new file mode 100644 index 00000000..e047ffb6 --- /dev/null +++ b/commonlisp/gilded-rose.lisp @@ -0,0 +1,131 @@ +;; Hi and welcome to team Gilded Rose. As you know, we are a small inn +;; with a prime location in a prominent city ran by a friendly +;; innkeeper named Allison. We also buy and sell only the finest goods. +;; Unfortunately, our goods are constantly degrading in quality as they +;; approach their sell by date. We have a system in place that updates +;; our inventory for us. It was developed by a no-nonsense type named +;; Leeroy, who has moved on to new adventures. Your task is to add the +;; new feature to our system so that we can begin selling a new +;; category of items. + +;; First an introduction to our system: +;; All items have a SellIn value which denotes the number of days we have to sell the item +;; All items have a Quality value which denotes how valuable the item is +;; At the end of each day our system lowers both values for every item +;; Pretty simple, right? Well this is where it gets interesting: +;; Once the sell by date has passed, Quality degrades twice as fast +;; The Quality of an item is never negative +;; "Aged Brie" actually increases in Quality the older it gets +;; The Quality of an item is never more than 50 +;; "Sulfuras", being a legendary item, never has to be sold or decreases in Quality +;; "Backstage passes", like aged brie, increases in Quality as it's +;; SellIn value approaches; Quality increases by 2 when there are 10 +;; days or less and by 3 when there are 5 days or less but Quality +;; drops to 0 after the concert + +;; We have recently signed a supplier of conjured items. This requires an update to our system: +;; "Conjured" items degrade in Quality twice as fast as normal items +;; Feel free to make any changes to the UpdateQuality method and add +;; any new code as long as everything still works correctly. However, +;; do not alter the Item class or Items property as those belong to the +;; goblin in the corner who will insta-rage and one-shot you as he +;; doesn't believe in shared code ownership (you can make the +;; UpdateQuality method and Items property static if you like, we'll +;; cover for you). +;; Just for clarification, an item can never have its Quality increase +;; above 50, however "Sulfuras" is a legendary item and as such its +;; Quality is 80 and it never alters. + +;; https://github.com/emilybache/GildedRose-Refactoring-Kata + +;; Common Lisp version: Rainer Joswig, joswig@lisp.de, 2016 +;; Minor modifications (independent of CL impl.): Manfred Bergmann, 2022 + +;;; ================================================================ +;;; Code + +(defpackage :gilded-rose + (:use :cl)) + +(in-package :gilded-rose) + + +;;; Class ITEM + +(defclass item () + ((name :initarg :name :type string) + (sell-in :initarg :sell-in :type integer) + (quality :initarg :quality :type integer))) + +(defmethod to-string ((i item)) + (with-slots (name quality sell-in) i + (format nil "~a, ~a, ~a" name sell-in quality))) + +;;; Class gilded-rose + +(defclass gilded-rose () + ((items :initarg :items))) + +(defmethod update-quality ((gr gilded-rose)) + (with-slots (items) gr + (dotimes (i (length items)) + (with-slots (name quality sell-in) + (elt items i) + (if (and (not (equalp name "Aged Brie")) + (not (equalp name "Backstage passes to a TAFKAL80ETC concert"))) + (if (> quality 0) + (if (not (equalp name "Sulfuras, Hand of Ragnaros")) + (setf quality (- quality 1)))) + (when (< quality 50) + (setf quality (+ quality 1)) + (when (equalp name "Backstage passes to a TAFKAL80ETC concert") + (if (< sell-in 11) + (if (< quality 50) + (setf quality (+ quality 1)))) + (if (< sell-in 6) + (if (< quality 50) + (setf quality (+ quality 1))))))) + + (if (not (equalp name "Sulfuras, Hand of Ragnaros")) + (setf sell-in (- sell-in 1))) + + (if (< sell-in 0) + (if (not (equalp name "Aged Brie")) + (if (not (equalp name "Backstage passes to a TAFKAL80ETC concert")) + (if (> quality 0) + (if (not (equalp name "Sulfuras, Hand of Ragnaros")) + (setf quality (- quality 1)))) + (setf quality (- quality quality))) + (if (< quality 50) + (setf quality (+ quality 1))))))))) + +;;; Example + +(defun run-gilded-rose (days) + (write-line "OMGHAI!") + (let* ((descriptions '(("+5 Dexterity Vest" 10 20) + ("Aged Brie" 2 0) + ("Elixir of the Mongoose" 5 7) + ("Sulfuras, Hand of Ragnaros" 0 80) + ("Sulfuras, Hand of Ragnaros" -1 80) + ("Backstage passes to a TAFKAL80ETC concert" 15 20) + ("Backstage passes to a TAFKAL80ETC concert" 10 49) + ("Backstage passes to a TAFKAL80ETC concert" 5 49) + ;; this conjured item does not work properly yet + ("Conjured Mana Cake" 3 6))) + (items (loop :for (name sell-in quality) :in descriptions + :collect (make-instance 'item + :name name + :sell-in sell-in + :quality quality))) + (app (make-instance 'gilded-rose :items items))) + (dotimes (i days) + (format t "-------- day ~a --------~%" i) + (format t "name, sell-in, quality~%") + (dolist (item items) + (write-line (to-string item))) + (terpri) + (update-quality app)))) + +;;; ================================================================ +;;; EOF diff --git a/cpp-catch2/.gitignore b/cpp-catch2/.gitignore new file mode 100644 index 00000000..401f2947 --- /dev/null +++ b/cpp-catch2/.gitignore @@ -0,0 +1,4 @@ +/build_meson/ +/subprojects/Catch2-*/ +/subprojects/packagecache/ +/cmake-build-*/ diff --git a/cpp-catch2/CMakeLists.txt b/cpp-catch2/CMakeLists.txt new file mode 100644 index 00000000..0617ad9c --- /dev/null +++ b/cpp-catch2/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.14..3.16) +set(CMAKE_VERBOSE_MAKEFILE ON) +project(GildedRoseKata VERSION 1.0 + DESCRIPTION "The GildedRose Refactoring kata for an approval testing approach" + LANGUAGES CXX) +include(FetchContent) + +FetchContent_Declare( + catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v2.13.10 +) +FetchContent_MakeAvailable(catch2) +LIST(APPEND CMAKE_MODULE_PATH + ${catch2_SOURCE_DIR}/contrib + ) + +FetchContent_Declare( + approvaltests_ho + URL https://github.com/approvals/ApprovalTests.cpp/releases/download/v.10.13.0/ApprovalTests.v.10.13.0.hpp + DOWNLOAD_NO_EXTRACT TRUE + DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR}/approvaltests + DOWNLOAD_NAME ApprovalTests.v.10.13.0.hpp +) +FetchContent_GetProperties(approvaltests_ho) +if (NOT approvaltests_ho_POPULATED) + FetchContent_Populate(approvaltests_ho) +endif () +add_library(approvaltests INTERFACE) +target_include_directories(approvaltests + INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/approvaltests + INTERFACE ${catch2_SOURCE_DIR}/single_include/catch2 + ) + +if( MINGW ) + # https://stackoverflow.com/questions/31890021/mingw-too-many-sections-bug-while-compiling-huge-header-file-in-qt + set (CMAKE_CXX_FLAGS "-Wa,-mbig-obj ") +endif() + +add_executable(gildedrose_catch2 + src/GildedRose.h + src/GildedRose.cc + test/gildedrose_catch.cpp + test/main.cpp) +set_target_properties(gildedrose_catch2 PROPERTIES CXX_STANDARD 11) +target_include_directories(gildedrose_catch2 + PUBLIC src) +target_link_libraries(gildedrose_catch2 Catch2::Catch2 approvaltests) + + + +include(CTest) +include(ParseAndAddCatchTests) +ParseAndAddCatchTests(gildedrose_catch2) diff --git a/cpp-catch2/README.md b/cpp-catch2/README.md new file mode 100644 index 00000000..ce8bfb01 --- /dev/null +++ b/cpp-catch2/README.md @@ -0,0 +1,21 @@ +C++ version of Gilded Rose with Catch 2 and Approvals +====================================================== + +This is a C++ start of the Gilded Rose Refactoring Kata. See +the [top level readme](https://github.com/emilybache/GildedRose-Refactoring-Kata) + for a general description of the exercise. + +There are two (failing) unit tests included here. One uses only the catch2 framework, the other additionally uses [ApprovalTests](https://github.com/approvals/approvaltests.cpp). You should choose one of these tests to work with and delete the other. + +CMake +----- + +CMake is included in CLion from JetBrains. Without CMake files +CLion has a hard time to handle c-projects. + +To install CMake (if you don't use CLion) on macOS using brew + + brew install cmake + +Tested on CMake 3.15.3 (included with CLion 2019.3) on macOS + diff --git a/cpp-catch2/src/GildedRose.cc b/cpp-catch2/src/GildedRose.cc new file mode 100644 index 00000000..8df23e47 --- /dev/null +++ b/cpp-catch2/src/GildedRose.cc @@ -0,0 +1,80 @@ +#include "GildedRose.h" + +GildedRose::GildedRose(vector & items) : items(items) +{} + +void GildedRose::updateQuality() +{ + for (int i = 0; i < items.size(); i++) + { + if (items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert") + { + if (items[i].quality > 0) + { + if (items[i].name != "Sulfuras, Hand of Ragnaros") + { + items[i].quality = items[i].quality - 1; + } + } + } + else + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + + if (items[i].name == "Backstage passes to a TAFKAL80ETC concert") + { + if (items[i].sellIn < 11) + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + + if (items[i].sellIn < 6) + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + } + } + } + + if (items[i].name != "Sulfuras, Hand of Ragnaros") + { + items[i].sellIn = items[i].sellIn - 1; + } + + if (items[i].sellIn < 0) + { + if (items[i].name != "Aged Brie") + { + if (items[i].name != "Backstage passes to a TAFKAL80ETC concert") + { + if (items[i].quality > 0) + { + if (items[i].name != "Sulfuras, Hand of Ragnaros") + { + items[i].quality = items[i].quality - 1; + } + } + } + else + { + items[i].quality = items[i].quality - items[i].quality; + } + } + else + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + } + } +} diff --git a/cpp-catch2/src/GildedRose.h b/cpp-catch2/src/GildedRose.h new file mode 100644 index 00000000..8464f87b --- /dev/null +++ b/cpp-catch2/src/GildedRose.h @@ -0,0 +1,24 @@ +#include +#include + +using namespace std; + +class Item +{ +public: + string name; + int sellIn; + int quality; + Item(string name, int sellIn, int quality) : name(name), sellIn(sellIn), quality(quality) + {} +}; + +class GildedRose +{ +public: + vector & items; + GildedRose(vector & items); + + void updateQuality(); +}; + diff --git a/cpp-catch2/test/ApprovalTests.hpp b/cpp-catch2/test/ApprovalTests.hpp new file mode 100644 index 00000000..5126d99f --- /dev/null +++ b/cpp-catch2/test/ApprovalTests.hpp @@ -0,0 +1 @@ +#include "ApprovalTests.v.10.13.0.hpp" diff --git a/cpp-catch2/test/gildedrose_catch.cpp b/cpp-catch2/test/gildedrose_catch.cpp new file mode 100644 index 00000000..af562b40 --- /dev/null +++ b/cpp-catch2/test/gildedrose_catch.cpp @@ -0,0 +1,32 @@ +#include +#include "ApprovalTests.hpp" + +#include "GildedRose.h" + +std::ostream& operator<<(std::ostream& os, const Item& obj) +{ + return os + << "name: " << obj.name + << ", sellIn: " << obj.sellIn + << ", quality: " << obj.quality; +} + +// This is a normal unit test using Catch2 +TEST_CASE("UpdateQuality") { + + vector items; + items.push_back(Item("foo", 0, 0)); + GildedRose app(items); + app.updateQuality(); + REQUIRE("fixme" == app.items[0].name); +} + +// This is an Approval test using https://github.com/approvals/approvaltests.cpp +TEST_CASE("UpdateQualityApprovalTest") { + vector items; + items.push_back(Item("foo", 0, 0)); + GildedRose app(items); + app.updateQuality(); + auto item = app.items[0]; + ApprovalTests::Approvals::verify(item); +} diff --git a/cpp-catch2/test/main.cpp b/cpp-catch2/test/main.cpp new file mode 100644 index 00000000..94bae37b --- /dev/null +++ b/cpp-catch2/test/main.cpp @@ -0,0 +1,5 @@ +#define CATCH_CONFIG_MAIN +#define APPROVALS_CATCH + +#include "catch2/catch.hpp" +#include "ApprovalTests.hpp" \ No newline at end of file diff --git a/cpp/.gitignore b/cpp/.gitignore new file mode 100644 index 00000000..5974ee76 --- /dev/null +++ b/cpp/.gitignore @@ -0,0 +1,4 @@ +.idea +.vs +cmake-build-debug +build diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt new file mode 100644 index 00000000..bed20236 --- /dev/null +++ b/cpp/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.13) +project(gilded-rose-refactoring-kata-cpp) + +# Load FetchContent module. +include(FetchContent) + +# Declare GoogleTest as the content to fetch +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.12.1 +) + +FetchContent_Declare( + catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.3.2 +) + +FetchContent_MakeAvailable(googletest catch2) + +#set(gtest_force_shared_crt OFF CACHE BOOL "" FORCE) +# Force Google Test to link the C/C++ runtimes dynamically when +# building on Visual Studio +# Link: +# * https://github.com/google/googletest/tree/release-1.8.1/googletest#visual-studio-dynamic-vs-static-runtimes +if (MSVC) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +endif() + +# uncomment this line to enable coverage measurements using gcov +# set(CMAKE_CXX_FLAGS "--coverage") + +enable_testing() +add_subdirectory(src) +add_subdirectory(lib) +add_subdirectory(test) diff --git a/cpp/README.md b/cpp/README.md new file mode 100644 index 00000000..8c9b61e0 --- /dev/null +++ b/cpp/README.md @@ -0,0 +1,68 @@ +# C++ version of Gilded Rose refactoring kata + +## Introduction +The C++ version of the Gilded Rose refactoring kata is available in four variants using different test frameworks: + +* Catch2 test framework + 1. Traditional unit test with the [Catch2](https://github.com/catchorg/Catch2) test framework in the `test/cpp_catch2_unittest` folder. + 2. [Approval tests](https://github.com/approvals/ApprovalTests.cpp) with the [Catch2](https://github.com/catchorg/Catch2) test framework in the `test/cpp_catch2_approvaltest` folder. +* Google Test framework + 1. Traditional unit test with the [Googletest](https://github.com/google/googletest) test framework in the `test/cpp_googletest_unittest` folder. + 2. [Approval tests](https://github.com/approvals/ApprovalTests.cpp) with the [Googletest](https://github.com/google/googletest) test framework in the `test/cpp_googletest_approvaltest` folder. + +The `GildedRose.cc` file, i.e. the code under test, is identical in all four variants. + +## Prerequisites + +* CMake version >= 3.13 +* C++ compiler that support C++11 + +## How to build and run tests in a terminal + +### Build tests + + $ cd ${GIT_FOLDER}/GildedRose-Refactoring-Kata/cpp + $ mkdir build + $ cd build + $ cmake .. + $ cmake --build . + +### Show available tests + + $ cd ${GIT_FOLDER}/GildedRose-Refactoring-Kata/cpp/build + $ ctest -N + Test project ${GIT_FOLDER}/GildedRose-Refactoring-Kata/cpp/build + Test #1: GildedRoseCatch2ApprovalTests + Test #2: GildedRoseCatch2UnitTests + Test #3: GildedRoseGoogletestApprovalTests + Test #4: GildedRoseGoogletestUnitTests + +### Run all tests + + $ ctest + +### Run all tests with verbose output + + $ ctest -VV + +### Run a specific test with verbose output + + $ ctest -VV --tests-regex Catch2Approval + +## How to build and run tests using the [CLion IDE](https://www.jetbrains.com/clion/) + +1. Start CLion +2. Select menu `File - Open...` +3. Select folder `${GIT_FOLDER}/GildedRose-Refactoring-Kata/cpp` +4. Select menu `Build - Build Project` +4. Select menu `Run - Run...` +4. Select what test variant to run, e.g. `GildedRoseCatch2ApprovalTests`. + +## How to build and run tests using Visual Studio 2019 + +1. Start Visual Studio 2019 +2. Select `Open a local folder` +3. Select folder `${GIT_FOLDER}/GildedRose-Refactoring-Kata/cpp` +4. Wait for message `CMake generation finished.` in the CMake output window at the bottom +5. Select what test variant to run in the drop down menu for Startup Items, e.g. `GildedRoseCatch2ApprovalTests.exe`. +6. Select menu `Debug - Start` diff --git a/cpp/lib/ApprovalTests.hpp b/cpp/lib/ApprovalTests.hpp new file mode 100644 index 00000000..6da64ef2 --- /dev/null +++ b/cpp/lib/ApprovalTests.hpp @@ -0,0 +1,4 @@ +#ifndef APPROVAL_TEST_1_APPROVALTESTS_HPP +#define APPROVAL_TEST_1_APPROVALTESTS_HPP +#include "ApprovalTests.v.6.0.0.hpp" +#endif diff --git a/cpp/lib/ApprovalTests.v.6.0.0.hpp b/cpp/lib/ApprovalTests.v.6.0.0.hpp new file mode 100644 index 00000000..a69b08e2 --- /dev/null +++ b/cpp/lib/ApprovalTests.v.6.0.0.hpp @@ -0,0 +1,2943 @@ +// Approval Tests version v.6.0.0 +// More information at: https://github.com/approvals/ApprovalTests.cpp +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + // ******************** From: Blocker.h +#ifndef APPROVALTESTS_CPP_BLOCKER_H +#define APPROVALTESTS_CPP_BLOCKER_H + +namespace ApprovalTests { +class Blocker +{ +public: + virtual ~Blocker() = default; + virtual bool isBlockingOnThisMachine() const = 0; +}; +} + +#endif + + // ******************** From: Macros.h +#ifndef APPROVALTESTS_CPP_MACROS_H +#define APPROVALTESTS_CPP_MACROS_H + + + + +#define APPROVAL_TESTS_UNUSED(expr) do { (void)(expr); } while (0) + +#if __cplusplus >= 201703L + #define APPROVAL_TESTS_NO_DISCARD [[nodiscard]] +#else + #define APPROVAL_TESTS_NO_DISCARD +#endif + +#endif + + // ******************** From: WinMinGWUtils.h +#ifndef APPROVALTESTS_CPP_WINMINGWUTILS_H +#define APPROVALTESTS_CPP_WINMINGWUTILS_H + +// + +#if (defined(__MINGW32__) || defined(__MINGW64__)) +#define APPROVAL_TESTS_MINGW +#endif + +#ifdef APPROVAL_TESTS_MINGW +#ifdef __cplusplus +extern "C" { +#endif + +#include /* errno_t, size_t */ + +errno_t getenv_s( + size_t *ret_required_buf_size, + char *buf, + size_t buf_size_in_bytes, + const char *name +); + +#ifdef __cplusplus +} +#endif + +#endif // APPROVAL_TESTS_MINGW + +// + +#endif + + // ******************** From: ApprovalWriter.h +#ifndef APPROVALTESTS_CPP_APPROVALWRITER_H +#define APPROVALTESTS_CPP_APPROVALWRITER_H + +namespace ApprovalTests { +class ApprovalWriter +{ +public: + virtual ~ApprovalWriter() = default; + virtual std::string getFileExtensionWithDot() const = 0; + virtual void write(std::string path) const = 0; + virtual void cleanUpReceived(std::string receivedPath) const = 0; +}; +} + +#endif + + // ******************** From: StringWriter.h +#ifndef APPROVALTESTS_CPP_STRINGWRITER_H +#define APPROVALTESTS_CPP_STRINGWRITER_H + + +namespace ApprovalTests { +class StringWriter : public ApprovalWriter +{ +private: + std::string s; + std::string ext; + +public: + explicit StringWriter( std::string contents, std::string fileExtensionWithDot = ".txt" ) + : s(std::move(contents)), ext(std::move(fileExtensionWithDot)) {} + + std::string getFileExtensionWithDot() const override + { + return ext; + } + + void write( std::string path ) const override + { + std::ofstream out( path.c_str(), std::ofstream::out ); + if ( ! out) + { + throw std::runtime_error("Unable to write file: " + path); + } + this->Write( out ); + out.close(); + } + + void Write( std::ostream &out ) const + { + out << s << "\n"; + } + + virtual void cleanUpReceived(std::string receivedPath) const override { + remove(receivedPath.c_str()); + } + + +}; +} +#endif + + // ******************** From: FileUtils.h + + + + +#ifndef APPROVALTESTS_CPP_FILEUTILS_H +#define APPROVALTESTS_CPP_FILEUTILS_H + + +namespace ApprovalTests { +class FileUtils { +public: + static bool fileExists(const std::string& path) + { + struct stat info{}; + return stat( path.c_str(), &info ) == 0; + } + + static int fileSize(const std::string& path) { + struct stat statbuf{}; + int stat_ok = stat(path.c_str(), &statbuf); + + if (stat_ok == -1) { + return -1; + } + + return int(statbuf.st_size); + } + + static void ensureFileExists(const std::string& fullFilePath) { + if (!fileExists(fullFilePath)) { + StringWriter s("", ""); + s.write(fullFilePath); + } + } + + static std::string getExtensionWithDot(const std::string& filePath) { + std::size_t found = filePath.find_last_of('.'); + return filePath.substr(found); + } + + static void writeToFile(const std::string& filePath, const std::string& content) + { + std::ofstream out(filePath.c_str(), std::ios::binary | std::ofstream::out); + if ( ! out) + { + throw std::runtime_error("Unable to write file: " + filePath); + } + out << content; + } +}; +} + +#endif + + // ******************** From: StringUtils.h + + +#ifndef APPROVALTESTS_CPP_STRINGUTILS_H +#define APPROVALTESTS_CPP_STRINGUTILS_H + + +namespace ApprovalTests { +class StringUtils +{ +public: + static std::string replaceAll(std::string inText, const std::string& find, const std::string& replaceWith) { + size_t start_pos = 0; + while ((start_pos = inText.find(find, start_pos)) != std::string::npos) { + inText.replace(start_pos, find.length(), replaceWith); + start_pos += replaceWith.length(); + } + return inText; + } + + static bool contains(const std::string& inText, const std::string& find) + { + return inText.find(find, 0) != std::string::npos; + } + + static std::string toLower(std::string inText) + { + std::string copy(inText); + std::transform(inText.begin(), inText.end(), copy.begin(), + [](char c){ return static_cast(tolower(c)); }); + return copy; + } + + static bool endsWith(std::string value, std::string ending) + { + if (ending.size() > value.size()) + { + return false; + } + return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); + } + + template + static std::string toString(const T& contents) + { + std::stringstream s; + s << contents; + return s.str(); + } + +}; +} +#endif + + // ******************** From: SystemUtils.h +#ifndef APPROVALTESTS_CPP_SYSTEMUTILS_H +#define APPROVALTESTS_CPP_SYSTEMUTILS_H + +// +#ifdef _WIN32 + // ReSharper disable once CppUnusedIncludeDirective + #include + #include + #include +#else + // ReSharper disable once CppUnusedIncludeDirective + #include +#endif +// + + + +namespace ApprovalTests { +class SystemUtils +{ +public: + static bool isWindowsOs() + { +#ifdef _WIN32 + return true; +#else + return false; +#endif + + } + + static bool isCygwin() + { +#ifdef __CYGWIN__ + return true; +#else + return false; +#endif + } + + static std::string getDirectorySeparator() + { + return isWindowsOs() ? "\\" : "/"; + } + + + static std::string checkFilenameCase(const std::string& fullPath) + { + if (!isWindowsOs() || !FileUtils::fileExists(fullPath)) + { + return fullPath; + } +#ifdef _WIN32 + + WIN32_FIND_DATAA findFileData; + HANDLE hFind = FindFirstFileA(fullPath.c_str(), &findFileData); + + if (hFind != INVALID_HANDLE_VALUE) + { + const std::string fixedFilename = findFileData.cFileName; + const std::string fixedPath = + StringUtils::replaceAll( fullPath, StringUtils::toLower(fixedFilename), fixedFilename ); + FindClose(hFind); + return fixedPath; + } + + +#endif + return fullPath; + + } + + static std::string safeGetEnvForWindows(char const *name) + { + APPROVAL_TESTS_UNUSED(name); +#ifdef _WIN32 + + + + + size_t size; + getenv_s(&size, nullptr, 0, name); + + if (size != 0) + { + std::string result; + result.resize(size); + getenv_s(&size, &*result.begin(), size, name); + result.pop_back(); + return result; + } +#endif + return std::string(); + } + + static std::string safeGetEnvForNonWindows(char const *name) + { + APPROVAL_TESTS_UNUSED(name); + char* p = nullptr; +#ifndef _WIN32 + p = getenv(name); +#endif + return (p != nullptr) ? p : std::string(); + } + + + static std::string safeGetEnv(char const *name) + { + return isWindowsOs() ? safeGetEnvForWindows(name) : safeGetEnvForNonWindows(name); + } + + static std::string getMachineName() + { + auto name = safeGetEnv("COMPUTERNAME"); + if ( ! name.empty()) + { + return name; + } + + name = safeGetEnv("HOSTNAME"); + if ( ! name.empty()) + { + return name; + } + + return "Unknown Computer"; + } + + static void makeDirectoryForWindows(const std::string& directory) + { + APPROVAL_TESTS_UNUSED(directory); +#ifdef _WIN32 + int nError = _mkdir(directory.c_str()); + if (nError != 0) + { + std::string helpMessage = std::string("Unable to create directory: ") + directory; + throw std::runtime_error( helpMessage ); + } +#endif + } + + static void makeDirectoryForNonWindows(const std::string& directory) + { + APPROVAL_TESTS_UNUSED(directory); +#ifndef _WIN32 + mode_t nMode = 0733; + int nError = mkdir(directory.c_str(),nMode); + if (nError != 0) + { + std::string helpMessage = std::string("Unable to create directory: ") + directory; + throw std::runtime_error( helpMessage ); + } +#endif + } + + static void makeDirectory(const std::string& directory) + { + makeDirectoryForWindows(directory); + makeDirectoryForNonWindows(directory); + } + + static void ensureDirectoryExists(const std::string& fullFilePath) + { + if (!FileUtils::fileExists(fullFilePath)) + { + makeDirectory(fullFilePath); + } + } +}; +} +#endif + + // ******************** From: MachineBlocker.h +#ifndef APPROVALTESTS_CPP_MACHINEBLOCKER_H +#define APPROVALTESTS_CPP_MACHINEBLOCKER_H + + +namespace ApprovalTests { +class MachineBlocker : public Blocker +{ +private: + std::string machineName; + bool block; + + MachineBlocker() = delete; + +public: + MachineBlocker(std::string machineName, bool block ) : machineName(std::move(machineName)), block(block) + { + } + + static MachineBlocker onMachineNamed( const std::string& machineName ) + { + return MachineBlocker(machineName, true); + } + + static MachineBlocker onMachinesNotNamed( const std::string& machineName ) + { + return MachineBlocker(machineName, false); + } + + virtual bool isBlockingOnThisMachine() const override + { + const auto isMachine = (SystemUtils::getMachineName() == machineName); + return isMachine == block; + } +}; +} + +#endif + + // ******************** From: FileUtilsSystemSpecific.h +#ifndef APPROVALTESTS_CPP_FILEUTILSSYSTEMSPECIFIC_H +#define APPROVALTESTS_CPP_FILEUTILSSYSTEMSPECIFIC_H + + +namespace ApprovalTests { +class FileUtilsSystemSpecific +{ +public: + static std::string getCommandLineForCopy(const std::string& source, const std::string& destination, bool isWindows) + { + if (isWindows) { + return std::string("copy /Y ") + "\"" + source + "\" \"" + destination + "\""; + } else { + return std::string("cp ") + "\"" + source + "\" \"" + destination + "\""; + } + } + + static void copyFile(const std::string& source, const std::string& destination ) + { + system( getCommandLineForCopy(source, destination, SystemUtils::isWindowsOs()).c_str() ); + } +}; +} +#endif + + // ******************** From: Reporter.h +#ifndef APPROVALTESTS_CPP_REPORTER_H +#define APPROVALTESTS_CPP_REPORTER_H + + +namespace ApprovalTests { + +class Reporter { +public: + virtual ~Reporter() = default; + virtual bool report(std::string received, std::string approved) const = 0; +}; + + +template +using IsNotDerivedFromReporter = typename std::enable_if::value, int>::type; +} + +#endif + + // ******************** From: AutoApproveReporter.h +#ifndef APPROVALTESTS_CPP_AUTOAPPROVEREPORTER_H +#define APPROVALTESTS_CPP_AUTOAPPROVEREPORTER_H + + + +namespace ApprovalTests { +class AutoApproveReporter : public Reporter +{ +public: + bool report(std::string received, std::string approved) const override + { + std::cout << "file " << approved << " automatically approved - next run should succeed\n"; + FileUtilsSystemSpecific::copyFile( received, approved ); + return true; + } +}; +} + +#endif + + // ******************** From: ApprovalNamer.h +#ifndef APPROVALTESTS_CPP_APPROVALNAMER_H +#define APPROVALTESTS_CPP_APPROVALNAMER_H + + +namespace ApprovalTests { +class ApprovalNamer +{ +public: + virtual ~ApprovalNamer() = default; + virtual std::string getApprovedFile(std::string extensionWithDot) const = 0; + virtual std::string getReceivedFile(std::string extensionWithDot) const = 0; + +}; +} + +#endif + + // ******************** From: ApprovalTestNamer.h +#ifndef APPROVALTESTS_CPP_APPROVALTESTNAMER_H +#define APPROVALTESTS_CPP_APPROVALTESTNAMER_H + + +namespace ApprovalTests { +class TestName { +public: + const std::string& getFileName() const { + return fileName; + } + + void setFileName(const std::string &file) { + fileName = SystemUtils::checkFilenameCase(file); + } + + std::vector sections; +private: + std::string fileName; +}; + +class TestConfiguration { +public: + std::string subdirectory; +}; + +class ApprovalTestNamer : public ApprovalNamer { +private: +public: + ApprovalTestNamer() = default; + + std::string getTestName() const { + std::stringstream ext; + auto test = getCurrentTest(); + for (size_t i = 0; i < test.sections.size(); i++) { + if (0 < i) { + ext << "."; + } + ext << test.sections[i]; + } + + return convertToFileName(ext.str()); + } + + static bool isForbidden(char c) + { + static std::string forbiddenChars("\\/:?\"<>|' "); + return std::string::npos != forbiddenChars.find(c); + } + + static std::string convertToFileName(const std::string& fileName) + { + std::stringstream result; + for (auto ch : fileName) + { + if (!isForbidden(ch)) + { + result << ch; + } + else + { + result << "_"; + } + } + return result.str(); + } + + static TestName &getCurrentTest() + { + try + { + return currentTest(); + } + catch( const std::runtime_error& ) + { + std::string helpMessage = getMisconfiguredMainHelp(); + throw std::runtime_error( helpMessage ); + } + } + +// + static std::string getMisconfiguredMainHelp() + { + std::string lineBreak = "************************************************************************************n"; + std::string lineBuffer = "* *n"; + std::string helpMessage = + "nn" + lineBreak + lineBuffer + +R"(* Welcome to Approval Tests. +* +* You have forgotten to configure your test framework for Approval Tests. +* +* To do this in Catch, add the following to your main.cpp: +* +* #define APPROVALS_CATCH +* #include "ApprovalTests.hpp" +* +* To do this in Google Test, add the following to your main.cpp: +* +* #define APPROVALS_GOOGLETEST +* #include "ApprovalTests.hpp" +* +* To do this in doctest, add the following to your main.cpp: +* +* #define APPROVALS_DOCTEST +* #include "ApprovalTests.hpp" +* +* For more information, please visit: +* https://github.com/approvals/ApprovalTests.cpp/blob/master/doc/GettingStarted.md +)" + + lineBuffer + lineBreak + 'n'; + return helpMessage; + } +// + + + + std::string getFileName() const { + return getSourceFileName(); + } + + + std::string getSourceFileName() const { + auto file = getCurrentTest().getFileName(); + auto start = file.rfind(SystemUtils::getDirectorySeparator()) + 1; + auto end = file.rfind('.'); + auto fileName = file.substr(start, end - start); + return convertToFileName(fileName); + } + + std::string getDirectory() const { + auto file = getCurrentTest().getFileName(); + auto end = file.rfind(SystemUtils::getDirectorySeparator()) + 1; + auto directory = file.substr(0, end); + if ( ! testConfiguration().subdirectory.empty() ) + { + directory += testConfiguration().subdirectory + SystemUtils::getDirectorySeparator(); + SystemUtils::ensureDirectoryExists(directory); + } + return directory; + } + + static TestName& currentTest(TestName* value = nullptr) + { + static TestName* staticValue; + if (value != nullptr) + { + staticValue = value; + } + if ( staticValue == nullptr ) + { + throw std::runtime_error("The variable in currentTest() is not initialised"); + } + return *staticValue; + } + + static TestConfiguration& testConfiguration() + { + static TestConfiguration configuration; + return configuration; + } + + virtual std::string getApprovedFile(std::string extensionWithDot) const override { + + return getFullFileName(".approved", extensionWithDot); + } + + virtual std::string getReceivedFile(std::string extensionWithDot) const override { + + return getFullFileName(".received", extensionWithDot); + } + + std::string getOutputFileBaseName() const { + return getSourceFileName() + "." + getTestName(); + } + + std::string getFullFileName(const std::string& approved, const std::string& extensionWithDot) const { + std::stringstream ext; + ext << getDirectory() << getOutputFileBaseName() << approved << extensionWithDot; + return ext.str(); + } +}; +} + +#endif + + // ******************** From: SectionNameDisposer.h +#ifndef APPROVALTESTS_CPP_SECTIONNAMEDISPOSER_H +#define APPROVALTESTS_CPP_SECTIONNAMEDISPOSER_H + + +namespace ApprovalTests { +class APPROVAL_TESTS_NO_DISCARD SectionNameDisposer +{ +public: + SectionNameDisposer(TestName& currentTest, const std::string& scope_name) : + currentTest(currentTest) + { + + + currentTest.sections.push_back(scope_name); + } + + ~SectionNameDisposer() + { + + currentTest.sections.pop_back(); + } +private: + TestName& currentTest; +}; +} + +#endif + + // ******************** From: GoogleCustomizationsFactory.h +#ifndef APPROVALTESTS_CPP_GOOGLECUSTOMIZATIONSFACTORY_H +#define APPROVALTESTS_CPP_GOOGLECUSTOMIZATIONSFACTORY_H + + + +namespace ApprovalTests { +class GoogleCustomizationsFactory +{ +public: + using Comparator = std::function; +private: + using ComparatorContainer = std::vector< Comparator >; + static ComparatorContainer& comparatorContainer() + { + static ComparatorContainer container; + if (container.empty()) + { + auto exactNameMatching = [](const std::string& testFileNameWithExtension, const std::string& testCaseName) + { + return StringUtils::contains(testFileNameWithExtension, testCaseName + "."); + }; + container.push_back( exactNameMatching ); + } + return container; + } + +public: + static ComparatorContainer getEquivalencyChecks() + { + return comparatorContainer(); + } + + APPROVAL_TESTS_NO_DISCARD static bool addTestCaseNameRedundancyCheck(const Comparator& comparator) + { + comparatorContainer().push_back(comparator); + return true; + } + + +}; +} + +#endif + + // ******************** From: CartesianProduct.h +#ifndef APPROVALTESTS_CPP_CARTESIANPRODUCT_H +#define APPROVALTESTS_CPP_CARTESIANPRODUCT_H + + +namespace ApprovalTests { +namespace CartesianProduct { +namespace Detail { + + + +template +using enable_if_t = typename std::enable_if::type; + + +template +struct index_sequence {}; + +template +struct make_index_sequence : make_index_sequence {}; + +template +struct make_index_sequence<0, Is...> : index_sequence {}; + + + + +template +constexpr std::size_t tuple_size() { + return std::tuple_size::type>::value; +} + +template +using make_tuple_idxs = make_index_sequence()>; + + + +template +constexpr auto apply_impl(F&& f, Tuple&& t, index_sequence) + -> decltype(std::forward(f)(std::get(std::forward(t))...)) +{ + return std::forward(f)(std::get(std::forward(t))...); +} + +template +auto apply(F&& f, Tuple&& t) + -> decltype(apply_impl(std::forward(f), std::forward(t), make_tuple_idxs{})) +{ + apply_impl(std::forward(f), std::forward(t), make_tuple_idxs{}); +} + + +template +void for_each_impl(Tuple&& t, F&& f, index_sequence) { + (void)std::initializer_list{ + (std::forward(f)(std::get(std::forward(t))), 0)... + }; +} + +template +void for_each(Tuple&& t, F&& f) { + for_each_impl(std::forward(t), std::forward(f), make_tuple_idxs{}); +} + +template +auto transform_impl(Tuple&& t, F&& f, index_sequence) + -> decltype(std::make_tuple(std::forward(f)(std::get(std::forward(t)))...)) +{ + return std::make_tuple(std::forward(f)(std::get(std::forward(t)))...); +} + +template +auto transform(Tuple&& t, F&& f = {}) + -> decltype(transform_impl(std::forward(t), std::forward(f), make_tuple_idxs{})) +{ + return transform_impl(std::forward(t), std::forward(f), make_tuple_idxs{}); +} + +template +struct find_if_body { + const Predicate& pred; + std::size_t& index; + std::size_t currentIndex = 0; + bool found = false; + + find_if_body(const Predicate& p, std::size_t& i) : pred(p), index(i) {} + + template + void operator()(T&& value) { + if (found) return; + if (pred(std::forward(value))) { + index = currentIndex; + found = true; + } + ++currentIndex; + } +}; + +template +std::size_t find_if(Tuple&& tuple, Predicate pred = {}) { + std::size_t idx = tuple_size(); + for_each(std::forward(tuple), find_if_body(pred, idx)); + return idx; +} + +template +bool any_of(Tuple&& tuple, Predicate pred = {}) { + return find_if(std::forward(tuple), pred) != tuple_size(); +} + +struct is_range_empty { + template + bool operator()(const T& range) const { + using std::begin; + using std::end; + return begin(range) == end(range); + } +}; + + +struct dereference_iterator { + template + auto operator()(It&& it) const -> decltype(*std::forward(it)) { + return *std::forward(it); + } +}; + + +template()-1> +enable_if_t +increment_iterator(Its& it, const Its&, const Its&) { + ++std::get(it); +} + + +template()-1> +enable_if_t +increment_iterator(Its& its, const Its& begins, const Its& ends) { + if (++std::get(its) == std::get(ends)) { + std::get(its) = std::get(begins); + increment_iterator(its, begins, ends); + } +} +} + + + + + +template +void cartesian_product(F&& f, const Ranges&... ranges) { + using std::begin; + using std::end; + + if (Detail::any_of(std::forward_as_tuple(ranges...))) + return; + + const auto begins = std::make_tuple(begin(ranges)...); + const auto ends = std::make_tuple(end(ranges)...); + + for (auto its = begins; std::get<0>(its) != std::get<0>(ends); Detail::increment_iterator(its, begins, ends)) { + + + + + Detail::apply(std::forward(f), Detail::transform(its)); + } +} +} +} + +#endif + + // ******************** From: ExistingFile.h +#ifndef APPROVALTESTS_CPP_EXISTINGFILE_H +#define APPROVALTESTS_CPP_EXISTINGFILE_H + + + +namespace ApprovalTests { +class ExistingFile : public ApprovalWriter{ + std::string filePath; +public: + explicit ExistingFile(std::string filePath) : filePath(std::move(filePath)){} + virtual std::string getFileExtensionWithDot() const override { + return FileUtils::getExtensionWithDot(filePath); + } + virtual void write(std::string ) const override { + + } + virtual void cleanUpReceived(std::string ) const override { + + } +}; +} + +#endif + + // ******************** From: CommandLauncher.h +#ifndef APPROVALTESTS_CPP_COMMANDLAUNCHER_H +#define APPROVALTESTS_CPP_COMMANDLAUNCHER_H + + +namespace ApprovalTests { + +class CommandLauncher +{ +public: + virtual ~CommandLauncher() = default; + virtual bool launch(std::vector argv) = 0; +}; +} + +#endif + + // ******************** From: SystemLauncher.h + +#ifndef APPROVALTESTS_CPP_SYSTEMLAUNCHER_H +#define APPROVALTESTS_CPP_SYSTEMLAUNCHER_H + + +namespace ApprovalTests { + using ConvertArgumentsFunctionPointer = std::vector(*)(std::vector); + +class SystemLauncher : public CommandLauncher +{ +private: + ConvertArgumentsFunctionPointer convertArgumentsForSystemLaunching; +public: + SystemLauncher() : SystemLauncher(doNothing) + { + } + + explicit SystemLauncher(std::vector (*pointer)(std::vector)) : convertArgumentsForSystemLaunching(pointer) + { + } + + + void setConvertArgumentsForSystemLaunchingFunction(ConvertArgumentsFunctionPointer function) + { + convertArgumentsForSystemLaunching = function; + } + + bool exists(const std::string& command) + { + bool foundByWhich = false; + if (!SystemUtils::isWindowsOs()) { + std::string which = "which " + command + " > /dev/null 2>&1"; + int result = system(which.c_str()); + foundByWhich = (result == 0); + } + return foundByWhich || FileUtils::fileExists(command); + + } + + static std::vector doNothing(std::vector argv) + { + return argv; + } + + bool launch(std::vector argv) override + { + if (!exists(argv.front())) + { + return false; + } + + argv = convertArgumentsForSystemLaunching(argv); + + std::string command = std::accumulate(argv.begin(), argv.end(), std::string(""), [](const std::string& a, const std::string& b) {return a + " " + "\"" + b + "\""; }); + std::string launch = SystemUtils::isWindowsOs() ? ("start \"\" " + command) : (command + " &"); + system(launch.c_str()); + return true; + } +}; +} + +#endif + + // ******************** From: CommandReporter.h +#ifndef APPROVALTESTS_CPP_COMMANDREPORTER_H +#define APPROVALTESTS_CPP_COMMANDREPORTER_H + + +namespace ApprovalTests { + +class CommandReporter : public Reporter { +private: + std::string cmd; + CommandLauncher *l; + +protected: + CommandReporter(std::string command, CommandLauncher *launcher) + : cmd(std::move(command)), l(launcher) { + } + +public: + bool report(std::string received, std::string approved) const override { + FileUtils::ensureFileExists(approved); + return l->launch(getFullCommand(received, approved)); + } + + std::vector getFullCommand(const std::string &received, const std::string &approved) const + { + std::vector fullCommand; + fullCommand.push_back(cmd); + fullCommand.push_back(received); + fullCommand.push_back(approved); + return fullCommand; + } +}; +} +#endif + + // ******************** From: DiffInfo.h +#ifndef APPROVALTESTS_CPP_DIFFINFO_H +#define APPROVALTESTS_CPP_DIFFINFO_H + + +namespace ApprovalTests { +enum class Type { TEXT, IMAGE, TEXT_AND_IMAGE }; + + + +struct DiffInfo +{ + DiffInfo(std::string program, Type type) : + program(std::move(program)), + arguments("%s %s"), + type(type) + { + } + DiffInfo(std::string program, std::string arguments, Type type) : + program(std::move(program)), + arguments(std::move(arguments)), + type(type) + { + } + std::string program; + std::string arguments; + Type type; + + std::string getProgramForOs() const + { + std::string result = program; + if (result.rfind("{ProgramFiles}", 0) == 0) + { + const std::vector envVars = + { + "ProgramFiles", + "ProgramW6432", + "ProgramFiles(x86)" + }; + + for(const auto& envVar : envVars) + { + std::string envVarValue = SystemUtils::safeGetEnv(envVar); + if (envVarValue.empty()) + { + continue; + } + envVarValue += '\\'; + + auto result1 = StringUtils::replaceAll(result, "{ProgramFiles}", envVarValue); + if (FileUtils::fileExists(result1)) + { + return result1; + } + } + } + return result; + } +}; +} + +#endif + + // ******************** From: DiffPrograms.h +#ifndef APPROVALTESTS_CPP_DIFFPROGRAMS_H +#define APPROVALTESTS_CPP_DIFFPROGRAMS_H + + + +#define APPROVAL_TESTS_MACROS_ENTRY(name, defaultValue) \ + static DiffInfo name() { return defaultValue; } + + +namespace ApprovalTests { +namespace DiffPrograms { + + + namespace Mac { + APPROVAL_TESTS_MACROS_ENTRY(DIFF_MERGE, + DiffInfo("/Applications/DiffMerge.app/Contents/MacOS/DiffMerge", "%s %s -nosplash", Type::TEXT)) + + APPROVAL_TESTS_MACROS_ENTRY(BEYOND_COMPARE, DiffInfo("/Applications/Beyond Compare.app/Contents/MacOS/bcomp", Type::TEXT)) + + APPROVAL_TESTS_MACROS_ENTRY(KALEIDOSCOPE, DiffInfo("/Applications/Kaleidoscope.app/Contents/MacOS/ksdiff", Type::TEXT_AND_IMAGE)) + + APPROVAL_TESTS_MACROS_ENTRY(KDIFF3, DiffInfo("/Applications/kdiff3.app/Contents/MacOS/kdiff3", "%s %s -m", Type::TEXT)) + + APPROVAL_TESTS_MACROS_ENTRY(P4MERGE, DiffInfo("/Applications/p4merge.app/Contents/MacOS/p4merge", Type::TEXT_AND_IMAGE)) + + APPROVAL_TESTS_MACROS_ENTRY(TK_DIFF, DiffInfo("/Applications/TkDiff.app/Contents/MacOS/tkdiff", Type::TEXT)) + + APPROVAL_TESTS_MACROS_ENTRY(VS_CODE, DiffInfo("/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code", "-d %s %s", Type::TEXT)) + } + namespace Linux { + + APPROVAL_TESTS_MACROS_ENTRY(KDIFF3, DiffInfo("kdiff3", Type::TEXT)) + + APPROVAL_TESTS_MACROS_ENTRY(MELD, DiffInfo("meld", Type::TEXT)) + } + namespace Windows { + APPROVAL_TESTS_MACROS_ENTRY(BEYOND_COMPARE_3, DiffInfo("{ProgramFiles}Beyond Compare 3\\BCompare.exe", Type::TEXT_AND_IMAGE)) + + APPROVAL_TESTS_MACROS_ENTRY(BEYOND_COMPARE_4, DiffInfo("{ProgramFiles}Beyond Compare 4\\BCompare.exe", Type::TEXT_AND_IMAGE)) + + APPROVAL_TESTS_MACROS_ENTRY(TORTOISE_IMAGE_DIFF, + DiffInfo("{ProgramFiles}TortoiseSVN\\bin\\TortoiseIDiff.exe", "/left:%s /right:%s", Type::IMAGE)) + + APPROVAL_TESTS_MACROS_ENTRY(TORTOISE_TEXT_DIFF, DiffInfo("{ProgramFiles}TortoiseSVN\\bin\\TortoiseMerge.exe", Type::TEXT)) + + APPROVAL_TESTS_MACROS_ENTRY(WIN_MERGE_REPORTER, DiffInfo("{ProgramFiles}WinMerge\\WinMergeU.exe", Type::TEXT_AND_IMAGE)) + + APPROVAL_TESTS_MACROS_ENTRY(ARAXIS_MERGE, DiffInfo("{ProgramFiles}Araxis\\Araxis Merge\\Compare.exe", Type::TEXT_AND_IMAGE)) + + APPROVAL_TESTS_MACROS_ENTRY(CODE_COMPARE, DiffInfo("{ProgramFiles}Devart\\Code Compare\\CodeCompare.exe", Type::TEXT)) + + APPROVAL_TESTS_MACROS_ENTRY(KDIFF3, DiffInfo("{ProgramFiles}KDiff3\\kdiff3.exe", Type::TEXT)) + APPROVAL_TESTS_MACROS_ENTRY(VS_CODE, DiffInfo("{ProgramFiles}Microsoft VS Code\\Code.exe", "-d %s %s", Type::TEXT)) + + } +} +} + +#endif + + // ******************** From: GenericDiffReporter.h +#ifndef APPROVALTESTS_CPP_GENERICDIFFREPORTER_H +#define APPROVALTESTS_CPP_GENERICDIFFREPORTER_H + + +namespace ApprovalTests { +class GenericDiffReporter : public CommandReporter { +private: + SystemLauncher launcher; +public: + explicit GenericDiffReporter(const std::string& program) : CommandReporter(program, &launcher) + { + checkForCygwin(); + } + explicit GenericDiffReporter(const DiffInfo& info) : CommandReporter(info.getProgramForOs(), &launcher) + { + checkForCygwin(); + } + + void checkForCygwin() + { + if ( SystemUtils::isCygwin()) + { + launcher.setConvertArgumentsForSystemLaunchingFunction(convertForCygwin); + } + } + + static std::vector convertForCygwin(std::vector argv) + { + if (! SystemUtils::isCygwin()) + { + return argv; + } + std::vector copy = argv; + for( size_t i = 0; i != argv.size(); ++i ) + { + if ( i == 0) + { + copy[i] = "$(cygpath '" + argv[i] + "')"; + } + else + { + copy[i] = "$(cygpath -aw '" + argv[i] + "')"; + } + } + return copy; + } +}; +} + +#endif + + // ******************** From: FirstWorkingReporter.h +#ifndef APPROVALTESTS_CPP_FIRSTWORKINGREPORTER_H +#define APPROVALTESTS_CPP_FIRSTWORKINGREPORTER_H + + +namespace ApprovalTests { +class FirstWorkingReporter : public Reporter +{ +private: + std::vector< std::unique_ptr > reporters; +public: + + explicit FirstWorkingReporter(const std::vector& theReporters) + { + for(auto r : theReporters) + { + reporters.push_back(std::unique_ptr(r)); + } + } + + bool report(std::string received, std::string approved) const override + { + for(auto& r : reporters) + { + if (r->report(received, approved)) + { + return true; + } + } + return false; + } +}; +} + +#endif + + // ******************** From: LinuxReporters.h +#ifndef APPROVALTESTS_CPP_LINUXREPORTERS_H +#define APPROVALTESTS_CPP_LINUXREPORTERS_H + + +namespace ApprovalTests { +namespace Linux +{ + class KDiff3Reporter : public GenericDiffReporter { + public: + KDiff3Reporter() : GenericDiffReporter(DiffPrograms::Linux::KDIFF3()) {} + }; + + class MeldReporter : public GenericDiffReporter { + public: + MeldReporter() : GenericDiffReporter(DiffPrograms::Linux::MELD()) {} + }; + + class LinuxDiffReporter : public FirstWorkingReporter + { + public: + LinuxDiffReporter() : FirstWorkingReporter( + { + + new MeldReporter(), + new KDiff3Reporter() + + } + ) + { + } + }; + +} +} + +#endif + + // ******************** From: MacReporters.h +#ifndef APPROVALTESTS_CPP_MACREPORTERS_H +#define APPROVALTESTS_CPP_MACREPORTERS_H + + +namespace ApprovalTests { +namespace Mac { + class DiffMergeReporter : public GenericDiffReporter { + public: + DiffMergeReporter() : GenericDiffReporter(DiffPrograms::Mac::DIFF_MERGE()) {} + }; + + class VisualStudioCodeReporter : public GenericDiffReporter { + public: + VisualStudioCodeReporter() : GenericDiffReporter(DiffPrograms::Mac::VS_CODE()) {} + }; + + class BeyondCompareReporter : public GenericDiffReporter { + public: + BeyondCompareReporter() : GenericDiffReporter(DiffPrograms::Mac::BEYOND_COMPARE()) {} + }; + + class KaleidoscopeReporter : public GenericDiffReporter { + public: + KaleidoscopeReporter() : GenericDiffReporter(DiffPrograms::Mac::KALEIDOSCOPE()) {} + }; + + class KDiff3Reporter : public GenericDiffReporter { + public: + KDiff3Reporter() : GenericDiffReporter(DiffPrograms::Mac::KDIFF3()) {} + }; + + class P4MergeReporter : public GenericDiffReporter { + public: + P4MergeReporter() : GenericDiffReporter(DiffPrograms::Mac::P4MERGE()) {} + }; + + class TkDiffReporter : public GenericDiffReporter { + public: + TkDiffReporter() : GenericDiffReporter(DiffPrograms::Mac::TK_DIFF()) {} + }; + + class MacDiffReporter : public FirstWorkingReporter { + public: + MacDiffReporter() : FirstWorkingReporter( + { + + new BeyondCompareReporter(), + new DiffMergeReporter(), + new KaleidoscopeReporter(), + new P4MergeReporter(), + new KDiff3Reporter(), + new TkDiffReporter(), + new VisualStudioCodeReporter() + + } + ) { + } + }; +} +} + +#endif + + // ******************** From: WindowsReporters.h +#ifndef APPROVALTESTS_CPP_WINDOWSREPORTERS_H +#define APPROVALTESTS_CPP_WINDOWSREPORTERS_H + + +namespace ApprovalTests { +namespace Windows { + class BeyondCompare3Reporter : public GenericDiffReporter { + public: + BeyondCompare3Reporter() : GenericDiffReporter(DiffPrograms::Windows::BEYOND_COMPARE_3()) {} + }; + + class VisualStudioCodeReporter : public GenericDiffReporter { + public: + VisualStudioCodeReporter() : GenericDiffReporter(DiffPrograms::Windows::VS_CODE()) {} + }; + + class BeyondCompare4Reporter : public GenericDiffReporter { + public: + BeyondCompare4Reporter() : GenericDiffReporter(DiffPrograms::Windows::BEYOND_COMPARE_4()) {} + }; + + class BeyondCompareReporter : public FirstWorkingReporter { + public: + BeyondCompareReporter() : FirstWorkingReporter({new BeyondCompare4Reporter(), new BeyondCompare3Reporter()}) { + } + }; + + class TortoiseImageDiffReporter : public GenericDiffReporter { + public: + TortoiseImageDiffReporter() : GenericDiffReporter(DiffPrograms::Windows::TORTOISE_IMAGE_DIFF()) {} + }; + + class TortoiseTextDiffReporter : public GenericDiffReporter { + public: + TortoiseTextDiffReporter() : GenericDiffReporter(DiffPrograms::Windows::TORTOISE_TEXT_DIFF()) {} + }; + + class TortoiseDiffReporter : public FirstWorkingReporter { + public: + TortoiseDiffReporter() : FirstWorkingReporter( + {new TortoiseTextDiffReporter(), new TortoiseImageDiffReporter()}) { + } + }; + + class WinMergeReporter : public GenericDiffReporter { + public: + WinMergeReporter() : GenericDiffReporter(DiffPrograms::Windows::WIN_MERGE_REPORTER()) {} + }; + + class AraxisMergeReporter : public GenericDiffReporter { + public: + AraxisMergeReporter() : GenericDiffReporter(DiffPrograms::Windows::ARAXIS_MERGE()) {} + }; + + class CodeCompareReporter : public GenericDiffReporter { + public: + CodeCompareReporter() : GenericDiffReporter(DiffPrograms::Windows::CODE_COMPARE()) {} + }; + + class KDiff3Reporter : public GenericDiffReporter { + public: + KDiff3Reporter() : GenericDiffReporter(DiffPrograms::Windows::KDIFF3()) {} + }; + + class WindowsDiffReporter : public FirstWorkingReporter { + public: + WindowsDiffReporter() : FirstWorkingReporter( + { + + new TortoiseDiffReporter(), + new BeyondCompareReporter(), + new WinMergeReporter(), + new AraxisMergeReporter(), + new CodeCompareReporter(), + new KDiff3Reporter(), + new VisualStudioCodeReporter(), + + } + ) { + } + }; +} +} + +#endif + + // ******************** From: DiffReporter.h +#ifndef APPROVALTESTS_CPP_DIFFREPORTER_H +#define APPROVALTESTS_CPP_DIFFREPORTER_H + + +namespace ApprovalTests { +class DiffReporter : public FirstWorkingReporter +{ +public: + DiffReporter() : FirstWorkingReporter( + { + new Mac::MacDiffReporter(), + new Linux::LinuxDiffReporter(), + new Windows::WindowsDiffReporter() + } + ) + { + } +}; +} + +#endif + + // ******************** From: DefaultReporterFactory.h +#ifndef APPROVALTESTS_CPP_DEFAULTREPORTERFACTORY_H +#define APPROVALTESTS_CPP_DEFAULTREPORTERFACTORY_H + + + +namespace ApprovalTests { + +class DefaultReporterFactory +{ + +private: + static std::shared_ptr& defaultReporter() + { + static std::shared_ptr reporter = std::make_shared(); + return reporter; + } + +public: + static std::shared_ptr getDefaultReporter() + { + return defaultReporter(); + } + + static void setDefaultReporter( const std::shared_ptr& reporter) + { + defaultReporter() = reporter; + } + + +}; +} + +#endif + + // ******************** From: DefaultReporterDisposer.h +#ifndef APPROVALTESTS_CPP_DEFAULTREPORTERDISPOSER_H +#define APPROVALTESTS_CPP_DEFAULTREPORTERDISPOSER_H + + +namespace ApprovalTests { + +class APPROVAL_TESTS_NO_DISCARD DefaultReporterDisposer +{ +private: + std::shared_ptr previous_result; +public: + explicit DefaultReporterDisposer(const std::shared_ptr& reporter) + { + previous_result = DefaultReporterFactory::getDefaultReporter(); + DefaultReporterFactory::setDefaultReporter(reporter); + } + + ~DefaultReporterDisposer() + { + DefaultReporterFactory::setDefaultReporter(previous_result); + } +}; +} + +#endif + + // ******************** From: DefaultReporter.h +#ifndef APPROVALTESTS_CPP_DEFAULTREPORTER_H +#define APPROVALTESTS_CPP_DEFAULTREPORTER_H + + + +namespace ApprovalTests { +class DefaultReporter : public Reporter +{ +public: + virtual bool report(std::string received, std::string approved) const override + { + return DefaultReporterFactory::getDefaultReporter()->report(received, approved); + } +}; +} + +#endif + + // ******************** From: SubdirectoryDisposer.h +#ifndef APPROVALTESTS_CPP_SUBDIRECTORYDISPOSER_H +#define APPROVALTESTS_CPP_SUBDIRECTORYDISPOSER_H + + + +namespace ApprovalTests { + +class APPROVAL_TESTS_NO_DISCARD SubdirectoryDisposer +{ +private: + std::string previous_result; +public: + explicit SubdirectoryDisposer(std::string subdirectory) + { + previous_result = ApprovalTestNamer::testConfiguration().subdirectory; + ApprovalTestNamer::testConfiguration().subdirectory = std::move(subdirectory); + } + + ~SubdirectoryDisposer() + { + ApprovalTestNamer::testConfiguration().subdirectory = previous_result; + } +}; +} + +#endif + + // ******************** From: DefaultNamerFactory.h +#ifndef APPROVALTESTS_CPP_DEFAULTNAMERFACTORY_H +#define APPROVALTESTS_CPP_DEFAULTNAMERFACTORY_H + + + +namespace ApprovalTests { + + using NamerCreator = std::function()>; + + +class DefaultNamerFactory +{ +private: + static NamerCreator& defaultNamer() + { + static NamerCreator namer = [](){return std::make_shared();}; + return namer; + } + +public: + static NamerCreator getDefaultNamer() + { + return defaultNamer(); + } + + static void setDefaultNamer( NamerCreator namer) + { + defaultNamer() = std::move(namer); + } + +}; +} + +#endif + + // ******************** From: ExistingFileNamer.h +#ifndef APPROVALTESTS_CPP_EXISTINGFILENAMER_H +#define APPROVALTESTS_CPP_EXISTINGFILENAMER_H + + +namespace ApprovalTests { +class ExistingFileNamer: public ApprovalNamer{ + std::string filePath; +public: + explicit ExistingFileNamer(std::string filePath): filePath(std::move(filePath)){ + + } + virtual std::string getApprovedFile(std::string extensionWithDot) const override { + return DefaultNamerFactory::getDefaultNamer()()->getApprovedFile(extensionWithDot); + } + virtual std::string getReceivedFile(std::string ) const override { + return filePath; + } + +}; +} + +#endif + + // ******************** From: DefaultNamerDisposer.h +#ifndef APPROVALTESTS_CPP_DEFAULTNAMERDISPOSER_H +#define APPROVALTESTS_CPP_DEFAULTNAMERDISPOSER_H + + +namespace ApprovalTests { + +class APPROVAL_TESTS_NO_DISCARD DefaultNamerDisposer +{ +private: + NamerCreator previous_result; +public: + explicit DefaultNamerDisposer(NamerCreator namerCreator) + { + previous_result = DefaultNamerFactory::getDefaultNamer(); + DefaultNamerFactory::setDefaultNamer(std::move(namerCreator)); + } + + ~DefaultNamerDisposer() + { + DefaultNamerFactory::setDefaultNamer(previous_result); + } +}; +} + +#endif + + // ******************** From: QuietReporter.h +#ifndef APPROVALTESTS_CPP_QUIETREPORTER_H +#define APPROVALTESTS_CPP_QUIETREPORTER_H + + +namespace ApprovalTests { + +class QuietReporter : public Reporter +{ +public: + bool report(std::string , std::string ) const override + { + return true; + } +}; +} + +#endif + + // ******************** From: CIBuildOnlyReporter.h +#ifndef APPROVALTESTS_CPP_CIBUILDONLYREPORTER_H +#define APPROVALTESTS_CPP_CIBUILDONLYREPORTER_H + + + +namespace ApprovalTests +{ + + class CIBuildOnlyReporter : public Reporter + { + private: + std::shared_ptr m_reporter; + + public: + explicit CIBuildOnlyReporter(std::shared_ptr reporter = std::make_shared()) + : m_reporter(reporter) + { + } + + bool report(std::string received, std::string approved) const override + { + if (!isRunningUnderCI()) + { + return false; + } + m_reporter->report(received, approved); + + + return true; + } + + static bool isRunningUnderCI() + { + + auto environmentVariablesForCI = { + + "CI", + "CONTINUOUS_INTEGRATION", + "GO_SERVER_URL", + "JENKINS_URL", + "TEAMCITY_VERSION" + + }; + for (const auto& variable : environmentVariablesForCI) + { + if (!SystemUtils::safeGetEnv(variable).empty()) + { + return true; + } + } + return false; + } + }; +} + +#endif + + // ******************** From: DefaultFrontLoadedReporter.h +#ifndef APPROVALTESTS_CPP_DEFAULTFRONTLOADEDREPORTER_H +#define APPROVALTESTS_CPP_DEFAULTFRONTLOADEDREPORTER_H + + +namespace ApprovalTests { +class DefaultFrontLoadedReporter : public FirstWorkingReporter +{ +public: + DefaultFrontLoadedReporter() : FirstWorkingReporter( + { + new CIBuildOnlyReporter() + } + ) + { + } +}; +} + +#endif + + // ******************** From: FrontLoadedReporterFactory.h +#ifndef APPROVALTESTS_CPP_FRONTLOADEDREPORTERFACTORY_H +#define APPROVALTESTS_CPP_FRONTLOADEDREPORTERFACTORY_H + + + +namespace ApprovalTests { + +class FrontLoadedReporterFactory +{ + static std::shared_ptr& frontLoadedReporter() + { + static std::shared_ptr reporter = std::make_shared(); + return reporter; + } + +public: + static std::shared_ptr getFrontLoadedReporter() + { + return frontLoadedReporter(); + } + + static void setFrontLoadedReporter( const std::shared_ptr& reporter) + { + frontLoadedReporter() = reporter; + } +}; +} + +#endif + + // ******************** From: FrontLoadedReporterDisposer.h +#ifndef APPROVALTESTS_CPP_FRONTLOADEDREPORTERDISPOSER_H +#define APPROVALTESTS_CPP_FRONTLOADEDREPORTERDISPOSER_H + + +namespace ApprovalTests { + +class APPROVAL_TESTS_NO_DISCARD FrontLoadedReporterDisposer +{ +private: + std::shared_ptr previous_result; +public: + explicit FrontLoadedReporterDisposer(const std::shared_ptr& reporter) + { + previous_result = FrontLoadedReporterFactory::getFrontLoadedReporter(); + FrontLoadedReporterFactory::setFrontLoadedReporter(reporter); + } + + ~FrontLoadedReporterDisposer() + { + FrontLoadedReporterFactory::setFrontLoadedReporter(previous_result); + } + +}; +} + +#endif + + // ******************** From: ApprovalException.h +#ifndef APPROVALTESTS_CPP_APPROVALEXCEPTION_H +#define APPROVALTESTS_CPP_APPROVALEXCEPTION_H + + +namespace ApprovalTests { +class ApprovalException : public std::exception +{ +private: + std::string message; +public: + explicit ApprovalException( const std::string& msg ) : message( msg ) {} + + virtual const char *what() const noexcept override + { + return message.c_str(); + } +}; + +class ApprovalMismatchException : public ApprovalException +{ +private: + std::string format( const std::string &received, const std::string &approved ) + { + std::stringstream s; + s << "Failed Approval: \n" + << "Received does not match approved \n" + << "Received : \"" << received << "\" \n" + << "Approved : \"" << approved << "\""; + return s.str(); + } +public: + ApprovalMismatchException(const std::string& received, const std::string& approved ) + : ApprovalException( format( received, approved ) ) + { + } +}; + +class ApprovalMissingException : public ApprovalException +{ +private: + std::string format( const std::string &file ) + { + std::stringstream s; + s << "Failed Approval: \n" + << "Approval File Not Found \n" + << "File: \"" << file << '"'; + return s.str(); + } +public: + ApprovalMissingException(const std::string& , const std::string& approved ) + : ApprovalException( format( approved ) ) + { + } +}; +} + +#endif + + // ******************** From: ApprovalComparator.h +#ifndef APPROVALTESTS_CPP_APPROVALCOMPARATOR_H +#define APPROVALTESTS_CPP_APPROVALCOMPARATOR_H + + +namespace ApprovalTests { +class ApprovalComparator +{ +public: + virtual ~ApprovalComparator() = default; + + virtual bool contentsAreEquivalent(std::string receivedPath, + std::string approvedPath) const = 0; +}; +} + +#endif + + // ******************** From: TextFileComparator.h +#ifndef APPROVALTESTS_CPP_TEXTFILECOMPARATOR_H +#define APPROVALTESTS_CPP_TEXTFILECOMPARATOR_H + + +namespace ApprovalTests { +class TextFileComparator : public ApprovalComparator +{ +public: + static std::ifstream::int_type getNextRelevantCharacter(std::ifstream& astream) + { + auto ch = astream.get(); + if (ch == '\r') + { + return astream.get(); + } + else + { + return ch; + } + } + + virtual bool contentsAreEquivalent(std::string receivedPath, + std::string approvedPath) const override + { + std::ifstream astream(approvedPath.c_str(), + std::ios::binary | std::ifstream::in); + std::ifstream rstream(receivedPath.c_str(), + std::ios::binary | std::ifstream::in); + + while (astream.good() && rstream.good()) { + int a = getNextRelevantCharacter(astream); + int r = getNextRelevantCharacter(rstream); + + if (a != r) { + return false; + } + } + return true; + } +}; +} +#endif + + // ******************** From: ComparatorDisposer.h +#ifndef APPROVALTESTS_CPP_COMPARATORDISPOSER_H +#define APPROVALTESTS_CPP_COMPARATORDISPOSER_H + + +namespace ApprovalTests +{ + +using ComparatorContainer = std::map >; + +class APPROVAL_TESTS_NO_DISCARD ComparatorDisposer +{ +public: + ComparatorDisposer( + ComparatorContainer &comparators, + std::string extensionWithDot, + std::shared_ptr previousComparator, + std::shared_ptr newComparator) + : + comparators(comparators), + ext_(extensionWithDot), + previousComparator(previousComparator) + { + comparators[extensionWithDot] = newComparator; + } + + ~ComparatorDisposer() + { + comparators[ext_] = previousComparator; + } + +private: + ComparatorContainer &comparators; + std::string ext_; + std::shared_ptr previousComparator; +}; + +} + +#endif + + // ******************** From: ComparatorFactory.h +#ifndef APPROVALTESTS_CPP_COMPARATORFACTORY_H +#define APPROVALTESTS_CPP_COMPARATORFACTORY_H + + +namespace ApprovalTests { + +class ComparatorFactory { +private: + static ComparatorContainer &comparators() { + static ComparatorContainer allComparators; + return allComparators; + } + +public: + static ComparatorDisposer + registerComparator(const std::string &extensionWithDot, std::shared_ptr comparator) { + return ComparatorDisposer(comparators(), extensionWithDot, + getComparatorForFileExtensionWithDot(extensionWithDot), + comparator); + } + + static std::shared_ptr getComparatorForFile(const std::string &receivedPath) { + const std::string fileExtension = FileUtils::getExtensionWithDot(receivedPath); + return getComparatorForFileExtensionWithDot(fileExtension); + } + + static std::shared_ptr + getComparatorForFileExtensionWithDot(const std::string &fileExtensionWithDot) { + auto iterator = comparators().find(fileExtensionWithDot); + if (iterator != comparators().end()) { + return iterator->second; + } + return std::make_shared(); + } +}; + +} + +#endif + + // ******************** From: FileApprover.h +#ifndef APPROVALTESTS_CPP_FILEAPPROVER_H +#define APPROVALTESTS_CPP_FILEAPPROVER_H + + +namespace ApprovalTests { + +class FileApprover { + +public: + FileApprover() = default; + + ~FileApprover() = default; + + static ComparatorDisposer registerComparatorForExtension(const std::string& extensionWithDot, std::shared_ptr comparator) + { + return ComparatorFactory::registerComparator(extensionWithDot, comparator); + } + + + static void verify(const std::string& receivedPath, + const std::string& approvedPath, + const ApprovalComparator& comparator) { + if (!FileUtils::fileExists(approvedPath)) { + throw ApprovalMissingException(receivedPath, approvedPath); + } + + if (!FileUtils::fileExists(receivedPath)) { + throw ApprovalMissingException(approvedPath, receivedPath); + } + + if (!comparator.contentsAreEquivalent(receivedPath, approvedPath)) { + throw ApprovalMismatchException(receivedPath, approvedPath); + } + } + + static void verify(const std::string& receivedPath, + const std::string& approvedPath) { + verify(receivedPath, approvedPath, *ComparatorFactory::getComparatorForFile(receivedPath)); + } + + static void verify(const ApprovalNamer& n, const ApprovalWriter& s, const Reporter& r) { + std::string approvedPath = n.getApprovedFile(s.getFileExtensionWithDot()); + std::string receivedPath = n.getReceivedFile(s.getFileExtensionWithDot()); + s.write(receivedPath); + try + { + verify(receivedPath, approvedPath); + s.cleanUpReceived(receivedPath); + } + catch (const ApprovalException&) { + reportAfterTryingFrontLoadedReporter(receivedPath, approvedPath, r); + throw; + } + } + + static void + reportAfterTryingFrontLoadedReporter(const std::string &receivedPath, const std::string &approvedPath, const Reporter &r) + { + auto tryFirst = FrontLoadedReporterFactory::getFrontLoadedReporter(); + if (!tryFirst->report(receivedPath, approvedPath)) + { + r.report(receivedPath, approvedPath); + } + } + + +}; +} + +#endif + + // ******************** From: Approvals.h +#ifndef APPROVALTESTS_CPP_APPROVALS_H +#define APPROVALTESTS_CPP_APPROVALS_H + + +namespace ApprovalTests { +class Approvals { +private: + Approvals() = default; + + ~Approvals() = default; + +public: + static std::shared_ptr getDefaultNamer() + { + return DefaultNamerFactory::getDefaultNamer()(); + } + + static void verify(std::string contents, const Reporter &reporter = DefaultReporter()) { + verifyWithExtension(contents, ".txt", reporter); + } + + static void verifyWithExtension(std::string contents, const std::string& fileExtensionWithDot, const Reporter &reporter = DefaultReporter()) { + StringWriter writer(contents, fileExtensionWithDot); + FileApprover::verify(*getDefaultNamer(), writer, reporter); + } + + static void verify(const ApprovalWriter& writer, const Reporter &reporter = DefaultReporter()) + { + FileApprover::verify(*getDefaultNamer(), writer, reporter); + } + + template + using IsNotDerivedFromWriter = typename std::enable_if::value, int>::type; + + template< + typename T, + typename = IsNotDerivedFromWriter> + static void verify(const T& contents, const Reporter &reporter = DefaultReporter()) { + verify(StringUtils::toString(contents), reporter); + } + + template< + typename T, + typename = IsNotDerivedFromWriter> + static void verifyWithExtension(const T& contents, const std::string& fileExtensionWithDot, const Reporter &reporter = DefaultReporter()) { + verifyWithExtension(StringUtils::toString(contents), fileExtensionWithDot, reporter); + } + + template< + typename T, + typename Function, + typename = IsNotDerivedFromReporter> + static void verify(const T& contents, + Function converter, + const Reporter &reporter = DefaultReporter()) + { + std::stringstream s; + converter(contents, s); + verify(s.str(), reporter); + } + + template< + typename T, + typename Function, + typename = IsNotDerivedFromReporter> + static void verifyWithExtension(const T& contents, + Function converter, + const std::string& fileExtensionWithDot, + const Reporter &reporter = DefaultReporter()) + { + std::stringstream s; + converter(contents, s); + verifyWithExtension(s.str(), fileExtensionWithDot, reporter); + } + + static void verifyExceptionMessage( + std::function functionThatThrows, + const Reporter &reporter = DefaultReporter()) + { + std::string message = "*** no exception thrown ***"; + try + { + functionThatThrows(); + } + catch(const std::exception& e) + { + message = e.what(); + } + verify(message, reporter); + } + + template + static void verifyAll(std::string header, + const Iterator &start, const Iterator &finish, + std::function converter, + const Reporter &reporter = DefaultReporter()) { + std::stringstream s; + if (!header.empty()) { + s << header << "\n\n\n"; + } + for (auto it = start; it != finish; ++it) { + converter(*it, s); + s << '\n'; + } + verify(s.str(), reporter); + } + + template + static void verifyAll(std::string header, + const Container &list, + std::function converter, + const Reporter &reporter = DefaultReporter()) { + verifyAll(header, list.begin(), list.end(), converter, reporter); + } + + template + static void verifyAll(std::string header, + const std::vector &list, + const Reporter &reporter = DefaultReporter()) { + int i = 0; + verifyAll>(header, list, [&](T e, std::ostream &s) { s << "[" << i++ << "] = " << e; }, + reporter); + } + + template + static void verifyAll(const std::vector &list, + const Reporter &reporter = DefaultReporter()) { + verifyAll("", list, reporter); + } + + static void verifyExistingFile(const std::string filePath, const Reporter &reporter = DefaultReporter()) { + ExistingFile writer(filePath); + ExistingFileNamer namer(filePath); + FileApprover::verify(namer, writer, reporter); + } + + static SubdirectoryDisposer useApprovalsSubdirectory(std::string subdirectory = "approval_tests") + { + return SubdirectoryDisposer(subdirectory); + } + + static DefaultReporterDisposer useAsDefaultReporter(const std::shared_ptr& reporter) + { + return DefaultReporterDisposer(reporter); + } + + static FrontLoadedReporterDisposer useAsFrontLoadedReporter(const std::shared_ptr& reporter) + { + return FrontLoadedReporterDisposer(reporter); + } + + static DefaultNamerDisposer useAsDefaultNamer(NamerCreator namerCreator) + { + return DefaultNamerDisposer(namerCreator); + } + +}; +} + +#endif + + // ******************** From: CombinationApprovals.h +#ifndef APPROVALTESTS_CPP_COMBINATIONAPPROVALS_H +#define APPROVALTESTS_CPP_COMBINATIONAPPROVALS_H + + +namespace ApprovalTests { +namespace CombinationApprovals { +namespace Detail { + + + + +template struct disjunction : std::false_type {}; +template struct disjunction : B1 {}; +template +struct disjunction : std::conditional>::type {}; + + + +struct print_input { + std::ostream& out; + template + void operator()(const T& input) { + out << ", " << input; + } +}; + + +template +struct serialize { + std::ostream& out; + Converter converter; + template + void operator()(T&& input1, Ts&&... inputs) { + + out << "(" << input1; + + CartesianProduct::Detail::for_each(std::forward_as_tuple(inputs...), print_input{out}); + out << ") => " << converter(input1, inputs...) << '\n'; + } +}; +} + +template +void verifyAllCombinations(Converter&& converter, const Reporter& reporter, const Container& input0, const Containers&... inputs) +{ + std::stringstream s; + CartesianProduct::cartesian_product(Detail::serialize{s, std::forward(converter)}, input0, inputs...); + Approvals::verify(s.str(), reporter); +} + +template +CartesianProduct::Detail::enable_if_t...>::value> +verifyAllCombinations(Converter&& converter, const Containers&... inputs) +{ + verifyAllCombinations(std::forward(converter), DefaultReporter(), inputs...); +} + +} +} + +#endif + + // ******************** From: Catch2Approvals.h + +#ifndef APPROVALTESTS_CPP_CATCH2APPROVALS_H +#define APPROVALTESTS_CPP_CATCH2APPROVALS_H + + +// +#if defined(APPROVALS_CATCH_EXISTING_MAIN) + #define APPROVALS_CATCH + #define CATCH_CONFIG_RUNNER +#elif defined(APPROVALS_CATCH) + #define CATCH_CONFIG_MAIN +#endif + +#ifdef APPROVALS_CATCH + +#include + +//namespace ApprovalTests { +struct Catch2ApprovalListener : Catch::EventListenerBase { + ApprovalTests::TestName currentTest; + using EventListenerBase::EventListenerBase; // This using allows us to use all base-class constructors + virtual void testCaseStarting(Catch::TestCaseInfo const &testInfo) override { + + currentTest.setFileName(testInfo.lineInfo.file); + ApprovalTests::ApprovalTestNamer::currentTest(¤tTest); + } + + virtual void testCaseEnded(Catch::TestCaseStats const &/*testCaseStats*/) override { + while (!currentTest.sections.empty()) { + currentTest.sections.pop_back(); + } + } + + virtual void sectionStarting(Catch::SectionInfo const §ionInfo) override { + currentTest.sections.push_back(sectionInfo.name); + } + + virtual void sectionEnded(Catch::SectionStats const &/*sectionStats*/) override { + currentTest.sections.pop_back(); + } +}; +//} +CATCH_REGISTER_LISTENER(Catch2ApprovalListener) + +#endif +#ifdef TEST_COMMIT_REVERT_CATCH + +//namespace ApprovalTests { +struct Catch2TestCommitRevert : Catch::EventListenerBase { + using EventListenerBase::EventListenerBase; // This using allows us to use all base-class constructors + virtual void testRunEnded( Catch::TestRunStats const& testRunStats )override{ + bool commit = testRunStats.totals.testCases.allOk(); + std::string message = "r "; + if (commit) { + std::cout << "git add -A n"; + std::cout << "git commit -m " << message; + } else + { + std::cout << "git clean -fd n"; + std::cout << "git reset --hard HEAD n"; + } + } +}; +//} +CATCH_REGISTER_LISTENER(Catch2TestCommitRevert) +#endif +// +#endif + + // ******************** From: DocTestApprovals.h +#ifndef APPROVALTESTS_CPP_DOCTESTAPPROVALS_H +#define APPROVALTESTS_CPP_DOCTESTAPPROVALS_H + + +// +#ifdef APPROVALS_DOCTEST + +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN + +#include + +namespace ApprovalTests { +// anonymous namespace to prevent compiler -Wsubobject-linkage warnings +// This is OK as this code is only compiled on main() +namespace { + struct AbstractReporter : doctest::IReporter { + virtual void report_query(const doctest::QueryData&) override {} + // called when the whole test run starts + virtual void test_run_start() override {} + + // called when the whole test run ends (caching a pointer to the input doesn't make sense here) + virtual void test_run_end(const doctest::TestRunStats &) override {} + + // called when a test case is started (safe to cache a pointer to the input) + virtual void test_case_start(const doctest::TestCaseData &) override {} + +#if 20305 <= DOCTEST_VERSION + // called when a test case is reentered because of unfinished subcases (safe to cache a pointer to the input) + virtual void test_case_reenter(const doctest::TestCaseData&) override {} +#endif + + // called when a test case has ended + virtual void test_case_end(const doctest::CurrentTestCaseStats &) override {} + + // called when an exception is thrown from the test case (or it crashes) + virtual void test_case_exception(const doctest::TestCaseException &) override {} + + // called whenever a subcase is entered (don't cache pointers to the input) + virtual void subcase_start(const doctest::SubcaseSignature &) override {} + + // called whenever a subcase is exited (don't cache pointers to the input) + virtual void subcase_end() override {} + + // called for each assert (don't cache pointers to the input) + virtual void log_assert(const doctest::AssertData &) override {} + + // called for each message (don't cache pointers to the input) + virtual void log_message(const doctest::MessageData &) override {} + + // called when a test case is skipped either because it doesn't pass the filters, has a skip decorator + // or isn't in the execution range (between first and last) (safe to cache a pointer to the input) + virtual void test_case_skipped(const doctest::TestCaseData &) override {} + + + }; + + struct DocTestApprovalListener : AbstractReporter { + TestName currentTest; + + // constructor has to accept the ContextOptions by ref as a single argument + explicit DocTestApprovalListener(const doctest::ContextOptions & /*in*/) { + } + + void test_case_start(const doctest::TestCaseData &testInfo) override { + + currentTest.sections.emplace_back(testInfo.m_name); + currentTest.setFileName(testInfo.m_file); + ApprovalTestNamer::currentTest(¤tTest); + } + + void test_case_end(const doctest::CurrentTestCaseStats & /*in*/) override { + + while (!currentTest.sections.empty()) { + currentTest.sections.pop_back(); + } + } + + void subcase_start(const doctest::SubcaseSignature &signature) override { + + currentTest.sections.emplace_back(signature.m_name); + } + + void subcase_end() override { + + currentTest.sections.pop_back(); + } + }; +} +} + +REGISTER_LISTENER("approvals", 0, ApprovalTests::DocTestApprovalListener); + + +#endif // APPROVALS_DOCTEST +// +#endif + + // ******************** From: GoogleConfiguration.h +#ifndef APPROVALTESTS_CPP_GOOGLECONFIGURATION_H +#define APPROVALTESTS_CPP_GOOGLECONFIGURATION_H + + +namespace ApprovalTests { +class GoogleConfiguration +{ +public: + + APPROVAL_TESTS_NO_DISCARD static bool addTestCaseNameRedundancyCheck(GoogleCustomizationsFactory::Comparator comparator) + { + return GoogleCustomizationsFactory::addTestCaseNameRedundancyCheck(comparator); + } + + + APPROVAL_TESTS_NO_DISCARD static bool addIgnorableTestCaseNameSuffix(std::string suffix) + { + return addTestCaseNameRedundancyCheck( createIgnorableTestCaseNameSuffixCheck(suffix) ); + } + + static GoogleCustomizationsFactory::Comparator createIgnorableTestCaseNameSuffixCheck( const std::string& suffix ) + { + return [suffix](std::string testFileNameWithExtension, std::string testCaseName) + { + if (testCaseName.length() <= suffix.length() || !StringUtils::endsWith(testCaseName, suffix)) + { + return false; + } + + auto withoutSuffix = testCaseName.substr(0, testCaseName.length() - suffix.length()); + auto withFileExtension = withoutSuffix + "."; + return StringUtils::contains(testFileNameWithExtension, withFileExtension); + }; + } +}; +} + +#endif + + // ******************** From: GoogleTestApprovals.h +#ifndef APPROVALTESTS_CPP_GOOGLTESTAPPPROVALS_H +#define APPROVALTESTS_CPP_GOOGLTESTAPPPROVALS_H + + +#ifdef APPROVALS_GOOGLETEST_EXISTING_MAIN +#define APPROVALS_GOOGLETEST +#endif + +#ifdef APPROVALS_GOOGLETEST + +// +#include "gtest/gtest.h" + +namespace ApprovalTests { +class GoogleTestListener : public testing::EmptyTestEventListener +{ + TestName currentTest; +public: + bool isDuplicate(std::string testFileNameWithExtension, std::string testCaseName) + { + for( auto check : GoogleCustomizationsFactory::getEquivalencyChecks()) + { + if (check(testFileNameWithExtension, testCaseName)) + { + return true; + } + } + return false; + } + + virtual void OnTestStart(const testing::TestInfo& testInfo) override + { + currentTest.setFileName(testInfo.file()); + currentTest.sections = {}; + if (! isDuplicate(currentTest.getFileName(), testInfo.test_case_name())) + { + currentTest.sections.emplace_back(testInfo.test_case_name()); + } + if (! std::string(testInfo.name()).empty()) + { + currentTest.sections.emplace_back(testInfo.name()); + } + + ApprovalTestNamer::currentTest(¤tTest); + } +}; + +inline void initializeApprovalTestsForGoogleTests() { + auto& listeners = testing::UnitTest::GetInstance()->listeners(); + listeners.Append(new GoogleTestListener); +} +} + +#ifndef APPROVALS_GOOGLETEST_EXISTING_MAIN +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + ApprovalTests::initializeApprovalTestsForGoogleTests(); + return RUN_ALL_TESTS(); +} +#endif //APPROVALS_GOOGLETEST_EXISTING_MAIN + +// +#endif +#endif + + // ******************** From: NamerFactory.h +#ifndef APPROVALTESTS_CPP_NAMERFACTORY_H +#define APPROVALTESTS_CPP_NAMERFACTORY_H + + + +namespace ApprovalTests { +struct NamerFactory +{ + static SectionNameDisposer appendToOutputFilename(const std::string& sectionName) + { + return SectionNameDisposer(ApprovalTestNamer::currentTest(), sectionName); + } +}; +} + +#endif + + // ******************** From: SeparateApprovedAndReceivedDirectoriesNamer.h +#ifndef APPROVALTESTS_CPP_SEPARATEAPPROVEDANDRECEIVEDDIRECTORIESNAMER_H +#define APPROVALTESTS_CPP_SEPARATEAPPROVEDANDRECEIVEDDIRECTORIESNAMER_H + + +namespace ApprovalTests { +class SeparateApprovedAndReceivedDirectoriesNamer : public ApprovalTestNamer +{ +public: + virtual ~SeparateApprovedAndReceivedDirectoriesNamer() = default; + + std::string getFullFileNameWithExtraDirectory(const std::string& approved, const std::string& extensionWithDot) const + { + std::string outputDirectory = getDirectory() + approved; + SystemUtils::ensureDirectoryExists(outputDirectory); + + std::string outputFile = getFileName() + "." + getTestName() + extensionWithDot; + + return outputDirectory + SystemUtils::getDirectorySeparator() + outputFile; + } + + virtual std::string getApprovedFile(std::string extensionWithDot) const override + { + return getFullFileNameWithExtraDirectory("approved", extensionWithDot); + } + + virtual std::string getReceivedFile(std::string extensionWithDot) const override + { + return getFullFileNameWithExtraDirectory("received", extensionWithDot); + } + + static DefaultNamerDisposer useAsDefaultNamer() + { + return Approvals::useAsDefaultNamer([](){return std::make_shared();}); + } + +}; +} + +#endif + + // ******************** From: AutoApproveIfMissingReporter.h +#ifndef APPROVALTESTS_CPP_AUTOAPPROVEIFMISSINGREPORTER_H +#define APPROVALTESTS_CPP_AUTOAPPROVEIFMISSINGREPORTER_H + + +namespace ApprovalTests { +class AutoApproveIfMissingReporter : public Reporter +{ +public: + bool report(std::string received, std::string approved) const override + { + if (FileUtils::fileExists(approved)) + { + return false; + } + + return AutoApproveReporter().report(received, approved); + } +}; +} + +#endif + + // ******************** From: BlockingReporter.h +#ifndef APPROVALTESTS_CPP_BLOCKINGREPORTER_H +#define APPROVALTESTS_CPP_BLOCKINGREPORTER_H + + + +namespace ApprovalTests { +class BlockingReporter : public Reporter +{ +private: + std::shared_ptr blocker; + + BlockingReporter() = delete; + +public: + explicit BlockingReporter( std::shared_ptr blocker ) : blocker(std::move(blocker)) + { + } + + static std::shared_ptr onMachineNamed( const std::string& machineName ) + { + auto machineBlocker = std::make_shared( MachineBlocker::onMachineNamed(machineName) ); + return std::make_shared(machineBlocker); + } + + static std::shared_ptr onMachinesNotNamed( const std::string& machineName ) + { + auto machineBlocker = std::make_shared( MachineBlocker::onMachinesNotNamed(machineName) ); + return std::make_shared(machineBlocker); + } + + virtual bool report(std::string , std::string ) const override + { + return blocker->isBlockingOnThisMachine(); + } +}; +} + +#endif + + // ******************** From: CIBuildOnlyReporterUtils.h +#ifndef APPROVALTESTS_CPP_CIBUILDONLYREPORTERUTILS_H +#define APPROVALTESTS_CPP_CIBUILDONLYREPORTERUTILS_H + + +namespace ApprovalTests +{ + namespace CIBuildOnlyReporterUtils + { + inline FrontLoadedReporterDisposer useAsFrontLoadedReporter(const std::shared_ptr& reporter) + { + return Approvals::useAsFrontLoadedReporter( + std::make_shared( reporter )); + } + } +} + +#endif + + // ******************** From: ClipboardReporter.h +#ifndef APPROVALTESTS_CPP_COMMANDLINEREPORTER_H +#define APPROVALTESTS_CPP_COMMANDLINEREPORTER_H + + + + +namespace ApprovalTests { +class ClipboardReporter : public Reporter { +public: + static std::string getCommandLineFor(const std::string& received, const std::string& approved, bool isWindows) + { + if (isWindows) { + return std::string("move /Y ") + "\"" + received + "\" \"" + approved + "\""; + } else { + return std::string("mv ") + "\"" + received + "\" \"" + approved + "\""; + } + } + + virtual bool report(std::string received, std::string approved) const override + { + copyToClipboard(getCommandLineFor(received, approved, SystemUtils::isWindowsOs())); + return true; + } + + static void copyToClipboard(const std::string& newClipboard) { + + + const std::string clipboardCommand = SystemUtils::isWindowsOs() ? "clip" : "pbclip"; + auto cmd = std::string("echo ") + newClipboard + " | " + clipboardCommand; + system(cmd.c_str()); + } +}; +} + +#endif + + // ******************** From: CombinationReporter.h +#ifndef APPROVALTESTS_CPP_COMBINATIONREPORTER_H +#define APPROVALTESTS_CPP_COMBINATIONREPORTER_H + + +namespace ApprovalTests { +class CombinationReporter : public Reporter +{ +private: + std::vector< std::unique_ptr > reporters; +public: + + explicit CombinationReporter(const std::vector& theReporters) + { + for(auto r : theReporters) + { + reporters.push_back(std::unique_ptr(r)); + } + } + + bool report(std::string received, std::string approved) const override + { + bool result = false; + for(auto& r : reporters) + { + result |= r->report(received, approved); + } + return result; + } +}; +} + +#endif + + // ******************** From: ExceptionCollector.h +#ifndef APPROVALTESTS_CPP_EXCEPTIONCOLLECTOR_H +#define APPROVALTESTS_CPP_EXCEPTIONCOLLECTOR_H + + +namespace ApprovalTests { +class ExceptionCollector +{ + std::vector exceptionMessages; + +public: + void gather(std::function functionThatThrows) + { + try + { + functionThatThrows(); + } + catch(const std::exception& e) + { + exceptionMessages.emplace_back(e.what()); + } + } + ~ExceptionCollector() + { + if ( ! exceptionMessages.empty()) + { + exceptionMessages.emplace_back("ERROR: Calling code forgot to call exceptionCollector.release()"); + } + release(); + } + + void release() + { + if (! exceptionMessages.empty()) + { + std::stringstream s; + s << exceptionMessages.size() << " exceptions were thrown:\n\n"; + int count = 1; + for( const auto& error : exceptionMessages) + { + s << count++ << ") " << error << '\n'; + } + exceptionMessages.clear(); + throw std::runtime_error(s.str()); + } + } +}; +} + +#endif + diff --git a/cpp/lib/CMakeLists.txt b/cpp/lib/CMakeLists.txt new file mode 100644 index 00000000..b04d9a2a --- /dev/null +++ b/cpp/lib/CMakeLists.txt @@ -0,0 +1,3 @@ +set(LIB_NAME lib) +add_library(${LIB_NAME} INTERFACE) +target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt new file mode 100644 index 00000000..0f60d192 --- /dev/null +++ b/cpp/src/CMakeLists.txt @@ -0,0 +1,4 @@ +set(SRC_LIB_NAME src) +add_library(${SRC_LIB_NAME}) +target_sources(${SRC_LIB_NAME} PRIVATE GildedRose.cc) +target_include_directories(${SRC_LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/cpp/src/GildedRose.cc b/cpp/src/GildedRose.cc new file mode 100644 index 00000000..8df23e47 --- /dev/null +++ b/cpp/src/GildedRose.cc @@ -0,0 +1,80 @@ +#include "GildedRose.h" + +GildedRose::GildedRose(vector & items) : items(items) +{} + +void GildedRose::updateQuality() +{ + for (int i = 0; i < items.size(); i++) + { + if (items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert") + { + if (items[i].quality > 0) + { + if (items[i].name != "Sulfuras, Hand of Ragnaros") + { + items[i].quality = items[i].quality - 1; + } + } + } + else + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + + if (items[i].name == "Backstage passes to a TAFKAL80ETC concert") + { + if (items[i].sellIn < 11) + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + + if (items[i].sellIn < 6) + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + } + } + } + + if (items[i].name != "Sulfuras, Hand of Ragnaros") + { + items[i].sellIn = items[i].sellIn - 1; + } + + if (items[i].sellIn < 0) + { + if (items[i].name != "Aged Brie") + { + if (items[i].name != "Backstage passes to a TAFKAL80ETC concert") + { + if (items[i].quality > 0) + { + if (items[i].name != "Sulfuras, Hand of Ragnaros") + { + items[i].quality = items[i].quality - 1; + } + } + } + else + { + items[i].quality = items[i].quality - items[i].quality; + } + } + else + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + } + } +} diff --git a/cpp/src/GildedRose.h b/cpp/src/GildedRose.h new file mode 100644 index 00000000..8464f87b --- /dev/null +++ b/cpp/src/GildedRose.h @@ -0,0 +1,24 @@ +#include +#include + +using namespace std; + +class Item +{ +public: + string name; + int sellIn; + int quality; + Item(string name, int sellIn, int quality) : name(name), sellIn(sellIn), quality(quality) + {} +}; + +class GildedRose +{ +public: + vector & items; + GildedRose(vector & items); + + void updateQuality(); +}; + diff --git a/cpp/test/CMakeLists.txt b/cpp/test/CMakeLists.txt new file mode 100644 index 00000000..bd89b333 --- /dev/null +++ b/cpp/test/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(cpp_catch2_approvaltest) +add_subdirectory(cpp_catch2_unittest) +add_subdirectory(cpp_googletest_approvaltest) +add_subdirectory(cpp_googletest_unittest) +add_subdirectory(cpp_texttest) diff --git a/cpp/test/cpp_catch2_approvaltest/CMakeLists.txt b/cpp/test/cpp_catch2_approvaltest/CMakeLists.txt new file mode 100644 index 00000000..d50498c1 --- /dev/null +++ b/cpp/test/cpp_catch2_approvaltest/CMakeLists.txt @@ -0,0 +1,15 @@ +set(TEST_NAME GildedRoseCatch2ApprovalTests) +add_executable(${TEST_NAME}) +target_sources(${TEST_NAME} PRIVATE GildedRoseCatch2ApprovalTests.cc) +target_link_libraries(${TEST_NAME} lib src Catch2::Catch2 Catch2::Catch2WithMain) +set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 11) +add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) + +# Set compiler option /FC for Visual Studio to to make the __FILE__ macro expand to full path. +# The __FILE__ macro is used by Catch2 to get the path to current test file. +# Links: +# * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019 +# * https://docs.microsoft.com/en-us/cpp/build/reference/fc-full-path-of-source-code-file-in-diagnostics?view=vs-2019 +if (MSVC) + target_compile_options(${TEST_NAME} PRIVATE "/FC") +endif() diff --git a/cpp/test/cpp_catch2_approvaltest/GildedRoseCatch2ApprovalTests.GildedRoseApprovalTests.approved.txt b/cpp/test/cpp_catch2_approvaltest/GildedRoseCatch2ApprovalTests.GildedRoseApprovalTests.approved.txt new file mode 100644 index 00000000..d056cec5 --- /dev/null +++ b/cpp/test/cpp_catch2_approvaltest/GildedRoseCatch2ApprovalTests.GildedRoseApprovalTests.approved.txt @@ -0,0 +1,2 @@ +(Foo, 1, 1) => name: Foo, sellIn: 0, quality: 0 + diff --git a/cpp/test/cpp_catch2_approvaltest/GildedRoseCatch2ApprovalTests.cc b/cpp/test/cpp_catch2_approvaltest/GildedRoseCatch2ApprovalTests.cc new file mode 100644 index 00000000..00a7cce3 --- /dev/null +++ b/cpp/test/cpp_catch2_approvaltest/GildedRoseCatch2ApprovalTests.cc @@ -0,0 +1,29 @@ +#define APPROVALS_CATCH +#include "ApprovalTests.hpp" +#include "GildedRose.h" + +std::ostream& operator<<(std::ostream& os, const Item& obj) +{ + return os + << "name: " << obj.name + << ", sellIn: " << obj.sellIn + << ", quality: " << obj.quality; +} + +TEST_CASE("GildedRoseApprovalTests", "VerifyCombinations") +{ + std::vector names { "Foo" }; + std::vector sellIns { 1 }; + std::vector qualities { 1 }; + + auto f = [](string name, int sellIn, int quality) { + vector items = {Item(name, sellIn, quality)}; + GildedRose app(items); + app.updateQuality(); + return items[0]; + }; + + ApprovalTests::CombinationApprovals::verifyAllCombinations( + f, + names, sellIns, qualities); +} diff --git a/cpp/test/cpp_catch2_unittest/CMakeLists.txt b/cpp/test/cpp_catch2_unittest/CMakeLists.txt new file mode 100644 index 00000000..eec29e9f --- /dev/null +++ b/cpp/test/cpp_catch2_unittest/CMakeLists.txt @@ -0,0 +1,15 @@ +set(TEST_NAME GildedRoseCatch2UnitTests) +add_executable(${TEST_NAME}) +target_sources(${TEST_NAME} PRIVATE GildedRoseCatch2UnitTests.cc) +target_link_libraries(${TEST_NAME} lib src Catch2::Catch2 Catch2::Catch2WithMain ) +set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 11) +add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) + +# Set compiler option /FC for Visual Studio to to make the __FILE__ macro expand to full path. +# The __FILE__ macro is used by Catch2 to get the path to current test file. +# Links: +# * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019 +# * https://docs.microsoft.com/en-us/cpp/build/reference/fc-full-path-of-source-code-file-in-diagnostics?view=vs-2019 +if (MSVC) + target_compile_options(${TEST_NAME} PRIVATE "/FC") +endif() diff --git a/cpp/test/cpp_catch2_unittest/GildedRoseCatch2UnitTests.cc b/cpp/test/cpp_catch2_unittest/GildedRoseCatch2UnitTests.cc new file mode 100644 index 00000000..22b6dbfd --- /dev/null +++ b/cpp/test/cpp_catch2_unittest/GildedRoseCatch2UnitTests.cc @@ -0,0 +1,12 @@ +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file +#include "catch2/catch_all.hpp" +#include "GildedRose.h" + +TEST_CASE("GildedRoseUnitTest", "Foo") +{ + vector items; + items.push_back(Item("Foo", 0, 0)); + GildedRose app(items); + app.updateQuality(); + REQUIRE("fixme" == app.items[0].name); +} diff --git a/cpp/test/cpp_googletest_approvaltest/CMakeLists.txt b/cpp/test/cpp_googletest_approvaltest/CMakeLists.txt new file mode 100644 index 00000000..8961da7c --- /dev/null +++ b/cpp/test/cpp_googletest_approvaltest/CMakeLists.txt @@ -0,0 +1,15 @@ +set(TEST_NAME GildedRoseGoogletestApprovalTests) +add_executable(${TEST_NAME}) +target_sources(${TEST_NAME} PRIVATE googletest_approval_main.cc GildedRoseGoogletestApprovalTests.cc) +target_link_libraries(${TEST_NAME} lib src gtest) +set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 11) +add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) + +# Set compiler option /FC for Visual Studio to to make the __FILE__ macro expand to full path. +# The __FILE__ macro can be used to get the path to current test file. +# Links: +# * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019 +# * https://docs.microsoft.com/en-us/cpp/build/reference/fc-full-path-of-source-code-file-in-diagnostics?view=vs-2019 +if (MSVC) + target_compile_options(${TEST_NAME} PRIVATE "/FC") +endif() diff --git a/cpp/test/cpp_googletest_approvaltest/GildedRoseGoogletestApprovalTests.GildedRoseApprovalTests.VerifyCombinations.approved.txt b/cpp/test/cpp_googletest_approvaltest/GildedRoseGoogletestApprovalTests.GildedRoseApprovalTests.VerifyCombinations.approved.txt new file mode 100644 index 00000000..d056cec5 --- /dev/null +++ b/cpp/test/cpp_googletest_approvaltest/GildedRoseGoogletestApprovalTests.GildedRoseApprovalTests.VerifyCombinations.approved.txt @@ -0,0 +1,2 @@ +(Foo, 1, 1) => name: Foo, sellIn: 0, quality: 0 + diff --git a/cpp/test/cpp_googletest_approvaltest/GildedRoseGoogletestApprovalTests.cc b/cpp/test/cpp_googletest_approvaltest/GildedRoseGoogletestApprovalTests.cc new file mode 100644 index 00000000..e615b9bf --- /dev/null +++ b/cpp/test/cpp_googletest_approvaltest/GildedRoseGoogletestApprovalTests.cc @@ -0,0 +1,33 @@ +// Include header files for test frameworks +#include +#include + +// Include code to be tested +#include "GildedRose.h" + +std::ostream& operator<<(std::ostream& os, const Item& obj) +{ + return os + << "name: " << obj.name + << ", sellIn: " << obj.sellIn + << ", quality: " << obj.quality; +} + +TEST(GildedRoseApprovalTests, VerifyCombinations) { + + std::vector names { "Foo" }; + std::vector sellIns { 1 }; + std::vector qualities { 1 }; + + auto f = [](string name, int sellIn, int quality) { + vector items = {Item(name, sellIn, quality)}; + GildedRose app(items); + app.updateQuality(); + return items[0]; + }; + + ApprovalTests::CombinationApprovals::verifyAllCombinations( + f, + names, sellIns, qualities); + +} diff --git a/cpp/test/cpp_googletest_approvaltest/googletest_approval_main.cc b/cpp/test/cpp_googletest_approvaltest/googletest_approval_main.cc new file mode 100644 index 00000000..9dcbc583 --- /dev/null +++ b/cpp/test/cpp_googletest_approvaltest/googletest_approval_main.cc @@ -0,0 +1,2 @@ +#define APPROVALS_GOOGLETEST // This tells Approval Tests to provide a main() - only do this in one cpp file +#include "ApprovalTests.hpp" diff --git a/cpp/test/cpp_googletest_unittest/CMakeLists.txt b/cpp/test/cpp_googletest_unittest/CMakeLists.txt new file mode 100644 index 00000000..3eb9751e --- /dev/null +++ b/cpp/test/cpp_googletest_unittest/CMakeLists.txt @@ -0,0 +1,15 @@ +set(TEST_NAME GildedRoseGoogletestUnitTests) +add_executable(${TEST_NAME}) +target_sources(${TEST_NAME} PRIVATE GildedRoseGoogletestUnitTests.cc) +target_link_libraries(${TEST_NAME} src gtest gtest_main) +set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 11) +add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) + +# Set compiler option /FC for Visual Studio to to make the __FILE__ macro expand to full path. +# The __FILE__ macro can be used to get the path to current test file. +# Links: +# * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019 +# * https://docs.microsoft.com/en-us/cpp/build/reference/fc-full-path-of-source-code-file-in-diagnostics?view=vs-2019 +if (MSVC) + target_compile_options(${TEST_NAME} PRIVATE "/FC") +endif() diff --git a/cpp/test/cpp_googletest_unittest/GildedRoseGoogletestUnitTests.cc b/cpp/test/cpp_googletest_unittest/GildedRoseGoogletestUnitTests.cc new file mode 100644 index 00000000..654b3d66 --- /dev/null +++ b/cpp/test/cpp_googletest_unittest/GildedRoseGoogletestUnitTests.cc @@ -0,0 +1,10 @@ +#include +#include "GildedRose.h" + +TEST(GildedRoseTest, Foo) { + vector items; + items.push_back(Item("Foo", 0, 0)); + GildedRose app(items); + app.updateQuality(); + EXPECT_EQ("fixme", app.items[0].name); +} diff --git a/cpp/test/cpp_texttest/CMakeLists.txt b/cpp/test/cpp_texttest/CMakeLists.txt new file mode 100644 index 00000000..d9eb71ea --- /dev/null +++ b/cpp/test/cpp_texttest/CMakeLists.txt @@ -0,0 +1,15 @@ +set(TEST_NAME GildedRoseTextTests) +add_executable(${TEST_NAME} GildedRoseTextTests.cc) +target_sources(${TEST_NAME} PRIVATE GildedRoseTextTests.cc) +target_link_libraries(${TEST_NAME} lib src) +set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 11) +add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) + +# Set compiler option /FC for Visual Studio to to make the __FILE__ macro expand to full path. +# The __FILE__ macro is used by Catch2 to get the path to current test file. +# Links: +# * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019 +# * https://docs.microsoft.com/en-us/cpp/build/reference/fc-full-path-of-source-code-file-in-diagnostics?view=vs-2019 +if (MSVC) + target_compile_options(${TEST_NAME} PRIVATE "/FC") +endif() diff --git a/cpp/test/cpp_texttest/GildedRoseTextTests.cc b/cpp/test/cpp_texttest/GildedRoseTextTests.cc new file mode 100644 index 00000000..548d1d6a --- /dev/null +++ b/cpp/test/cpp_texttest/GildedRoseTextTests.cc @@ -0,0 +1,43 @@ +#include +#include "GildedRose.h" + +void print_item(Item& item) +{ + std::cout << item.name << ", " << item.sellIn << ", " << item.quality << std::endl; +} + +int main() +{ + vector items; + + items.push_back({"+5 Dexterity Vest", 10, 20}); + items.push_back({"Aged Brie", 2, 0}); + items.push_back({"Elixir of the Mongoose", 5, 7}); + items.push_back({"Sulfuras, Hand of Ragnaros", 0, 80}); + items.push_back({"Sulfuras, Hand of Ragnaros", -1, 80}); + items.push_back({"Backstage passes to a TAFKAL80ETC concert", 15, 20}); + items.push_back({"Backstage passes to a TAFKAL80ETC concert", 10, 49}); + items.push_back({"Backstage passes to a TAFKAL80ETC concert", 5, 49}); + + // this Conjured item doesn't yet work properly + items.push_back({"Conjured Mana Cake", 3, 6}); + + std::cout << "OMGHAI!" << std::endl; + + GildedRose app(items); + + for (int day = 0; day <= 30; day++) + { + std::cout << "-------- day " << day << " --------" << std::endl; + std::cout << "name, sellIn, quality" << std::endl; + for (auto& item : items) + { + print_item(item); + } + std::cout << std::endl; + app.updateQuality(); + } + return 0; +} + + diff --git a/csharp.NUnit/.gitignore b/csharp.NUnit/.gitignore new file mode 100644 index 00000000..b0e5bb0c --- /dev/null +++ b/csharp.NUnit/.gitignore @@ -0,0 +1,300 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs +.vscode diff --git a/csharp.NUnit/GildedRose.sln b/csharp.NUnit/GildedRose.sln new file mode 100644 index 00000000..fc591ee5 --- /dev/null +++ b/csharp.NUnit/GildedRose.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.34916.146 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GildedRose", "GildedRose\GildedRose.csproj", "{D781C52B-92C0-48BF-8414-177495DF4174}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GildedRoseTests", "GildedRoseTests\GildedRoseTests.csproj", "{CB6715CE-A283-4C70-9C1B-F58822077731}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{848F53B4-A532-4386-9DC3-1A477E7D6FCF}" + ProjectSection(SolutionItems) = preProject + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D781C52B-92C0-48BF-8414-177495DF4174}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D781C52B-92C0-48BF-8414-177495DF4174}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D781C52B-92C0-48BF-8414-177495DF4174}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D781C52B-92C0-48BF-8414-177495DF4174}.Release|Any CPU.Build.0 = Release|Any CPU + {CB6715CE-A283-4C70-9C1B-F58822077731}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB6715CE-A283-4C70-9C1B-F58822077731}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB6715CE-A283-4C70-9C1B-F58822077731}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB6715CE-A283-4C70-9C1B-F58822077731}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {02785EA4-86A5-4E57-9A9A-6998FAE1E617} + EndGlobalSection +EndGlobal diff --git a/csharp.NUnit/GildedRose/GildedRose.cs b/csharp.NUnit/GildedRose/GildedRose.cs new file mode 100644 index 00000000..c08bf5f8 --- /dev/null +++ b/csharp.NUnit/GildedRose/GildedRose.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; + +namespace GildedRoseKata; + +public class GildedRose +{ + IList Items; + + public GildedRose(IList Items) + { + this.Items = Items; + } + + public void UpdateQuality() + { + for (var i = 0; i < Items.Count; i++) + { + if (Items[i].Name != "Aged Brie" && Items[i].Name != "Backstage passes to a TAFKAL80ETC concert") + { + if (Items[i].Quality > 0) + { + if (Items[i].Name != "Sulfuras, Hand of Ragnaros") + { + Items[i].Quality = Items[i].Quality - 1; + } + } + } + else + { + if (Items[i].Quality < 50) + { + Items[i].Quality = Items[i].Quality + 1; + + if (Items[i].Name == "Backstage passes to a TAFKAL80ETC concert") + { + if (Items[i].SellIn < 11) + { + if (Items[i].Quality < 50) + { + Items[i].Quality = Items[i].Quality + 1; + } + } + + if (Items[i].SellIn < 6) + { + if (Items[i].Quality < 50) + { + Items[i].Quality = Items[i].Quality + 1; + } + } + } + } + } + + if (Items[i].Name != "Sulfuras, Hand of Ragnaros") + { + Items[i].SellIn = Items[i].SellIn - 1; + } + + if (Items[i].SellIn < 0) + { + if (Items[i].Name != "Aged Brie") + { + if (Items[i].Name != "Backstage passes to a TAFKAL80ETC concert") + { + if (Items[i].Quality > 0) + { + if (Items[i].Name != "Sulfuras, Hand of Ragnaros") + { + Items[i].Quality = Items[i].Quality - 1; + } + } + } + else + { + Items[i].Quality = Items[i].Quality - Items[i].Quality; + } + } + else + { + if (Items[i].Quality < 50) + { + Items[i].Quality = Items[i].Quality + 1; + } + } + } + } + } +} \ No newline at end of file diff --git a/csharp.NUnit/GildedRose/GildedRose.csproj b/csharp.NUnit/GildedRose/GildedRose.csproj new file mode 100644 index 00000000..0e318c1b --- /dev/null +++ b/csharp.NUnit/GildedRose/GildedRose.csproj @@ -0,0 +1,9 @@ + + + + Exe + GildedRoseKata + net8.0 + + + \ No newline at end of file diff --git a/csharp.NUnit/GildedRose/Item.cs b/csharp.NUnit/GildedRose/Item.cs new file mode 100644 index 00000000..5ad18dea --- /dev/null +++ b/csharp.NUnit/GildedRose/Item.cs @@ -0,0 +1,8 @@ +namespace GildedRoseKata; + +public class Item +{ + public string Name { get; set; } + public int SellIn { get; set; } + public int Quality { get; set; } +} \ No newline at end of file diff --git a/csharp.NUnit/GildedRose/Program.cs b/csharp.NUnit/GildedRose/Program.cs new file mode 100644 index 00000000..1c71999e --- /dev/null +++ b/csharp.NUnit/GildedRose/Program.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + +namespace GildedRoseKata; + +public class Program +{ + public static void Main(string[] args) + { + Console.WriteLine("OMGHAI!"); + + IList items = new List + { + new Item {Name = "+5 Dexterity Vest", SellIn = 10, Quality = 20}, + new Item {Name = "Aged Brie", SellIn = 2, Quality = 0}, + new Item {Name = "Elixir of the Mongoose", SellIn = 5, Quality = 7}, + new Item {Name = "Sulfuras, Hand of Ragnaros", SellIn = 0, Quality = 80}, + new Item {Name = "Sulfuras, Hand of Ragnaros", SellIn = -1, Quality = 80}, + new Item + { + Name = "Backstage passes to a TAFKAL80ETC concert", + SellIn = 15, + Quality = 20 + }, + new Item + { + Name = "Backstage passes to a TAFKAL80ETC concert", + SellIn = 10, + Quality = 49 + }, + new Item + { + Name = "Backstage passes to a TAFKAL80ETC concert", + SellIn = 5, + Quality = 49 + }, + // this conjured item does not work properly yet + new Item {Name = "Conjured Mana Cake", SellIn = 3, Quality = 6} + }; + + var app = new GildedRose(items); + + int days = 2; + if (args.Length > 0) + { + days = int.Parse(args[0]) + 1; + } + + for (var i = 0; i < days; i++) + { + Console.WriteLine("-------- day " + i + " --------"); + Console.WriteLine("name, sellIn, quality"); + for (var j = 0; j < items.Count; j++) + { + Console.WriteLine(items[j].Name + ", " + items[j].SellIn + ", " + items[j].Quality); + } + Console.WriteLine(""); + app.UpdateQuality(); + } + } +} \ No newline at end of file diff --git a/csharp.NUnit/GildedRoseTests/ApprovalTest.ThirtyDays.verified.txt b/csharp.NUnit/GildedRoseTests/ApprovalTest.ThirtyDays.verified.txt new file mode 100644 index 00000000..cd66984f --- /dev/null +++ b/csharp.NUnit/GildedRoseTests/ApprovalTest.ThirtyDays.verified.txt @@ -0,0 +1,373 @@ +OMGHAI! +-------- day 0 -------- +name, sellIn, quality ++5 Dexterity Vest, 10, 20 +Aged Brie, 2, 0 +Elixir of the Mongoose, 5, 7 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 15, 20 +Backstage passes to a TAFKAL80ETC concert, 10, 49 +Backstage passes to a TAFKAL80ETC concert, 5, 49 +Conjured Mana Cake, 3, 6 + +-------- day 1 -------- +name, sellIn, quality ++5 Dexterity Vest, 9, 19 +Aged Brie, 1, 1 +Elixir of the Mongoose, 4, 6 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 14, 21 +Backstage passes to a TAFKAL80ETC concert, 9, 50 +Backstage passes to a TAFKAL80ETC concert, 4, 50 +Conjured Mana Cake, 2, 5 + +-------- day 2 -------- +name, sellIn, quality ++5 Dexterity Vest, 8, 18 +Aged Brie, 0, 2 +Elixir of the Mongoose, 3, 5 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 13, 22 +Backstage passes to a TAFKAL80ETC concert, 8, 50 +Backstage passes to a TAFKAL80ETC concert, 3, 50 +Conjured Mana Cake, 1, 4 + +-------- day 3 -------- +name, sellIn, quality ++5 Dexterity Vest, 7, 17 +Aged Brie, -1, 4 +Elixir of the Mongoose, 2, 4 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 12, 23 +Backstage passes to a TAFKAL80ETC concert, 7, 50 +Backstage passes to a TAFKAL80ETC concert, 2, 50 +Conjured Mana Cake, 0, 3 + +-------- day 4 -------- +name, sellIn, quality ++5 Dexterity Vest, 6, 16 +Aged Brie, -2, 6 +Elixir of the Mongoose, 1, 3 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 11, 24 +Backstage passes to a TAFKAL80ETC concert, 6, 50 +Backstage passes to a TAFKAL80ETC concert, 1, 50 +Conjured Mana Cake, -1, 1 + +-------- day 5 -------- +name, sellIn, quality ++5 Dexterity Vest, 5, 15 +Aged Brie, -3, 8 +Elixir of the Mongoose, 0, 2 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 10, 25 +Backstage passes to a TAFKAL80ETC concert, 5, 50 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Conjured Mana Cake, -2, 0 + +-------- day 6 -------- +name, sellIn, quality ++5 Dexterity Vest, 4, 14 +Aged Brie, -4, 10 +Elixir of the Mongoose, -1, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 9, 27 +Backstage passes to a TAFKAL80ETC concert, 4, 50 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Conjured Mana Cake, -3, 0 + +-------- day 7 -------- +name, sellIn, quality ++5 Dexterity Vest, 3, 13 +Aged Brie, -5, 12 +Elixir of the Mongoose, -2, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 8, 29 +Backstage passes to a TAFKAL80ETC concert, 3, 50 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Conjured Mana Cake, -4, 0 + +-------- day 8 -------- +name, sellIn, quality ++5 Dexterity Vest, 2, 12 +Aged Brie, -6, 14 +Elixir of the Mongoose, -3, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 7, 31 +Backstage passes to a TAFKAL80ETC concert, 2, 50 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Conjured Mana Cake, -5, 0 + +-------- day 9 -------- +name, sellIn, quality ++5 Dexterity Vest, 1, 11 +Aged Brie, -7, 16 +Elixir of the Mongoose, -4, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 6, 33 +Backstage passes to a TAFKAL80ETC concert, 1, 50 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Conjured Mana Cake, -6, 0 + +-------- day 10 -------- +name, sellIn, quality ++5 Dexterity Vest, 0, 10 +Aged Brie, -8, 18 +Elixir of the Mongoose, -5, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 5, 35 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Conjured Mana Cake, -7, 0 + +-------- day 11 -------- +name, sellIn, quality ++5 Dexterity Vest, -1, 8 +Aged Brie, -9, 20 +Elixir of the Mongoose, -6, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 4, 38 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Conjured Mana Cake, -8, 0 + +-------- day 12 -------- +name, sellIn, quality ++5 Dexterity Vest, -2, 6 +Aged Brie, -10, 22 +Elixir of the Mongoose, -7, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 3, 41 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Conjured Mana Cake, -9, 0 + +-------- day 13 -------- +name, sellIn, quality ++5 Dexterity Vest, -3, 4 +Aged Brie, -11, 24 +Elixir of the Mongoose, -8, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 2, 44 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Conjured Mana Cake, -10, 0 + +-------- day 14 -------- +name, sellIn, quality ++5 Dexterity Vest, -4, 2 +Aged Brie, -12, 26 +Elixir of the Mongoose, -9, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 1, 47 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Conjured Mana Cake, -11, 0 + +-------- day 15 -------- +name, sellIn, quality ++5 Dexterity Vest, -5, 0 +Aged Brie, -13, 28 +Elixir of the Mongoose, -10, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Conjured Mana Cake, -12, 0 + +-------- day 16 -------- +name, sellIn, quality ++5 Dexterity Vest, -6, 0 +Aged Brie, -14, 30 +Elixir of the Mongoose, -11, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Conjured Mana Cake, -13, 0 + +-------- day 17 -------- +name, sellIn, quality ++5 Dexterity Vest, -7, 0 +Aged Brie, -15, 32 +Elixir of the Mongoose, -12, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Conjured Mana Cake, -14, 0 + +-------- day 18 -------- +name, sellIn, quality ++5 Dexterity Vest, -8, 0 +Aged Brie, -16, 34 +Elixir of the Mongoose, -13, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Conjured Mana Cake, -15, 0 + +-------- day 19 -------- +name, sellIn, quality ++5 Dexterity Vest, -9, 0 +Aged Brie, -17, 36 +Elixir of the Mongoose, -14, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Conjured Mana Cake, -16, 0 + +-------- day 20 -------- +name, sellIn, quality ++5 Dexterity Vest, -10, 0 +Aged Brie, -18, 38 +Elixir of the Mongoose, -15, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Conjured Mana Cake, -17, 0 + +-------- day 21 -------- +name, sellIn, quality ++5 Dexterity Vest, -11, 0 +Aged Brie, -19, 40 +Elixir of the Mongoose, -16, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Backstage passes to a TAFKAL80ETC concert, -16, 0 +Conjured Mana Cake, -18, 0 + +-------- day 22 -------- +name, sellIn, quality ++5 Dexterity Vest, -12, 0 +Aged Brie, -20, 42 +Elixir of the Mongoose, -17, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Backstage passes to a TAFKAL80ETC concert, -17, 0 +Conjured Mana Cake, -19, 0 + +-------- day 23 -------- +name, sellIn, quality ++5 Dexterity Vest, -13, 0 +Aged Brie, -21, 44 +Elixir of the Mongoose, -18, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Backstage passes to a TAFKAL80ETC concert, -18, 0 +Conjured Mana Cake, -20, 0 + +-------- day 24 -------- +name, sellIn, quality ++5 Dexterity Vest, -14, 0 +Aged Brie, -22, 46 +Elixir of the Mongoose, -19, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Backstage passes to a TAFKAL80ETC concert, -19, 0 +Conjured Mana Cake, -21, 0 + +-------- day 25 -------- +name, sellIn, quality ++5 Dexterity Vest, -15, 0 +Aged Brie, -23, 48 +Elixir of the Mongoose, -20, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Backstage passes to a TAFKAL80ETC concert, -20, 0 +Conjured Mana Cake, -22, 0 + +-------- day 26 -------- +name, sellIn, quality ++5 Dexterity Vest, -16, 0 +Aged Brie, -24, 50 +Elixir of the Mongoose, -21, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Backstage passes to a TAFKAL80ETC concert, -16, 0 +Backstage passes to a TAFKAL80ETC concert, -21, 0 +Conjured Mana Cake, -23, 0 + +-------- day 27 -------- +name, sellIn, quality ++5 Dexterity Vest, -17, 0 +Aged Brie, -25, 50 +Elixir of the Mongoose, -22, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Backstage passes to a TAFKAL80ETC concert, -17, 0 +Backstage passes to a TAFKAL80ETC concert, -22, 0 +Conjured Mana Cake, -24, 0 + +-------- day 28 -------- +name, sellIn, quality ++5 Dexterity Vest, -18, 0 +Aged Brie, -26, 50 +Elixir of the Mongoose, -23, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Backstage passes to a TAFKAL80ETC concert, -18, 0 +Backstage passes to a TAFKAL80ETC concert, -23, 0 +Conjured Mana Cake, -25, 0 + +-------- day 29 -------- +name, sellIn, quality ++5 Dexterity Vest, -19, 0 +Aged Brie, -27, 50 +Elixir of the Mongoose, -24, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Backstage passes to a TAFKAL80ETC concert, -19, 0 +Backstage passes to a TAFKAL80ETC concert, -24, 0 +Conjured Mana Cake, -26, 0 + +-------- day 30 -------- +name, sellIn, quality ++5 Dexterity Vest, -20, 0 +Aged Brie, -28, 50 +Elixir of the Mongoose, -25, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Backstage passes to a TAFKAL80ETC concert, -20, 0 +Backstage passes to a TAFKAL80ETC concert, -25, 0 +Conjured Mana Cake, -27, 0 + diff --git a/csharp.NUnit/GildedRoseTests/ApprovalTest.cs b/csharp.NUnit/GildedRoseTests/ApprovalTest.cs new file mode 100644 index 00000000..32828859 --- /dev/null +++ b/csharp.NUnit/GildedRoseTests/ApprovalTest.cs @@ -0,0 +1,28 @@ +using GildedRoseKata; + +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +using VerifyNUnit; + +using NUnit.Framework; + +namespace GildedRoseTests; + +public class ApprovalTest +{ + [Test] + public Task ThirtyDays() + { + var fakeOutput = new StringBuilder(); + Console.SetOut(new StringWriter(fakeOutput)); + Console.SetIn(new StringReader($"a{Environment.NewLine}")); + + Program.Main(new string[] { "30" }); + var output = fakeOutput.ToString(); + + return Verifier.Verify(output); + } +} \ No newline at end of file diff --git a/csharp.NUnit/GildedRoseTests/GildedRoseTest.cs b/csharp.NUnit/GildedRoseTests/GildedRoseTest.cs new file mode 100644 index 00000000..6f7c585c --- /dev/null +++ b/csharp.NUnit/GildedRoseTests/GildedRoseTest.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using GildedRoseKata; +using NUnit.Framework; + +namespace GildedRoseTests; + +public class GildedRoseTest +{ + [Test] + public void Foo() + { + var items = new List { new Item { Name = "foo", SellIn = 0, Quality = 0 } }; + var app = new GildedRose(items); + app.UpdateQuality(); + Assert.That(items[0].Name, Is.EqualTo("fixme")); + } +} \ No newline at end of file diff --git a/csharp.NUnit/GildedRoseTests/GildedRoseTests.csproj b/csharp.NUnit/GildedRoseTests/GildedRoseTests.csproj new file mode 100644 index 00000000..c84df6ce --- /dev/null +++ b/csharp.NUnit/GildedRoseTests/GildedRoseTests.csproj @@ -0,0 +1,23 @@ + + + + Exe + net8.0 + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + \ No newline at end of file diff --git a/csharp.NUnit/README.md b/csharp.NUnit/README.md new file mode 100644 index 00000000..8e1eaea9 --- /dev/null +++ b/csharp.NUnit/README.md @@ -0,0 +1,24 @@ +# Gilded Rose starting position in C# NUnit + +## Build the project + +Use your normal build tools to build the projects in Debug mode. +For example, you can use the `dotnet` command line tool: + +``` cmd +dotnet build GildedRose.sln -c Debug +``` + +## Run the Gilded Rose Command-Line program + +For e.g. 10 days: + +``` cmd +GildedRose/bin/Debug/net8.0/GildedRose 10 +``` + +## Run all the unit tests + +``` cmd +dotnet test +``` \ No newline at end of file diff --git a/csharp.xUnit/.gitignore b/csharp.xUnit/.gitignore new file mode 100644 index 00000000..b0e5bb0c --- /dev/null +++ b/csharp.xUnit/.gitignore @@ -0,0 +1,300 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs +.vscode diff --git a/csharp.xUnit/GildedRose.sln b/csharp.xUnit/GildedRose.sln new file mode 100644 index 00000000..59e41407 --- /dev/null +++ b/csharp.xUnit/GildedRose.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.34916.146 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GildedRose", "GildedRose\GildedRose.csproj", "{D781C52B-92C0-48BF-8414-177495DF4174}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GildedRoseTests", "GildedRoseTests\GildedRoseTests.csproj", "{CB6715CE-A283-4C70-9C1B-F58822077731}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C4DC2F6D-79E1-4AFB-8B3E-9305F18439CE}" + ProjectSection(SolutionItems) = preProject + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D781C52B-92C0-48BF-8414-177495DF4174}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D781C52B-92C0-48BF-8414-177495DF4174}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D781C52B-92C0-48BF-8414-177495DF4174}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D781C52B-92C0-48BF-8414-177495DF4174}.Release|Any CPU.Build.0 = Release|Any CPU + {CB6715CE-A283-4C70-9C1B-F58822077731}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB6715CE-A283-4C70-9C1B-F58822077731}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB6715CE-A283-4C70-9C1B-F58822077731}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB6715CE-A283-4C70-9C1B-F58822077731}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {02785EA4-86A5-4E57-9A9A-6998FAE1E617} + EndGlobalSection +EndGlobal diff --git a/csharp.xUnit/GildedRose/GildedRose.cs b/csharp.xUnit/GildedRose/GildedRose.cs new file mode 100644 index 00000000..c08bf5f8 --- /dev/null +++ b/csharp.xUnit/GildedRose/GildedRose.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; + +namespace GildedRoseKata; + +public class GildedRose +{ + IList Items; + + public GildedRose(IList Items) + { + this.Items = Items; + } + + public void UpdateQuality() + { + for (var i = 0; i < Items.Count; i++) + { + if (Items[i].Name != "Aged Brie" && Items[i].Name != "Backstage passes to a TAFKAL80ETC concert") + { + if (Items[i].Quality > 0) + { + if (Items[i].Name != "Sulfuras, Hand of Ragnaros") + { + Items[i].Quality = Items[i].Quality - 1; + } + } + } + else + { + if (Items[i].Quality < 50) + { + Items[i].Quality = Items[i].Quality + 1; + + if (Items[i].Name == "Backstage passes to a TAFKAL80ETC concert") + { + if (Items[i].SellIn < 11) + { + if (Items[i].Quality < 50) + { + Items[i].Quality = Items[i].Quality + 1; + } + } + + if (Items[i].SellIn < 6) + { + if (Items[i].Quality < 50) + { + Items[i].Quality = Items[i].Quality + 1; + } + } + } + } + } + + if (Items[i].Name != "Sulfuras, Hand of Ragnaros") + { + Items[i].SellIn = Items[i].SellIn - 1; + } + + if (Items[i].SellIn < 0) + { + if (Items[i].Name != "Aged Brie") + { + if (Items[i].Name != "Backstage passes to a TAFKAL80ETC concert") + { + if (Items[i].Quality > 0) + { + if (Items[i].Name != "Sulfuras, Hand of Ragnaros") + { + Items[i].Quality = Items[i].Quality - 1; + } + } + } + else + { + Items[i].Quality = Items[i].Quality - Items[i].Quality; + } + } + else + { + if (Items[i].Quality < 50) + { + Items[i].Quality = Items[i].Quality + 1; + } + } + } + } + } +} \ No newline at end of file diff --git a/csharp.xUnit/GildedRose/GildedRose.csproj b/csharp.xUnit/GildedRose/GildedRose.csproj new file mode 100644 index 00000000..0e318c1b --- /dev/null +++ b/csharp.xUnit/GildedRose/GildedRose.csproj @@ -0,0 +1,9 @@ + + + + Exe + GildedRoseKata + net8.0 + + + \ No newline at end of file diff --git a/csharp.xUnit/GildedRose/Item.cs b/csharp.xUnit/GildedRose/Item.cs new file mode 100644 index 00000000..5ad18dea --- /dev/null +++ b/csharp.xUnit/GildedRose/Item.cs @@ -0,0 +1,8 @@ +namespace GildedRoseKata; + +public class Item +{ + public string Name { get; set; } + public int SellIn { get; set; } + public int Quality { get; set; } +} \ No newline at end of file diff --git a/csharp.xUnit/GildedRose/Program.cs b/csharp.xUnit/GildedRose/Program.cs new file mode 100644 index 00000000..1c71999e --- /dev/null +++ b/csharp.xUnit/GildedRose/Program.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + +namespace GildedRoseKata; + +public class Program +{ + public static void Main(string[] args) + { + Console.WriteLine("OMGHAI!"); + + IList items = new List + { + new Item {Name = "+5 Dexterity Vest", SellIn = 10, Quality = 20}, + new Item {Name = "Aged Brie", SellIn = 2, Quality = 0}, + new Item {Name = "Elixir of the Mongoose", SellIn = 5, Quality = 7}, + new Item {Name = "Sulfuras, Hand of Ragnaros", SellIn = 0, Quality = 80}, + new Item {Name = "Sulfuras, Hand of Ragnaros", SellIn = -1, Quality = 80}, + new Item + { + Name = "Backstage passes to a TAFKAL80ETC concert", + SellIn = 15, + Quality = 20 + }, + new Item + { + Name = "Backstage passes to a TAFKAL80ETC concert", + SellIn = 10, + Quality = 49 + }, + new Item + { + Name = "Backstage passes to a TAFKAL80ETC concert", + SellIn = 5, + Quality = 49 + }, + // this conjured item does not work properly yet + new Item {Name = "Conjured Mana Cake", SellIn = 3, Quality = 6} + }; + + var app = new GildedRose(items); + + int days = 2; + if (args.Length > 0) + { + days = int.Parse(args[0]) + 1; + } + + for (var i = 0; i < days; i++) + { + Console.WriteLine("-------- day " + i + " --------"); + Console.WriteLine("name, sellIn, quality"); + for (var j = 0; j < items.Count; j++) + { + Console.WriteLine(items[j].Name + ", " + items[j].SellIn + ", " + items[j].Quality); + } + Console.WriteLine(""); + app.UpdateQuality(); + } + } +} \ No newline at end of file diff --git a/csharp.xUnit/GildedRoseTests/ApprovalTest.ThirtyDays.verified.txt b/csharp.xUnit/GildedRoseTests/ApprovalTest.ThirtyDays.verified.txt new file mode 100644 index 00000000..cd66984f --- /dev/null +++ b/csharp.xUnit/GildedRoseTests/ApprovalTest.ThirtyDays.verified.txt @@ -0,0 +1,373 @@ +OMGHAI! +-------- day 0 -------- +name, sellIn, quality ++5 Dexterity Vest, 10, 20 +Aged Brie, 2, 0 +Elixir of the Mongoose, 5, 7 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 15, 20 +Backstage passes to a TAFKAL80ETC concert, 10, 49 +Backstage passes to a TAFKAL80ETC concert, 5, 49 +Conjured Mana Cake, 3, 6 + +-------- day 1 -------- +name, sellIn, quality ++5 Dexterity Vest, 9, 19 +Aged Brie, 1, 1 +Elixir of the Mongoose, 4, 6 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 14, 21 +Backstage passes to a TAFKAL80ETC concert, 9, 50 +Backstage passes to a TAFKAL80ETC concert, 4, 50 +Conjured Mana Cake, 2, 5 + +-------- day 2 -------- +name, sellIn, quality ++5 Dexterity Vest, 8, 18 +Aged Brie, 0, 2 +Elixir of the Mongoose, 3, 5 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 13, 22 +Backstage passes to a TAFKAL80ETC concert, 8, 50 +Backstage passes to a TAFKAL80ETC concert, 3, 50 +Conjured Mana Cake, 1, 4 + +-------- day 3 -------- +name, sellIn, quality ++5 Dexterity Vest, 7, 17 +Aged Brie, -1, 4 +Elixir of the Mongoose, 2, 4 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 12, 23 +Backstage passes to a TAFKAL80ETC concert, 7, 50 +Backstage passes to a TAFKAL80ETC concert, 2, 50 +Conjured Mana Cake, 0, 3 + +-------- day 4 -------- +name, sellIn, quality ++5 Dexterity Vest, 6, 16 +Aged Brie, -2, 6 +Elixir of the Mongoose, 1, 3 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 11, 24 +Backstage passes to a TAFKAL80ETC concert, 6, 50 +Backstage passes to a TAFKAL80ETC concert, 1, 50 +Conjured Mana Cake, -1, 1 + +-------- day 5 -------- +name, sellIn, quality ++5 Dexterity Vest, 5, 15 +Aged Brie, -3, 8 +Elixir of the Mongoose, 0, 2 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 10, 25 +Backstage passes to a TAFKAL80ETC concert, 5, 50 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Conjured Mana Cake, -2, 0 + +-------- day 6 -------- +name, sellIn, quality ++5 Dexterity Vest, 4, 14 +Aged Brie, -4, 10 +Elixir of the Mongoose, -1, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 9, 27 +Backstage passes to a TAFKAL80ETC concert, 4, 50 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Conjured Mana Cake, -3, 0 + +-------- day 7 -------- +name, sellIn, quality ++5 Dexterity Vest, 3, 13 +Aged Brie, -5, 12 +Elixir of the Mongoose, -2, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 8, 29 +Backstage passes to a TAFKAL80ETC concert, 3, 50 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Conjured Mana Cake, -4, 0 + +-------- day 8 -------- +name, sellIn, quality ++5 Dexterity Vest, 2, 12 +Aged Brie, -6, 14 +Elixir of the Mongoose, -3, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 7, 31 +Backstage passes to a TAFKAL80ETC concert, 2, 50 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Conjured Mana Cake, -5, 0 + +-------- day 9 -------- +name, sellIn, quality ++5 Dexterity Vest, 1, 11 +Aged Brie, -7, 16 +Elixir of the Mongoose, -4, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 6, 33 +Backstage passes to a TAFKAL80ETC concert, 1, 50 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Conjured Mana Cake, -6, 0 + +-------- day 10 -------- +name, sellIn, quality ++5 Dexterity Vest, 0, 10 +Aged Brie, -8, 18 +Elixir of the Mongoose, -5, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 5, 35 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Conjured Mana Cake, -7, 0 + +-------- day 11 -------- +name, sellIn, quality ++5 Dexterity Vest, -1, 8 +Aged Brie, -9, 20 +Elixir of the Mongoose, -6, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 4, 38 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Conjured Mana Cake, -8, 0 + +-------- day 12 -------- +name, sellIn, quality ++5 Dexterity Vest, -2, 6 +Aged Brie, -10, 22 +Elixir of the Mongoose, -7, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 3, 41 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Conjured Mana Cake, -9, 0 + +-------- day 13 -------- +name, sellIn, quality ++5 Dexterity Vest, -3, 4 +Aged Brie, -11, 24 +Elixir of the Mongoose, -8, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 2, 44 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Conjured Mana Cake, -10, 0 + +-------- day 14 -------- +name, sellIn, quality ++5 Dexterity Vest, -4, 2 +Aged Brie, -12, 26 +Elixir of the Mongoose, -9, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 1, 47 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Conjured Mana Cake, -11, 0 + +-------- day 15 -------- +name, sellIn, quality ++5 Dexterity Vest, -5, 0 +Aged Brie, -13, 28 +Elixir of the Mongoose, -10, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Conjured Mana Cake, -12, 0 + +-------- day 16 -------- +name, sellIn, quality ++5 Dexterity Vest, -6, 0 +Aged Brie, -14, 30 +Elixir of the Mongoose, -11, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Conjured Mana Cake, -13, 0 + +-------- day 17 -------- +name, sellIn, quality ++5 Dexterity Vest, -7, 0 +Aged Brie, -15, 32 +Elixir of the Mongoose, -12, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Conjured Mana Cake, -14, 0 + +-------- day 18 -------- +name, sellIn, quality ++5 Dexterity Vest, -8, 0 +Aged Brie, -16, 34 +Elixir of the Mongoose, -13, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Conjured Mana Cake, -15, 0 + +-------- day 19 -------- +name, sellIn, quality ++5 Dexterity Vest, -9, 0 +Aged Brie, -17, 36 +Elixir of the Mongoose, -14, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Conjured Mana Cake, -16, 0 + +-------- day 20 -------- +name, sellIn, quality ++5 Dexterity Vest, -10, 0 +Aged Brie, -18, 38 +Elixir of the Mongoose, -15, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Conjured Mana Cake, -17, 0 + +-------- day 21 -------- +name, sellIn, quality ++5 Dexterity Vest, -11, 0 +Aged Brie, -19, 40 +Elixir of the Mongoose, -16, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Backstage passes to a TAFKAL80ETC concert, -16, 0 +Conjured Mana Cake, -18, 0 + +-------- day 22 -------- +name, sellIn, quality ++5 Dexterity Vest, -12, 0 +Aged Brie, -20, 42 +Elixir of the Mongoose, -17, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Backstage passes to a TAFKAL80ETC concert, -17, 0 +Conjured Mana Cake, -19, 0 + +-------- day 23 -------- +name, sellIn, quality ++5 Dexterity Vest, -13, 0 +Aged Brie, -21, 44 +Elixir of the Mongoose, -18, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Backstage passes to a TAFKAL80ETC concert, -18, 0 +Conjured Mana Cake, -20, 0 + +-------- day 24 -------- +name, sellIn, quality ++5 Dexterity Vest, -14, 0 +Aged Brie, -22, 46 +Elixir of the Mongoose, -19, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Backstage passes to a TAFKAL80ETC concert, -19, 0 +Conjured Mana Cake, -21, 0 + +-------- day 25 -------- +name, sellIn, quality ++5 Dexterity Vest, -15, 0 +Aged Brie, -23, 48 +Elixir of the Mongoose, -20, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Backstage passes to a TAFKAL80ETC concert, -20, 0 +Conjured Mana Cake, -22, 0 + +-------- day 26 -------- +name, sellIn, quality ++5 Dexterity Vest, -16, 0 +Aged Brie, -24, 50 +Elixir of the Mongoose, -21, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Backstage passes to a TAFKAL80ETC concert, -16, 0 +Backstage passes to a TAFKAL80ETC concert, -21, 0 +Conjured Mana Cake, -23, 0 + +-------- day 27 -------- +name, sellIn, quality ++5 Dexterity Vest, -17, 0 +Aged Brie, -25, 50 +Elixir of the Mongoose, -22, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Backstage passes to a TAFKAL80ETC concert, -17, 0 +Backstage passes to a TAFKAL80ETC concert, -22, 0 +Conjured Mana Cake, -24, 0 + +-------- day 28 -------- +name, sellIn, quality ++5 Dexterity Vest, -18, 0 +Aged Brie, -26, 50 +Elixir of the Mongoose, -23, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Backstage passes to a TAFKAL80ETC concert, -18, 0 +Backstage passes to a TAFKAL80ETC concert, -23, 0 +Conjured Mana Cake, -25, 0 + +-------- day 29 -------- +name, sellIn, quality ++5 Dexterity Vest, -19, 0 +Aged Brie, -27, 50 +Elixir of the Mongoose, -24, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Backstage passes to a TAFKAL80ETC concert, -19, 0 +Backstage passes to a TAFKAL80ETC concert, -24, 0 +Conjured Mana Cake, -26, 0 + +-------- day 30 -------- +name, sellIn, quality ++5 Dexterity Vest, -20, 0 +Aged Brie, -28, 50 +Elixir of the Mongoose, -25, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Backstage passes to a TAFKAL80ETC concert, -20, 0 +Backstage passes to a TAFKAL80ETC concert, -25, 0 +Conjured Mana Cake, -27, 0 + diff --git a/csharp.xUnit/GildedRoseTests/ApprovalTest.cs b/csharp.xUnit/GildedRoseTests/ApprovalTest.cs new file mode 100644 index 00000000..3020c86f --- /dev/null +++ b/csharp.xUnit/GildedRoseTests/ApprovalTest.cs @@ -0,0 +1,28 @@ +using GildedRoseKata; + +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +using VerifyXunit; + +using Xunit; + +namespace GildedRoseTests; + +public class ApprovalTest +{ + [Fact] + public Task ThirtyDays() + { + var fakeoutput = new StringBuilder(); + Console.SetOut(new StringWriter(fakeoutput)); + Console.SetIn(new StringReader($"a{Environment.NewLine}")); + + Program.Main(new string[] { "30" }); + var output = fakeoutput.ToString(); + + return Verifier.Verify(output); + } +} \ No newline at end of file diff --git a/csharp.xUnit/GildedRoseTests/GildedRoseTest.cs b/csharp.xUnit/GildedRoseTests/GildedRoseTest.cs new file mode 100644 index 00000000..63fd7b1b --- /dev/null +++ b/csharp.xUnit/GildedRoseTests/GildedRoseTest.cs @@ -0,0 +1,17 @@ +using Xunit; +using System.Collections.Generic; +using GildedRoseKata; + +namespace GildedRoseTests; + +public class GildedRoseTest +{ + [Fact] + public void foo() + { + IList Items = new List { new Item { Name = "foo", SellIn = 0, Quality = 0 } }; + GildedRose app = new GildedRose(Items); + app.UpdateQuality(); + Assert.Equal("fixme", Items[0].Name); + } +} \ No newline at end of file diff --git a/csharp.xUnit/GildedRoseTests/GildedRoseTests.csproj b/csharp.xUnit/GildedRoseTests/GildedRoseTests.csproj new file mode 100644 index 00000000..bed0eb47 --- /dev/null +++ b/csharp.xUnit/GildedRoseTests/GildedRoseTests.csproj @@ -0,0 +1,25 @@ + + + + net8.0 + false + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/csharp.xUnit/README.md b/csharp.xUnit/README.md new file mode 100644 index 00000000..92b3cb9e --- /dev/null +++ b/csharp.xUnit/README.md @@ -0,0 +1,24 @@ +# Gilded Rose starting position in C# xUnit + +## Build the project + +Use your normal build tools to build the projects in Debug mode. +For example, you can use the `dotnet` command line tool: + +``` cmd +dotnet build GildedRose.sln -c Debug +``` + +## Run the Gilded Rose Command-Line program + +For e.g. 10 days: + +``` cmd +GildedRose/bin/Debug/net8.0/GildedRose 10 +``` + +## Run all the unit tests + +``` cmd +dotnet test +``` \ No newline at end of file diff --git a/d/.gitignore b/d/.gitignore new file mode 100644 index 00000000..33ba95ba --- /dev/null +++ b/d/.gitignore @@ -0,0 +1 @@ +.dub \ No newline at end of file diff --git a/d/dub.json b/d/dub.json new file mode 100644 index 00000000..cce9856b --- /dev/null +++ b/d/dub.json @@ -0,0 +1,16 @@ +{ + "name": "GildedRose", + "configurations": [ + { + "name": "GuildedRose", + "targetType": "executable", + "mainSourceFile": "src/GildedRoseTextTests.d" + }, + { + "name": "unittest", + "targetType": "executable", + "sourcePaths": ["test"], + "mainSourceFile": "test/GildedRoseUnitTests.d" + } + ] +} diff --git a/d/src/GildedRose.d b/d/src/GildedRose.d new file mode 100644 index 00000000..1b0a8795 --- /dev/null +++ b/d/src/GildedRose.d @@ -0,0 +1,93 @@ +struct Item +{ + string name; + int sellIn; + int quality; +} + +class GildedRose +{ +public: + Item[] items; + this(Item[] items) + { + this.items = items.dup; + } + + void updateQuality() + { + for (int i = 0; i < items.length; i++) + { + if (items[i].name != "Aged Brie" + && items[i].name != "Backstage passes to a TAFKAL80ETC concert") + { + if (items[i].quality > 0) + { + if (items[i].name != "Sulfuras, Hand of Ragnaros") + { + items[i].quality = items[i].quality - 1; + } + } + } + else + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + + if (items[i].name == "Backstage passes to a TAFKAL80ETC concert") + { + if (items[i].sellIn < 11) + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + + if (items[i].sellIn < 6) + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + } + } + } + + if (items[i].name != "Sulfuras, Hand of Ragnaros") + { + items[i].sellIn = items[i].sellIn - 1; + } + + if (items[i].sellIn < 0) + { + if (items[i].name != "Aged Brie") + { + if (items[i].name != "Backstage passes to a TAFKAL80ETC concert") + { + if (items[i].quality > 0) + { + if (items[i].name != "Sulfuras, Hand of Ragnaros") + { + items[i].quality = items[i].quality - 1; + } + } + } + else + { + items[i].quality = items[i].quality - items[i].quality; + } + } + else + { + if (items[i].quality < 50) + { + items[i].quality = items[i].quality + 1; + } + } + } + } + } +} diff --git a/d/src/GildedRoseTextTests.d b/d/src/GildedRoseTextTests.d new file mode 100644 index 00000000..3dc6887f --- /dev/null +++ b/d/src/GildedRoseTextTests.d @@ -0,0 +1,38 @@ +import GildedRose; + +int main() +{ + import std.stdio : writefln, writeln; + + Item[] items = [ + Item("+5 Dexterity Vest", 10, 20), + Item("Aged Brie", 2, 0), + Item("Elixir of the Mongoose", 5, 7), + Item("Sulfuras, Hand of Ragnaros", 0, 80), + Item("Sulfuras, Hand of Ragnaros", -1, 80), + Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this Conjured item doesn't yet work properly + Item("Conjured Mana Cake", 3, 6), + ]; + + auto app = new GildedRose(items); + + writeln("OMGHAI!"); + + for (int day = 0; day <= 30; day++) + { + writefln!"-------- day %s --------"(day); + writeln("Item(name, sellIn, quality)"); + foreach (item; app.items) + { + writeln(item); + } + writeln; + + app.updateQuality; + } + + return 0; +} diff --git a/d/test/GildedRoseUnitTests.d b/d/test/GildedRoseUnitTests.d new file mode 100644 index 00000000..ac58c703 --- /dev/null +++ b/d/test/GildedRoseUnitTests.d @@ -0,0 +1,25 @@ +import GildedRose; + +unittest +{ + Item[] items = [ Item("Foo", 0, 0)]; + auto app = new GildedRose(items); + + app.updateQuality; + + assert("fixme" == app.items[0].name); +} + +void example() +{ + Item[] items = [ + Item("+5 Dexterity Vest", 10, 20), + Item("Aged Brie", 2, 0), + Item("Elixir of the Mongoose", 5, 7), + Item("Sulfuras, Hand of Ragnaros", 0, 80), + Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + Item("Conjured Mana Cake", 3, 6), + ]; + auto app = new GildedRose(items); + app.updateQuality; +} diff --git a/dart/.gitignore b/dart/.gitignore new file mode 100644 index 00000000..33d0df0e --- /dev/null +++ b/dart/.gitignore @@ -0,0 +1,6 @@ +# Files and directories created by pub +.packages +.pub/ +packages +pubspec.lock # (Remove this pattern if you wish to check in your lock file) +.idea \ No newline at end of file diff --git a/dart/bin/main.dart b/dart/bin/main.dart new file mode 100644 index 00000000..240994c5 --- /dev/null +++ b/dart/bin/main.dart @@ -0,0 +1,35 @@ +import 'package:gilded_rose/gilded_rose.dart'; + +main(List args) { + print("OMGHAI!"); + + var items = [ + new Item("+5 Dexterity Vest", 10, 20), + new Item("Aged Brie", 2, 0), + new Item("Elixir of the Mongoose", 5, 7), + new Item("Sulfuras, Hand of Ragnaros", 0, 80), + new Item("Sulfuras, Hand of Ragnaros", -1, 80), + new Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this conjured item does not work properly yet + new Item("Conjured Mana Cake", 3, 6) + ]; + + GildedRose app = new GildedRose(items); + + int days = 2; + if (args.length > 0) { + days = int.parse(args[0]) + 1; + } + + for (int i = 0; i < days; i++) { + print("-------- day $i --------"); + print("name, sellIn, quality"); + for (var item in items) { + print(item); + } + print(''); + app.updateQuality(); + } +} diff --git a/dart/lib/gilded_rose.dart b/dart/lib/gilded_rose.dart new file mode 100644 index 00000000..94857b32 --- /dev/null +++ b/dart/lib/gilded_rose.dart @@ -0,0 +1,68 @@ +class GildedRose { + List items; + + GildedRose(this.items); + + void updateQuality() { + for (int i = 0; i < items.length; i++) { + if (items[i].name != "Aged Brie" && + items[i].name != "Backstage passes to a TAFKAL80ETC concert") { + if (items[i].quality > 0) { + if (items[i].name != "Sulfuras, Hand of Ragnaros") { + items[i].quality = items[i].quality - 1; + } + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + + if (items[i].name == "Backstage passes to a TAFKAL80ETC concert") { + if (items[i].sellIn < 11) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + + if (items[i].sellIn < 6) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + } + } + } + + if (items[i].name != "Sulfuras, Hand of Ragnaros") { + items[i].sellIn = items[i].sellIn - 1; + } + + if (items[i].sellIn < 0) { + if (items[i].name != "Aged Brie") { + if (items[i].name != "Backstage passes to a TAFKAL80ETC concert") { + if (items[i].quality > 0) { + if (items[i].name != "Sulfuras, Hand of Ragnaros") { + items[i].quality = items[i].quality - 1; + } + } + } else { + items[i].quality = items[i].quality - items[i].quality; + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1; + } + } + } + } + } +} + +class Item { + String name; + int sellIn; + int quality; + + Item(this.name, this.sellIn, this.quality); + + String toString() => '$name, $sellIn, $quality'; +} diff --git a/dart/pubspec.yaml b/dart/pubspec.yaml new file mode 100644 index 00000000..ce8365c9 --- /dev/null +++ b/dart/pubspec.yaml @@ -0,0 +1,9 @@ +name: gilded_rose +version: 0.0.1 +description: A simple console application. + +environment: + sdk: '>=2.10.0 <3.0.0' + +dev_dependencies: + test: ^1.16.8 diff --git a/dart/test/gilded_rose_test.dart b/dart/test/gilded_rose_test.dart new file mode 100644 index 00000000..69e4a489 --- /dev/null +++ b/dart/test/gilded_rose_test.dart @@ -0,0 +1,13 @@ +import 'package:test/test.dart'; +import 'package:gilded_rose/gilded_rose.dart'; + +main() { + test('foo', () { + var item = new Item('foo', 0, 0); + var items = [item]; + + GildedRose app = new GildedRose(items); + app.updateQuality(); + expect("fixme", app.items[0].name); + }); +} diff --git a/elixir/.gitignore b/elixir/.gitignore new file mode 100644 index 00000000..69fa449d --- /dev/null +++ b/elixir/.gitignore @@ -0,0 +1 @@ +_build/ diff --git a/elixir/config/config.exs b/elixir/config/config.exs new file mode 100644 index 00000000..d2d855e6 --- /dev/null +++ b/elixir/config/config.exs @@ -0,0 +1 @@ +use Mix.Config diff --git a/elixir/lib/gilded_rose.ex b/elixir/lib/gilded_rose.ex new file mode 100644 index 00000000..7edd8c83 --- /dev/null +++ b/elixir/lib/gilded_rose.ex @@ -0,0 +1,83 @@ +defmodule GildedRose do + # Example + # update_quality([%Item{name: "Backstage passes to a TAFKAL80ETC concert", sell_in: 9, quality: 1}]) + # => [%Item{name: "Backstage passes to a TAFKAL80ETC concert", sell_in: 8, quality: 3}] + + def update_quality(items) do + Enum.map(items, &update_item/1) + end + + def update_item(item) do + item = cond do + item.name != "Aged Brie" && item.name != "Backstage passes to a TAFKAL80ETC concert" -> + if item.quality > 0 do + if item.name != "Sulfuras, Hand of Ragnaros" do + %{item | quality: item.quality - 1} + else + item + end + else + item + end + true -> + cond do + item.quality < 50 -> + item = %{item | quality: item.quality + 1} + cond do + item.name == "Backstage passes to a TAFKAL80ETC concert" -> + item = cond do + item.sell_in < 11 -> + cond do + item.quality < 50 -> + %{item | quality: item.quality + 1} + true -> item + end + true -> item + end + cond do + item.sell_in < 6 -> + cond do + item.quality < 50 -> + %{item | quality: item.quality + 1} + true -> item + end + true -> item + end + true -> item + end + true -> item + end + end + item = cond do + item.name != "Sulfuras, Hand of Ragnaros" -> + %{item | sell_in: item.sell_in - 1} + true -> item + end + cond do + item.sell_in < 0 -> + cond do + item.name != "Aged Brie" -> + cond do + item.name != "Backstage passes to a TAFKAL80ETC concert" -> + cond do + item.quality > 0 -> + cond do + item.name != "Sulfuras, Hand of Ragnaros" -> + %{item | quality: item.quality - 1} + true -> item + end + true -> item + end + true -> %{item | quality: item.quality - item.quality} + end + true -> + cond do + item.quality < 50 -> + %{item | quality: item.quality + 1} + true -> item + end + end + true -> item + end + end +end diff --git a/elixir/lib/item.ex b/elixir/lib/item.ex new file mode 100644 index 00000000..0a20edbf --- /dev/null +++ b/elixir/lib/item.ex @@ -0,0 +1,3 @@ +defmodule Item do + defstruct name: nil, sell_in: nil, quality: nil +end diff --git a/elixir/mix.exs b/elixir/mix.exs new file mode 100644 index 00000000..c50af0d0 --- /dev/null +++ b/elixir/mix.exs @@ -0,0 +1,9 @@ +defmodule GildedRose.Mixfile do + use Mix.Project + + def project do + [app: :gilded_rose, + version: "0.0.1", + elixir: "~> 1.0"] + end +end diff --git a/elixir/test/gilded_rose_test.exs b/elixir/test/gilded_rose_test.exs new file mode 100644 index 00000000..56b8f4a5 --- /dev/null +++ b/elixir/test/gilded_rose_test.exs @@ -0,0 +1,10 @@ +defmodule GildedRoseTest do + use ExUnit.Case + + test "begin the journey of refactoring" do + items = [%Item{name: "foo", sell_in: 0, quality: 0}] + GildedRose.update_quality(items) + %{name: firstItemName} = List.first(items) + assert "fixme" == firstItemName + end +end diff --git a/elixir/test/test_helper.exs b/elixir/test/test_helper.exs new file mode 100644 index 00000000..869559e7 --- /dev/null +++ b/elixir/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/elm/.gitignore b/elm/.gitignore new file mode 100644 index 00000000..d9100021 --- /dev/null +++ b/elm/.gitignore @@ -0,0 +1,5 @@ +elm-stuff +# elm-repl generated files +repl-temp-* +*.src +src/index.html diff --git a/elm/README.md b/elm/README.md new file mode 100644 index 00000000..30aa4660 --- /dev/null +++ b/elm/README.md @@ -0,0 +1,7 @@ +# Gilded Rose (elm) + +To run tests, enter `elm-test` + +### Installing `elm-test` + +https://github.com/elm-explorations/test diff --git a/elm/elm.json b/elm/elm.json new file mode 100644 index 00000000..5c475c39 --- /dev/null +++ b/elm/elm.json @@ -0,0 +1,28 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/browser": "1.0.2", + "elm/core": "1.0.4", + "elm/html": "1.0.0" + }, + "indirect": { + "elm/json": "1.1.3", + "elm/time": "1.0.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.2" + } + }, + "test-dependencies": { + "direct": { + "elm-explorations/test": "1.2.2" + }, + "indirect": { + "elm/random": "1.0.0" + } + } +} diff --git a/elm/src/GildedRose.elm b/elm/src/GildedRose.elm new file mode 100644 index 00000000..bf97c94a --- /dev/null +++ b/elm/src/GildedRose.elm @@ -0,0 +1,53 @@ +module GildedRose exposing (Item, update_quality) + + +type alias Item = + { name : String + , sell_by : Int + , quality : Int + } + + +update_quality : List Item -> List Item +update_quality items = + List.map + (\item -> + if item.name == "Aged Brie" || item.name == "Backstage passes to a TAFKAL80ETC concert" then + if item.quality < 50 then + if item.name == "Backstage passes to a TAFKAL80ETC concert" then + if item.sell_by < 0 then + { item | sell_by = item.sell_by - 1, quality = 0 } + + else if item.sell_by < 6 then + { item | sell_by = item.sell_by - 1, quality = item.quality + 3 } + + else if item.sell_by < 11 then + { item | sell_by = item.sell_by - 1, quality = item.quality + 2 } + + else + { item | sell_by = item.sell_by - 1, quality = item.quality + 1 } + + else + { item | sell_by = item.sell_by - 1, quality = item.quality + 1 } + + else + { item | sell_by = item.sell_by } + + else if item.name /= "Aged Brie" && item.name /= "Sulfuras, Hand of Ragnaros" then + if item.sell_by < 0 && item.quality > 0 then + if item.quality >= 2 then + { item | sell_by = item.sell_by - 1, quality = item.quality - 2 } + + else + { item | sell_by = item.sell_by - 1, quality = 0 } + + else if item.quality >= 1 then + { item | sell_by = item.sell_by - 1, quality = item.quality - 1 } + + else + { item | sell_by = item.sell_by - 1, quality = 0 } + + else + item + ) + items diff --git a/elm/src/Main.elm b/elm/src/Main.elm new file mode 100644 index 00000000..bb225f23 --- /dev/null +++ b/elm/src/Main.elm @@ -0,0 +1,7 @@ +module Main exposing (main) + +import Html exposing (..) + + +main = + text "Gilded Rose" diff --git a/elm/tests/GildedRoseTest.elm b/elm/tests/GildedRoseTest.elm new file mode 100644 index 00000000..2a2cff01 --- /dev/null +++ b/elm/tests/GildedRoseTest.elm @@ -0,0 +1,18 @@ +module GildedRoseTest exposing (..) + +import Expect exposing (Expectation) +import Fuzz exposing (Fuzzer, int, list, string) +import GildedRose exposing (..) +import Test exposing (..) + + +suite : Test +suite = + test "example test" + (\_ -> + let + foo = + Item "foo" 10 30 + in + Expect.equal foo.name "fixme" + ) diff --git a/erlang/.gitignore b/erlang/.gitignore new file mode 100644 index 00000000..2a23a830 --- /dev/null +++ b/erlang/.gitignore @@ -0,0 +1,20 @@ +.rebar3 +_* +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +_build +*.iml +rebar3.crashdump +*~ +.idea + diff --git a/erlang/README.md b/erlang/README.md new file mode 100644 index 00000000..7c117b4b --- /dev/null +++ b/erlang/README.md @@ -0,0 +1,31 @@ +GildedRose +===== + +You will need to install [erlang](https://www.erlang.org/), and [rebar3](https://github.com/erlang/rebar3). I recommend following the instructions from JetBrains: [Getting Started with Erlang](https://www.jetbrains.com/help/idea/erlang.html). + +When you open this project with IntelliJ I recommend that you do + + File | New | Project from existing sources + +Then be sure to select "Erlang" as the project type, and configure the rebar3 location correctly, +(There is a copy of it in this repo). + +If you're having trouble executing the 'main' application, check your module path in your Project Settings. +I found I had to manually set the module compile output path to '_build/default/lib/gilded_rose/ebin'. +The test output path seemed to be correct as '.eunit' + +Build +----- + + $ rebar3 compile + +Run Tests +--------- + + $ rebar3 eunit + +TextTest Fixture +---------------- +To run for 30 days, I think this ought to work but I tend to run it through the IDE instead: + + $ erl -pa _build/default/lib/gilded_rose/ebin -pa . -eval main:main([30]). -s init stop -noshell diff --git a/erlang/include/gilded_rose.hrl b/erlang/include/gilded_rose.hrl new file mode 100644 index 00000000..4b74d598 --- /dev/null +++ b/erlang/include/gilded_rose.hrl @@ -0,0 +1,5 @@ +-record(item, { + name :: binary(), + sell_in = 0 :: integer(), + quality = 0 :: integer() +}). diff --git a/erlang/include/texttest_fixture.hrl b/erlang/include/texttest_fixture.hrl new file mode 100644 index 00000000..6bde4e1f --- /dev/null +++ b/erlang/include/texttest_fixture.hrl @@ -0,0 +1 @@ +-include("gilded_rose.hrl"). diff --git a/erlang/rebar.config b/erlang/rebar.config new file mode 100644 index 00000000..bf4b1d17 --- /dev/null +++ b/erlang/rebar.config @@ -0,0 +1,2 @@ +{erl_opts, [warnings_as_errors]}. +{eunit_opts, [verbose]}. \ No newline at end of file diff --git a/erlang/rebar.lock b/erlang/rebar.lock new file mode 100644 index 00000000..57afcca0 --- /dev/null +++ b/erlang/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/erlang/src/gilded_rose.app.src b/erlang/src/gilded_rose.app.src new file mode 100644 index 00000000..4c47019a --- /dev/null +++ b/erlang/src/gilded_rose.app.src @@ -0,0 +1 @@ +{application, gilded_rose, [{vsn, "0.1"}]}. diff --git a/erlang/src/gilded_rose.erl b/erlang/src/gilded_rose.erl new file mode 100644 index 00000000..68f7efac --- /dev/null +++ b/erlang/src/gilded_rose.erl @@ -0,0 +1,87 @@ +-module(gilded_rose). + +-include("gilded_rose.hrl"). + +-export([ + update_quality/1 +]). + +-spec update_quality([#item{}]) -> [#item{}]. +update_quality(Items) -> + lists:map(fun update_item/1, Items). + +-spec update_item(#item{}) -> #item{}. +update_item(Item = #item{name = Name}) -> + Item1 = if + Name /= "Aged Brie" andalso Name /= "Backstage passes to a TAFKAL80ETC concert" -> + if + Item#item.quality > 0 -> + if + Name /= "Sulfuras, Hand of Ragnaros" -> + Item#item{quality = Item#item.quality - 1}; + true -> + Item + end; + true -> + Item + end; + true -> + if + Item#item.quality < 50 -> + Item2 = Item#item{quality = Item#item.quality + 1}, + if + Name == "Backstage passes to a TAFKAL80ETC concert" -> + Item3 = if + Item2#item.sell_in < 11 -> + if + Item2#item.quality < 50 -> + Item2#item{quality = Item2#item.quality + 1}; + true -> Item2 + end; + true -> Item2 + end, + if + Item3#item.sell_in < 6 -> + if + Item3#item.quality < 50 -> + Item3#item{quality = Item3#item.quality + 1}; + true -> Item3 + end; + true -> Item3 + end; + true -> Item2 + end; + true -> Item + end + end, + Item4 = if + Name /= "Sulfuras, Hand of Ragnaros" -> + Item1#item{sell_in = Item1#item.sell_in - 1}; + true -> Item1 + end, + if + Item4#item.sell_in < 0 -> + if + Name /= "Aged Brie" -> + if + Name /= "Backstage passes to a TAFKAL80ETC concert" -> + if + Item4#item.quality > 0 -> + if + Name /= "Sulfuras, Hand of Ragnaros" -> + Item4#item{quality = Item4#item.quality - 1}; + true -> Item4 + end; + true -> Item4 + end; + true -> Item4#item{quality = Item4#item.quality - Item4#item.quality} + end; + true -> + if + Item4#item.quality < 50 -> + Item4#item{quality = Item4#item.quality + 1}; + true -> Item4 + end + end; + true -> Item4 + end. diff --git a/erlang/src/main.erl b/erlang/src/main.erl new file mode 100644 index 00000000..17f76489 --- /dev/null +++ b/erlang/src/main.erl @@ -0,0 +1,34 @@ +-module(main). + +-include("texttest_fixture.hrl"). + +-export([main/1]). + +-define(ALL_ITEMS, [ + #item{name = "+5 Dexterity Vest", sell_in = 10, quality = 20}, + #item{name = "Aged Brie", sell_in = 2, quality = 0}, + #item{name = "Elixir of the Mongoose", sell_in = 5, quality = 7}, + #item{name = "Sulfuras, Hand of Ragnaros", sell_in = 0, quality = 80}, + #item{name = "Sulfuras, Hand of Ragnaros", sell_in = -1, quality = 80}, + #item{name = "Backstage passes to a TAFKAL80ETC concert", sell_in = 15, quality = 20}, + #item{name = "Backstage passes to a TAFKAL80ETC concert", sell_in = 10, quality = 49}, + #item{name = "Backstage passes to a TAFKAL80ETC concert", sell_in = 5, quality = 49}, + #item{name = "Conjured Mana Cake", sell_in = 3, quality = 6} +]). + +main([Days]) -> + try + texttest_fixture:print_update_quality(Days, ?ALL_ITEMS) + catch + ExceptionClass:ExceptionInfo -> + io:format("something went wrong:~n~p : ~p~n", [ExceptionClass, ExceptionInfo]), + usage() + end; + +main(_) -> + usage(). + +usage() -> + io:format("usage: main main number_of_days\ne.g. main main [30]\n"), + halt(1). + diff --git a/erlang/src/texttest_fixture.erl b/erlang/src/texttest_fixture.erl new file mode 100644 index 00000000..30ac0863 --- /dev/null +++ b/erlang/src/texttest_fixture.erl @@ -0,0 +1,21 @@ +-module(texttest_fixture). + +-include("texttest_fixture.hrl"). + +-export([print_update_quality/2]). + +print_one_day(Day, Items) -> + io:format("~n-------- day ~p --------~n", [Day]), + io:format("name, sellIn, quality~n", []), + lists:foreach(fun(#item{name = Name, sell_in = SellIn, quality = Quality}) -> + io:format("~s, ~p, ~p~n", [Name, SellIn, Quality]) + end, Items). + +print_update_quality(Days, Items) -> + io:format("OMGHAI!"), + lists:foreach( + fun(Day) -> print_one_day(Day, Items), + gilded_rose:update_quality(Items) + end, lists:seq(0, Days - 1) + ). + diff --git a/erlang/test/gilded_rose_test.erl b/erlang/test/gilded_rose_test.erl new file mode 100644 index 00000000..9efd0400 --- /dev/null +++ b/erlang/test/gilded_rose_test.erl @@ -0,0 +1,9 @@ +-module(gilded_rose_test). + +-include_lib("eunit/include/eunit.hrl"). + +-include("gilded_rose.hrl"). + +update_quality_test_() -> [ + {"foo", ?_assertMatch([#item{name= "Foo"}], gilded_rose:update_quality([#item{name = "Foo", sell_in = 0, quality = 0}]))} +]. diff --git a/fortran/CMakeLists.txt b/fortran/CMakeLists.txt new file mode 100644 index 00000000..86a67af0 --- /dev/null +++ b/fortran/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.0.0) +project(GildedRose Fortran) +include(CTest) + +set(EXERCISM_RUN_ALL_TESTS 1) + +# Activate Fortran compiler warnings +if(CMAKE_Fortran_COMPILER_ID MATCHES "Intel") # Intel fortran + if(WIN32) + set (CMAKE_Fortran_FLAGS "/warn:all") + else() + set (CMAKE_Fortran_FLAGS "-warn all") + endif() +endif() +if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU" ) # GFortran +# set (CMAKE_Fortran_FLAGS "-std=f2008 -W -Wall -Wextra -pedantic -fbacktrace -Wdo-subscript") + set (CMAKE_Fortran_FLAGS "-std=f2008 -W -Wall -Wextra -pedantic -fbacktrace ") + set (CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS} -o0 -ffpe-trap=zero,invalid,overflow,underflow") +endif() + +add_executable(GildedRose_text_test + src/GildedRose.f90 + test/GildedRose_text_test.f90 + ) +add_executable(GildedRose_unity_test + src/GildedRose.f90 + test/GildedRose_unity_test.f90 + ) + + add_test(GildedRose_text_test GildedRose_text_test) + add_test(GildedRose_unity_test GildedRose_unity_test) + + diff --git a/fortran/README.md b/fortran/README.md new file mode 100644 index 00000000..26b79201 --- /dev/null +++ b/fortran/README.md @@ -0,0 +1,57 @@ +# Fortran version of Gilded Rose refactoring kata + +## Introduction +The Fortran90 version of the Gilded Rose refactoring kata. + +## Prerequisites + +* CMake version >= 3.13 +* Fortran compiler + * Tested with: + * gfortran + * Intel Fortran + +## How to build and run tests in a terminal + +### Build tests + + $ cd ${GIT_FOLDER}/GildedRose-Refactoring-Kata/fortran + $ mkdir build + $ cd build + $ cmake .. + $ cmake --build . + +### Show available tests + + $ cd ${GIT_FOLDER}/GildedRose-Refactoring-Kata/fortran/build + $ ctest -N + Test project ${GIT_FOLDER}/GildedRose-Refactoring-Kata/fortran/build + Test #1: GildedRose_text_test + Test #2: GildedRose_unity_test + + Total Tests: 2 + +### Run all tests + + $ ctest + +### Run all tests with verbose output + + $ ctest -VV + +## How to build and run tests using the [CLion IDE](https://www.jetbrains.com/clion/) + +1. Start CLion +2. Select menu `File - Open...` +3. Select folder `${GIT_FOLDER}/GildedRose-Refactoring-Kata/fortran` +4. Select menu `Build - Build Project` +4. Select menu `Run - Run...` + +## How to build and run tests using Visual Studio 2019 + +1. Start Visual Studio 2019 +2. Select `Open a local folder` +3. Select folder `${GIT_FOLDER}/GildedRose-Refactoring-Kata/fortran` +4. Wait for message `CMake generation finished.` in the CMake output window at the bottom +5. Select ALL_BUILD and build to build the source code +6. Select RUN_TEST and build which will execute the tests diff --git a/fortran/src/GildedRose.f90 b/fortran/src/GildedRose.f90 new file mode 100644 index 00000000..68e9c896 --- /dev/null +++ b/fortran/src/GildedRose.f90 @@ -0,0 +1,144 @@ +module GildedRose + implicit none + private + save + + public init_item + public print_item + public update_quality + public int2str + + integer, parameter, public :: MAX_STR_LEN = 80 + + type, public :: Item + character(len=MAX_STR_LEN) :: name + integer :: sellIn + integer :: quality + end type + +contains + + subroutine init_item(it, name, sellIn, quality) + type(Item) :: it + character(len=*) :: name + integer :: sellIn + integer :: quality + + it%name = trim(name) + it%sellIn = sellIn + it%quality = quality + + end + + function int2str( int_val ) + implicit none + integer :: int_val + character( int(log10(real(max(abs(int_val),1)))) + 1 + & + (1-sign(1,int_val))/2 ) :: int2str + character(64) :: all_chars + write(all_chars,*) int_val + int2str = trim( adjustl( all_chars ) ) + end function + + subroutine print_item(it) + type(Item) :: it + write(*,*) trim(it%name), ', ', int2str(it%sellIn), ', ', int2str(it%quality) + end subroutine + + subroutine update_quality(items, size) + type(Item), dimension(:) :: items + integer :: size + + integer :: i + + do i = 1, size + if (items(i)%name /= "Aged Brie" .and. items(i)%name /= "Backstage passes to a TAFKAL80ETC concert" ) then + + if (items(i)%quality > 0) then + + if (items(i)%name /= "Sulfuras, Hand of Ragnaros" ) then + + items(i)%quality = items(i)%quality - 1 + endif + + endif + + else + + if (items(i)%quality < 50) then + + items(i)%quality = items(i)%quality + 1 + + if ( items(i)%name =="Backstage passes to a TAFKAL80ETC concert") then + + if (items(i)%sellIn < 11) then + + if (items(i)%quality < 50) then + + items(i)%quality = items(i)%quality + 1 + + endif + + endif + + if (items(i)%sellIn < 6) then + + if (items(i)%quality < 50) then + + items(i)%quality = items(i)%quality + 1 + + endif + + endif + + endif + + endif + + endif + if (items(i)%name /= "Sulfuras, Hand of Ragnaros") then + + items(i)%sellIn = items(i)%sellIn - 1 + + endif + + if (items(i)%sellIn < 0) then + + if (items(i)%name /= "Aged Brie" ) then + + if (items(i)%name /= "Backstage passes to a TAFKAL80ETC concert" ) then + + if (items(i)%quality > 0 ) then + + if (items(i)%name /= "Sulfuras, Hand of Ragnaros" )then + + items(i)%quality = items(i)%quality - 1 + + endif + + endif + + else + + items(i)%quality = items(i)%quality - items(i)%quality + + end if + + else + + if (items(i)%quality < 50) then + + items(i)%quality = items(i)%quality + 1 + + end if + + end if + + end if + + end do + + end subroutine + + +end module diff --git a/fortran/test/GildedRose_text_test.f90 b/fortran/test/GildedRose_text_test.f90 new file mode 100644 index 00000000..c285970d --- /dev/null +++ b/fortran/test/GildedRose_text_test.f90 @@ -0,0 +1,52 @@ + + + + + + +program GildedRose_text_test + use GildedRose + implicit none + type(Item) :: items(9) + integer :: last + integer :: day + integer :: index + + last = 1 + + call init_item(items(last), "+5 Dexterity Vest", 10, 20) + last=last+1 + call init_item(items(last), "Aged Brie", 2, 0) + last=last+1 + call init_item(items(last), "Elixir of the Mongoose", 5, 7) + last=last+1 + call init_item(items(last), "Sulfuras, Hand of Ragnaros", 0, 80) + last=last+1 + call init_item(items(last), "Sulfuras, Hand of Ragnaros", -1, 80) + last=last+1 + call init_item(items(last), "Backstage passes to a TAFKAL80ETC concert", 15, 20) + last=last+1 + call init_item(items(last), "Backstage passes to a TAFKAL80ETC concert", 10, 49) + last=last+1 + call init_item(items(last), "Backstage passes to a TAFKAL80ETC concert", 5, 49) + ! this Conjured item doesn't yet work properly + last=last+1 + call init_item(items(last), "Conjured Mana Cake", 3, 6) + + write(*,*) "OMGHAI!" + + + do day = 1, 2 + write(*,*) "-------- day "//int2str(day)//" --------" + write(*,*) "name, sellIn, quality" + do index = 1, last + call print_item(items(index)) + end do + + write(*,*) " " + + call update_quality(items, last) + enddo + + +end program diff --git a/fortran/test/GildedRose_unity_test.f90 b/fortran/test/GildedRose_unity_test.f90 new file mode 100644 index 00000000..82032a67 --- /dev/null +++ b/fortran/test/GildedRose_unity_test.f90 @@ -0,0 +1,21 @@ + + + + + + +program GildedRose_unity_test + use GildedRose + implicit none + type(Item):: it(1) + + call init_item(it(1), "foo", 10, 20) + call update_quality(it, 1) + if (trim(it(1)%name) /= "fixMe" ) then + write(*,*) "ERRORUnity test Failed" + stop 1 + else + write(*,*) "Unity test OK" + endif + +end program diff --git a/fsharp-core/GildedRose.ApprovalTests/GildedRose.ApprovalTests.fsproj b/fsharp-core/GildedRose.ApprovalTests/GildedRose.ApprovalTests.fsproj new file mode 100644 index 00000000..f26f9caa --- /dev/null +++ b/fsharp-core/GildedRose.ApprovalTests/GildedRose.ApprovalTests.fsproj @@ -0,0 +1,33 @@ + + + + net6.0 + + false + + + + + Always + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + + + diff --git a/fsharp-core/GildedRose.ApprovalTests/GildedRoseTest.ApprovalTest.ThirtyDays.received.txt b/fsharp-core/GildedRose.ApprovalTests/GildedRoseTest.ApprovalTest.ThirtyDays.received.txt new file mode 100644 index 00000000..cd66984f --- /dev/null +++ b/fsharp-core/GildedRose.ApprovalTests/GildedRoseTest.ApprovalTest.ThirtyDays.received.txt @@ -0,0 +1,373 @@ +OMGHAI! +-------- day 0 -------- +name, sellIn, quality ++5 Dexterity Vest, 10, 20 +Aged Brie, 2, 0 +Elixir of the Mongoose, 5, 7 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 15, 20 +Backstage passes to a TAFKAL80ETC concert, 10, 49 +Backstage passes to a TAFKAL80ETC concert, 5, 49 +Conjured Mana Cake, 3, 6 + +-------- day 1 -------- +name, sellIn, quality ++5 Dexterity Vest, 9, 19 +Aged Brie, 1, 1 +Elixir of the Mongoose, 4, 6 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 14, 21 +Backstage passes to a TAFKAL80ETC concert, 9, 50 +Backstage passes to a TAFKAL80ETC concert, 4, 50 +Conjured Mana Cake, 2, 5 + +-------- day 2 -------- +name, sellIn, quality ++5 Dexterity Vest, 8, 18 +Aged Brie, 0, 2 +Elixir of the Mongoose, 3, 5 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 13, 22 +Backstage passes to a TAFKAL80ETC concert, 8, 50 +Backstage passes to a TAFKAL80ETC concert, 3, 50 +Conjured Mana Cake, 1, 4 + +-------- day 3 -------- +name, sellIn, quality ++5 Dexterity Vest, 7, 17 +Aged Brie, -1, 4 +Elixir of the Mongoose, 2, 4 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 12, 23 +Backstage passes to a TAFKAL80ETC concert, 7, 50 +Backstage passes to a TAFKAL80ETC concert, 2, 50 +Conjured Mana Cake, 0, 3 + +-------- day 4 -------- +name, sellIn, quality ++5 Dexterity Vest, 6, 16 +Aged Brie, -2, 6 +Elixir of the Mongoose, 1, 3 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 11, 24 +Backstage passes to a TAFKAL80ETC concert, 6, 50 +Backstage passes to a TAFKAL80ETC concert, 1, 50 +Conjured Mana Cake, -1, 1 + +-------- day 5 -------- +name, sellIn, quality ++5 Dexterity Vest, 5, 15 +Aged Brie, -3, 8 +Elixir of the Mongoose, 0, 2 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 10, 25 +Backstage passes to a TAFKAL80ETC concert, 5, 50 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Conjured Mana Cake, -2, 0 + +-------- day 6 -------- +name, sellIn, quality ++5 Dexterity Vest, 4, 14 +Aged Brie, -4, 10 +Elixir of the Mongoose, -1, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 9, 27 +Backstage passes to a TAFKAL80ETC concert, 4, 50 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Conjured Mana Cake, -3, 0 + +-------- day 7 -------- +name, sellIn, quality ++5 Dexterity Vest, 3, 13 +Aged Brie, -5, 12 +Elixir of the Mongoose, -2, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 8, 29 +Backstage passes to a TAFKAL80ETC concert, 3, 50 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Conjured Mana Cake, -4, 0 + +-------- day 8 -------- +name, sellIn, quality ++5 Dexterity Vest, 2, 12 +Aged Brie, -6, 14 +Elixir of the Mongoose, -3, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 7, 31 +Backstage passes to a TAFKAL80ETC concert, 2, 50 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Conjured Mana Cake, -5, 0 + +-------- day 9 -------- +name, sellIn, quality ++5 Dexterity Vest, 1, 11 +Aged Brie, -7, 16 +Elixir of the Mongoose, -4, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 6, 33 +Backstage passes to a TAFKAL80ETC concert, 1, 50 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Conjured Mana Cake, -6, 0 + +-------- day 10 -------- +name, sellIn, quality ++5 Dexterity Vest, 0, 10 +Aged Brie, -8, 18 +Elixir of the Mongoose, -5, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 5, 35 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Conjured Mana Cake, -7, 0 + +-------- day 11 -------- +name, sellIn, quality ++5 Dexterity Vest, -1, 8 +Aged Brie, -9, 20 +Elixir of the Mongoose, -6, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 4, 38 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Conjured Mana Cake, -8, 0 + +-------- day 12 -------- +name, sellIn, quality ++5 Dexterity Vest, -2, 6 +Aged Brie, -10, 22 +Elixir of the Mongoose, -7, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 3, 41 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Conjured Mana Cake, -9, 0 + +-------- day 13 -------- +name, sellIn, quality ++5 Dexterity Vest, -3, 4 +Aged Brie, -11, 24 +Elixir of the Mongoose, -8, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 2, 44 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Conjured Mana Cake, -10, 0 + +-------- day 14 -------- +name, sellIn, quality ++5 Dexterity Vest, -4, 2 +Aged Brie, -12, 26 +Elixir of the Mongoose, -9, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 1, 47 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Conjured Mana Cake, -11, 0 + +-------- day 15 -------- +name, sellIn, quality ++5 Dexterity Vest, -5, 0 +Aged Brie, -13, 28 +Elixir of the Mongoose, -10, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Conjured Mana Cake, -12, 0 + +-------- day 16 -------- +name, sellIn, quality ++5 Dexterity Vest, -6, 0 +Aged Brie, -14, 30 +Elixir of the Mongoose, -11, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Conjured Mana Cake, -13, 0 + +-------- day 17 -------- +name, sellIn, quality ++5 Dexterity Vest, -7, 0 +Aged Brie, -15, 32 +Elixir of the Mongoose, -12, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Conjured Mana Cake, -14, 0 + +-------- day 18 -------- +name, sellIn, quality ++5 Dexterity Vest, -8, 0 +Aged Brie, -16, 34 +Elixir of the Mongoose, -13, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Conjured Mana Cake, -15, 0 + +-------- day 19 -------- +name, sellIn, quality ++5 Dexterity Vest, -9, 0 +Aged Brie, -17, 36 +Elixir of the Mongoose, -14, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Conjured Mana Cake, -16, 0 + +-------- day 20 -------- +name, sellIn, quality ++5 Dexterity Vest, -10, 0 +Aged Brie, -18, 38 +Elixir of the Mongoose, -15, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Conjured Mana Cake, -17, 0 + +-------- day 21 -------- +name, sellIn, quality ++5 Dexterity Vest, -11, 0 +Aged Brie, -19, 40 +Elixir of the Mongoose, -16, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Backstage passes to a TAFKAL80ETC concert, -16, 0 +Conjured Mana Cake, -18, 0 + +-------- day 22 -------- +name, sellIn, quality ++5 Dexterity Vest, -12, 0 +Aged Brie, -20, 42 +Elixir of the Mongoose, -17, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Backstage passes to a TAFKAL80ETC concert, -17, 0 +Conjured Mana Cake, -19, 0 + +-------- day 23 -------- +name, sellIn, quality ++5 Dexterity Vest, -13, 0 +Aged Brie, -21, 44 +Elixir of the Mongoose, -18, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Backstage passes to a TAFKAL80ETC concert, -18, 0 +Conjured Mana Cake, -20, 0 + +-------- day 24 -------- +name, sellIn, quality ++5 Dexterity Vest, -14, 0 +Aged Brie, -22, 46 +Elixir of the Mongoose, -19, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Backstage passes to a TAFKAL80ETC concert, -19, 0 +Conjured Mana Cake, -21, 0 + +-------- day 25 -------- +name, sellIn, quality ++5 Dexterity Vest, -15, 0 +Aged Brie, -23, 48 +Elixir of the Mongoose, -20, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Backstage passes to a TAFKAL80ETC concert, -20, 0 +Conjured Mana Cake, -22, 0 + +-------- day 26 -------- +name, sellIn, quality ++5 Dexterity Vest, -16, 0 +Aged Brie, -24, 50 +Elixir of the Mongoose, -21, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Backstage passes to a TAFKAL80ETC concert, -16, 0 +Backstage passes to a TAFKAL80ETC concert, -21, 0 +Conjured Mana Cake, -23, 0 + +-------- day 27 -------- +name, sellIn, quality ++5 Dexterity Vest, -17, 0 +Aged Brie, -25, 50 +Elixir of the Mongoose, -22, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Backstage passes to a TAFKAL80ETC concert, -17, 0 +Backstage passes to a TAFKAL80ETC concert, -22, 0 +Conjured Mana Cake, -24, 0 + +-------- day 28 -------- +name, sellIn, quality ++5 Dexterity Vest, -18, 0 +Aged Brie, -26, 50 +Elixir of the Mongoose, -23, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Backstage passes to a TAFKAL80ETC concert, -18, 0 +Backstage passes to a TAFKAL80ETC concert, -23, 0 +Conjured Mana Cake, -25, 0 + +-------- day 29 -------- +name, sellIn, quality ++5 Dexterity Vest, -19, 0 +Aged Brie, -27, 50 +Elixir of the Mongoose, -24, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Backstage passes to a TAFKAL80ETC concert, -19, 0 +Backstage passes to a TAFKAL80ETC concert, -24, 0 +Conjured Mana Cake, -26, 0 + +-------- day 30 -------- +name, sellIn, quality ++5 Dexterity Vest, -20, 0 +Aged Brie, -28, 50 +Elixir of the Mongoose, -25, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Backstage passes to a TAFKAL80ETC concert, -20, 0 +Backstage passes to a TAFKAL80ETC concert, -25, 0 +Conjured Mana Cake, -27, 0 + diff --git a/fsharp-core/GildedRose.ApprovalTests/Tests.fs b/fsharp-core/GildedRose.ApprovalTests/Tests.fs new file mode 100644 index 00000000..06709445 --- /dev/null +++ b/fsharp-core/GildedRose.ApprovalTests/Tests.fs @@ -0,0 +1,19 @@ +module GildedRose.ApprovalTests + +open System +open Xunit +open System.Text +open System.IO + +[] +let ``Thirty day report is correct`` () = + let expected = File.ReadAllText "GildedRoseTest.ApprovalTest.ThirtyDays.received.txt" + + let fakeoutput = new StringBuilder() + Console.SetOut(new StringWriter(fakeoutput)) + Console.SetIn(new StringReader("a\n")) + + GildedRose.Program.main [||] |> ignore + let actual = fakeoutput.ToString() + + Assert.Equal(expected, actual) diff --git a/fsharp-core/GildedRose.UnitTests/GildedRose.UnitTests.fsproj b/fsharp-core/GildedRose.UnitTests/GildedRose.UnitTests.fsproj new file mode 100644 index 00000000..9617805b --- /dev/null +++ b/fsharp-core/GildedRose.UnitTests/GildedRose.UnitTests.fsproj @@ -0,0 +1,31 @@ + + + + net6.0 + + false + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + + + diff --git a/fsharp-core/GildedRose.UnitTests/Tests.fs b/fsharp-core/GildedRose.UnitTests/Tests.fs new file mode 100644 index 00000000..c2f88505 --- /dev/null +++ b/fsharp-core/GildedRose.UnitTests/Tests.fs @@ -0,0 +1,14 @@ +module GildedRose.UnitTests + +open GildedRose +open System.Collections.Generic +open Xunit +open Swensen.Unquote + +[] +let ``My test`` () = + let Items = new List() + Items.Add({Name = "foo"; SellIn = 0; Quality = 0}) + let app = new GildedRose(Items) + app.UpdateQuality() + test <@ "fixme" = Items.[0].Name @> \ No newline at end of file diff --git a/fsharp-core/GildedRose.sln b/fsharp-core/GildedRose.sln new file mode 100644 index 00000000..801ef9b1 --- /dev/null +++ b/fsharp-core/GildedRose.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.421 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "GildedRose", "GildedRose\GildedRose.fsproj", "{63814E28-6A6A-4496-BD16-DC1DFD79CDC8}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "GildedRose.UnitTests", "GildedRose.UnitTests\GildedRose.UnitTests.fsproj", "{34C47A43-9A27-44C0-9CA8-15324A76FD20}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "GildedRose.ApprovalTests", "GildedRose.ApprovalTests\GildedRose.ApprovalTests.fsproj", "{C2C723A1-31CF-445C-BC8B-3CFC8620C8EB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {63814E28-6A6A-4496-BD16-DC1DFD79CDC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63814E28-6A6A-4496-BD16-DC1DFD79CDC8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63814E28-6A6A-4496-BD16-DC1DFD79CDC8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63814E28-6A6A-4496-BD16-DC1DFD79CDC8}.Release|Any CPU.Build.0 = Release|Any CPU + {34C47A43-9A27-44C0-9CA8-15324A76FD20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {34C47A43-9A27-44C0-9CA8-15324A76FD20}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34C47A43-9A27-44C0-9CA8-15324A76FD20}.Release|Any CPU.ActiveCfg = Release|Any CPU + {34C47A43-9A27-44C0-9CA8-15324A76FD20}.Release|Any CPU.Build.0 = Release|Any CPU + {C2C723A1-31CF-445C-BC8B-3CFC8620C8EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2C723A1-31CF-445C-BC8B-3CFC8620C8EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2C723A1-31CF-445C-BC8B-3CFC8620C8EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2C723A1-31CF-445C-BC8B-3CFC8620C8EB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {16C52833-8AA9-469D-88D2-A3C9249ED08F} + EndGlobalSection +EndGlobal diff --git a/fsharp-core/GildedRose/GildedRose.fsproj b/fsharp-core/GildedRose/GildedRose.fsproj new file mode 100644 index 00000000..f3235211 --- /dev/null +++ b/fsharp-core/GildedRose/GildedRose.fsproj @@ -0,0 +1,16 @@ + + + + Exe + net6.0 + + + + + + + + + + + diff --git a/fsharp-core/GildedRose/Program.fs b/fsharp-core/GildedRose/Program.fs new file mode 100644 index 00000000..a5adc97e --- /dev/null +++ b/fsharp-core/GildedRose/Program.fs @@ -0,0 +1,65 @@ +namespace GildedRose + +open System.Collections.Generic + +type Item = { Name: string; SellIn: int; Quality: int } + +type GildedRose(items:IList) = + let Items = items + + member this.UpdateQuality() = + for i = 0 to Items.Count - 1 do + if Items.[i].Name <> "Aged Brie" && Items.[i].Name <> "Backstage passes to a TAFKAL80ETC concert" then + if Items.[i].Quality > 0 then + if Items.[i].Name <> "Sulfuras, Hand of Ragnaros" then + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality - 1) } + else + if Items.[i].Quality < 50 then + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality + 1) } + if Items.[i].Name = "Backstage passes to a TAFKAL80ETC concert" then + if Items.[i].SellIn < 11 then + if Items.[i].Quality < 50 then + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality + 1) } + if Items.[i].SellIn < 6 then + if Items.[i].Quality < 50 then + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality + 1) } + if Items.[i].Name <> "Sulfuras, Hand of Ragnaros" then + Items.[i] <- { Items.[i] with SellIn = (Items.[i].SellIn - 1) } + if Items.[i].SellIn < 0 then + if Items.[i].Name <> "Aged Brie" then + if Items.[i].Name <> "Backstage passes to a TAFKAL80ETC concert" then + if Items.[i].Quality > 0 then + if Items.[i].Name <> "Sulfuras, Hand of Ragnaros" then + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality - 1) } + else + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality - Items.[i].Quality) } + else + if Items.[i].Quality < 50 then + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality + 1) } + () + + +module Program = + [] + let main argv = + printfn "OMGHAI!" + let Items = new List() + Items.Add({Name = "+5 Dexterity Vest"; SellIn = 10; Quality = 20}) + Items.Add({Name = "Aged Brie"; SellIn = 2; Quality = 0}) + Items.Add({Name = "Elixir of the Mongoose"; SellIn = 5; Quality = 7}) + Items.Add({Name = "Sulfuras, Hand of Ragnaros"; SellIn = 0; Quality = 80}) + Items.Add({Name = "Sulfuras, Hand of Ragnaros"; SellIn = -1; Quality = 80}) + Items.Add({Name = "Backstage passes to a TAFKAL80ETC concert"; SellIn = 15; Quality = 20}) + Items.Add({Name = "Backstage passes to a TAFKAL80ETC concert"; SellIn = 10; Quality = 49}) + Items.Add({Name = "Backstage passes to a TAFKAL80ETC concert"; SellIn = 5; Quality = 49}) + Items.Add({Name = "Conjured Mana Cake"; SellIn = 3; Quality = 6}) + + let app = new GildedRose(Items) + for i = 0 to 30 do + printfn "-------- day %d --------" i + printfn "name, sellIn, quality" + for j = 0 to Items.Count - 1 do + printfn "%s, %d, %d" Items.[j].Name Items.[j].SellIn Items.[j].Quality + printfn "" + app.UpdateQuality() + 0 \ No newline at end of file diff --git a/fsharp/GildedRose.Tests/GildedRose.Tests.fsproj b/fsharp/GildedRose.Tests/GildedRose.Tests.fsproj new file mode 100644 index 00000000..f021553f --- /dev/null +++ b/fsharp/GildedRose.Tests/GildedRose.Tests.fsproj @@ -0,0 +1,97 @@ + + + + + Debug + AnyCPU + 2.0 + 9f9d20e1-cfc0-442c-870a-76e5abf0e7fe + Library + GildedRose.Tests + GildedRose.Tests + v4.5 + true + 4.3.1.0 + GildedRose.Tests + + + true + full + false + false + bin\Debug\ + DEBUG;TRACE + 3 + AnyCPU + bin\Debug\GildedRose.Tests.XML + true + + + pdbonly + true + true + bin\Release\ + TRACE + 3 + AnyCPU + bin\Release\GildedRose.Tests.XML + true + + + 11 + + + + + $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets + + + + + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets + + + + + + + + + + + ..\packages\ApprovalTests.3.0.10\lib\net40\ApprovalTests.dll + True + + + ..\packages\ApprovalUtilities.3.0.10\lib\net45\ApprovalUtilities.dll + True + + + ..\packages\ApprovalUtilities.3.0.10\lib\net45\ApprovalUtilities.Net45.dll + True + + + + True + + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + True + + + + + + GildedRose + {2660ef56-b4c1-4dcf-b106-278211bd26c6} + True + + + + \ No newline at end of file diff --git a/fsharp/GildedRose.Tests/GildedRoseTest.fs b/fsharp/GildedRose.Tests/GildedRoseTest.fs new file mode 100644 index 00000000..d29ed6d6 --- /dev/null +++ b/fsharp/GildedRose.Tests/GildedRoseTest.fs @@ -0,0 +1,32 @@ +module GildedRoseTest + +open GildedRose +open System +open System.IO +open System.Text +open NUnit.Framework +open System.Collections.Generic +open ApprovalTests +open ApprovalTests.Reporters + +[] +type GildedRoseTest () as this = + [] member this.Foo ()= + let Items = new List() + Items.Add({Name = "foo"; SellIn = 0; Quality = 0}) + let app = new GildedRose(Items) + app.UpdateQuality() + Assert.AreEqual("fixme", Items.[0].Name) + +[] +[)>] +type ApprovalTest () as this = + [] member this.ThirtyDays ()= + let fakeoutput = new StringBuilder() + Console.SetOut(new StringWriter(fakeoutput)) + Console.SetIn(new StringReader("a\n")) + + main Array.empty + let output = fakeoutput.ToString() + Approvals.Verify(output) + () \ No newline at end of file diff --git a/fsharp/GildedRose.Tests/packages.config b/fsharp/GildedRose.Tests/packages.config new file mode 100644 index 00000000..eff071b2 --- /dev/null +++ b/fsharp/GildedRose.Tests/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/fsharp/GildedRose.sln b/fsharp/GildedRose.sln new file mode 100644 index 00000000..027a29bf --- /dev/null +++ b/fsharp/GildedRose.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "GildedRose", "GildedRose\GildedRose.fsproj", "{2660EF56-B4C1-4DCF-B106-278211BD26C6}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "GildedRose.Tests", "GildedRose.Tests\GildedRose.Tests.fsproj", "{9F9D20E1-CFC0-442C-870A-76E5ABF0E7FE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2660EF56-B4C1-4DCF-B106-278211BD26C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2660EF56-B4C1-4DCF-B106-278211BD26C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2660EF56-B4C1-4DCF-B106-278211BD26C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2660EF56-B4C1-4DCF-B106-278211BD26C6}.Release|Any CPU.Build.0 = Release|Any CPU + {9F9D20E1-CFC0-442C-870A-76E5ABF0E7FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F9D20E1-CFC0-442C-870A-76E5ABF0E7FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F9D20E1-CFC0-442C-870A-76E5ABF0E7FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F9D20E1-CFC0-442C-870A-76E5ABF0E7FE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/fsharp/GildedRose/App.config b/fsharp/GildedRose/App.config new file mode 100644 index 00000000..8e156463 --- /dev/null +++ b/fsharp/GildedRose/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/fsharp/GildedRose/GildedRose.fs b/fsharp/GildedRose/GildedRose.fs new file mode 100644 index 00000000..f14f4cc9 --- /dev/null +++ b/fsharp/GildedRose/GildedRose.fs @@ -0,0 +1,63 @@ +module GildedRose + +open System.Collections.Generic + +type Item = { Name: string; SellIn: int; Quality: int } + +type GildedRose(items:IList) as this = + let Items = items + + member this.UpdateQuality() = + for i = 0 to Items.Count - 1 do + if Items.[i].Name <> "Aged Brie" && Items.[i].Name <> "Backstage passes to a TAFKAL80ETC concert" then + if Items.[i].Quality > 0 then + if Items.[i].Name <> "Sulfuras, Hand of Ragnaros" then + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality - 1) } + else + if Items.[i].Quality < 50 then + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality + 1) } + if Items.[i].Name = "Backstage passes to a TAFKAL80ETC concert" then + if Items.[i].SellIn < 11 then + if Items.[i].Quality < 50 then + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality + 1) } + if Items.[i].SellIn < 6 then + if Items.[i].Quality < 50 then + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality + 1) } + if Items.[i].Name <> "Sulfuras, Hand of Ragnaros" then + Items.[i] <- { Items.[i] with SellIn = (Items.[i].SellIn - 1) } + if Items.[i].SellIn < 0 then + if Items.[i].Name <> "Aged Brie" then + if Items.[i].Name <> "Backstage passes to a TAFKAL80ETC concert" then + if Items.[i].Quality > 0 then + if Items.[i].Name <> "Sulfuras, Hand of Ragnaros" then + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality - 1) } + else + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality - Items.[i].Quality) } + else + if Items.[i].Quality < 50 then + Items.[i] <- { Items.[i] with Quality = (Items.[i].Quality + 1) } + () + +[] +let main argv = + printfn "OMGHAI!" + let Items = new List() + Items.Add({Name = "+5 Dexterity Vest"; SellIn = 10; Quality = 20}) + Items.Add({Name = "Aged Brie"; SellIn = 2; Quality = 0}) + Items.Add({Name = "Elixir of the Mongoose"; SellIn = 5; Quality = 7}) + Items.Add({Name = "Sulfuras, Hand of Ragnaros"; SellIn = 0; Quality = 80}) + Items.Add({Name = "Sulfuras, Hand of Ragnaros"; SellIn = -1; Quality = 80}) + Items.Add({Name = "Backstage passes to a TAFKAL80ETC concert"; SellIn = 15; Quality = 20}) + Items.Add({Name = "Backstage passes to a TAFKAL80ETC concert"; SellIn = 10; Quality = 49}) + Items.Add({Name = "Backstage passes to a TAFKAL80ETC concert"; SellIn = 5; Quality = 49}) + Items.Add({Name = "Conjured Mana Cake"; SellIn = 3; Quality = 6}) + + let app = new GildedRose(Items) + for i = 0 to 30 do + printfn "-------- day %d --------" i + printfn "name, sellIn, quality" + for j = 0 to Items.Count - 1 do + printfn "%s, %d, %d" Items.[j].Name Items.[j].SellIn Items.[j].Quality + printfn "" + app.UpdateQuality() + 0 \ No newline at end of file diff --git a/fsharp/GildedRose/GildedRose.fsproj b/fsharp/GildedRose/GildedRose.fsproj new file mode 100644 index 00000000..520dda6e --- /dev/null +++ b/fsharp/GildedRose/GildedRose.fsproj @@ -0,0 +1,76 @@ + + + + + Debug + AnyCPU + 2.0 + 2660ef56-b4c1-4dcf-b106-278211bd26c6 + Exe + GildedRose + GildedRose + v4.5 + true + 4.3.1.0 + GildedRose + + + true + full + false + false + bin\Debug\ + DEBUG;TRACE + 3 + AnyCPU + bin\Debug\GildedRose.XML + true + + + pdbonly + true + true + bin\Release\ + TRACE + 3 + AnyCPU + bin\Release\GildedRose.XML + true + + + + + True + + + + + + + + + + + 11 + + + + + $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets + + + + + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets + + + + + + \ No newline at end of file diff --git a/gleam/.gitignore b/gleam/.gitignore new file mode 100644 index 00000000..90b7875b --- /dev/null +++ b/gleam/.gitignore @@ -0,0 +1,5 @@ +*.beam +*.ez +build +erl_crash.dump +manifest.toml diff --git a/gleam/README.md b/gleam/README.md new file mode 100644 index 00000000..61be7f7d --- /dev/null +++ b/gleam/README.md @@ -0,0 +1,20 @@ +# Gilded Rose starting position in Gleam + +I assume you have [installed](https://gleam.run/getting-started/installing/) `gleam` on your system. + +## Run unit tests from the command line + +```sh +$ gleam test +``` + +## Get the TextTest fixture on the command line + +Execute it on the command line with an argument for the number of days: + +```sh +$ gleam run -- --days=30 +``` + +Compare the output to [stdout.gr](../texttests/ThirtyDays/stdout.gr) for validation. +There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. diff --git a/gleam/gleam.toml b/gleam/gleam.toml new file mode 100644 index 00000000..1fcc01b4 --- /dev/null +++ b/gleam/gleam.toml @@ -0,0 +1,21 @@ +name = "program" +version = "1.0.0" + +# Fill out these fields if you intend to generate HTML documentation or publish +# your project to the Hex package manager. +# +# description = "" +# licences = ["Apache-2.0"] +# repository = { type = "github", user = "username", repo = "project" } +# links = [{ title = "Website", href = "https://gleam.run" }] +# +# For a full reference of all the available options, you can have a look at +# https://gleam.run/writing-gleam/gleam-toml/. + +[dependencies] +gleam_stdlib = ">= 0.38.0 and < 1.0.0" +glint = ">= 0.18.1 and < 1.0.0" +argv = ">= 1.0.2 and < 2.0.0" + +[dev-dependencies] +gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/gleam/src/gilded_rose.gleam b/gleam/src/gilded_rose.gleam new file mode 100644 index 00000000..6a5c6e0e --- /dev/null +++ b/gleam/src/gilded_rose.gleam @@ -0,0 +1,109 @@ +import gleam/list + +pub type GildedRose = + List(Item) + +pub type Item { + Item(name: String, sell_in: Int, quality: Int) +} + +pub fn update_quality(inventory: GildedRose) -> GildedRose { + let update_quality_item = fn(item: Item) { + let new_quality = case + item.name != "Aged Brie" + && item.name != "Backstage passes to a TAFKAL80ETC concert" + { + True -> { + case item.quality > 0 { + True -> + case item.name != "Sulfuras, Hand of Ragnaros" { + True -> item.quality - 1 + False -> item.quality + } + False -> item.quality + } + } + False -> { + case item.quality < 50 { + True -> + item.quality + + 1 + + case item.name == "Backstage passes to a TAFKAL80ETC concert" { + True -> + case item.sell_in < 11 { + True -> { + case item.quality < 49 { + True -> + 1 + + case item.sell_in < 6 { + True -> + case item.quality < 48 { + True -> 1 + False -> 0 + } + False -> 0 + } + False -> 0 + } + } + False -> 0 + } + False -> 0 + } + False -> item.quality + } + } + } + + let new_sell_in = case item.name != "Sulfuras, Hand of Ragnaros" { + True -> item.sell_in - 1 + False -> item.sell_in + } + + case new_sell_in < 0 { + True -> + case item.name != "Aged Brie" { + True -> + case item.name != "Backstage passes to a TAFKAL80ETC concert" { + True -> + case new_quality > 0 { + True -> + case item.name != "Sulfuras, Hand of Ragnaros" { + True -> + Item( + ..item, + sell_in: new_sell_in, + quality: new_quality - 1, + ) + False -> Item(..item, sell_in: new_sell_in, quality: 80) + } + False -> + Item(..item, sell_in: new_sell_in, quality: new_quality) + } + False -> + Item( + ..item, + sell_in: new_sell_in, + quality: new_quality - new_quality, + ) + } + False -> + case new_quality < 50 { + True -> + Item(..item, sell_in: new_sell_in, quality: new_quality + 1) + False -> Item(..item, sell_in: new_sell_in, quality: 50) + } + } + False -> + case item.name == "Sulfuras, Hand of Ragnaros" { + True -> Item(..item, sell_in: new_sell_in, quality: 80) + False -> + case new_quality > 50 { + True -> Item(..item, sell_in: new_sell_in, quality: 50) + False -> Item(..item, sell_in: new_sell_in, quality: new_quality) + } + } + } + } + list.map(inventory, update_quality_item) +} diff --git a/gleam/src/program.gleam b/gleam/src/program.gleam new file mode 100644 index 00000000..0d402d12 --- /dev/null +++ b/gleam/src/program.gleam @@ -0,0 +1,76 @@ +import argv +import gilded_rose.{type GildedRose, type Item, Item, update_quality} +import gleam/function +import gleam/int +import gleam/io +import gleam/list +import gleam/string +import glint +import glint/flag + +pub fn main() { + run_cli_app(update_quality) +} + +pub fn run_cli_app(modify_inventory: fn(GildedRose) -> GildedRose) { + let days_flag = "days" + + let number_of_days = + flag.int() + |> flag.default(2) + |> flag.description("Number of days") + + let simulate_inventory = fn() { + use input <- glint.command() + + let assert Ok(number_of_days) = + flag.get_int(from: input.flags, for: days_flag) + simulate(number_of_days, modify_inventory) + } + + let app = + glint.new() + |> glint.with_name("Gilded Rose") + |> glint.group_flag([], days_flag, number_of_days) + |> glint.add(at: [], do: simulate_inventory()) + + io.println("OMGHAI!") + glint.run_and_handle(app, argv.load().arguments, function.identity) +} + +const test_fixture = [ + Item("+5 Dexterity Vest", 10, 20), Item("Aged Brie", 2, 0), + Item("Elixir of the Mongoose", 5, 7), + Item("Sulfuras, Hand of Ragnaros", 0, 80), + Item("Sulfuras, Hand of Ragnaros", -1, 80), + Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + Item("Conjured Mana Cake", 3, 6), +] + +pub fn simulate( + number_of_days: Int, + modify_inventory: fn(GildedRose) -> GildedRose, +) -> GildedRose { + let days = list.range(0, number_of_days) + let display_item = fn(item: Item) { + string.join( + [item.name, int.to_string(item.sell_in), int.to_string(item.quality)], + with: ", ", + ) + } + list.fold(days, from: test_fixture, with: fn(inventory, day) { + case day != 0 { + True -> io.println("") + False -> Nil + } + io.println(string.join( + ["-------- day", int.to_string(day), "--------"], + with: " ", + )) + io.println("name, sellIn, quality") + list.each(inventory, fn(item) { io.println(display_item(item)) }) + modify_inventory(inventory) + }) +} diff --git a/gleam/test/gilded_rose_test.gleam b/gleam/test/gilded_rose_test.gleam new file mode 100644 index 00000000..42ac60ce --- /dev/null +++ b/gleam/test/gilded_rose_test.gleam @@ -0,0 +1,8 @@ +import gilded_rose.{Item, update_quality} +import gleeunit/should + +pub fn update_quality_test() { + let inventory = [Item("foo", 0, 0)] + let assert [new_item] = update_quality(inventory) + new_item.name |> should.equal("fixme") +} diff --git a/gleam/test/program_test.gleam b/gleam/test/program_test.gleam new file mode 100644 index 00000000..ecd12adf --- /dev/null +++ b/gleam/test/program_test.gleam @@ -0,0 +1,5 @@ +import gleeunit + +pub fn main() { + gleeunit.main() +} diff --git a/go/README.md b/go/README.md new file mode 100644 index 00000000..7b017964 --- /dev/null +++ b/go/README.md @@ -0,0 +1,21 @@ +# GO Starter + +- Run : + +```shell +go run texttest_fixture.go [; default: 2] +``` + +- Run tests : + +```shell +go test ./... +``` + +- Run tests and coverage : + +```shell +go test ./... -coverprofile=coverage.out + +go tool cover -html=coverage.out +``` \ No newline at end of file diff --git a/go/gildedrose/gildedrose.go b/go/gildedrose/gildedrose.go new file mode 100644 index 00000000..e0f17e12 --- /dev/null +++ b/go/gildedrose/gildedrose.go @@ -0,0 +1,58 @@ +package gildedrose + +type Item struct { + Name string + SellIn, Quality int +} + +func UpdateQuality(items []*Item) { + for i := 0; i < len(items); i++ { + + if items[i].Name != "Aged Brie" && items[i].Name != "Backstage passes to a TAFKAL80ETC concert" { + if items[i].Quality > 0 { + if items[i].Name != "Sulfuras, Hand of Ragnaros" { + items[i].Quality = items[i].Quality - 1 + } + } + } else { + if items[i].Quality < 50 { + items[i].Quality = items[i].Quality + 1 + if items[i].Name == "Backstage passes to a TAFKAL80ETC concert" { + if items[i].SellIn < 11 { + if items[i].Quality < 50 { + items[i].Quality = items[i].Quality + 1 + } + } + if items[i].SellIn < 6 { + if items[i].Quality < 50 { + items[i].Quality = items[i].Quality + 1 + } + } + } + } + } + + if items[i].Name != "Sulfuras, Hand of Ragnaros" { + items[i].SellIn = items[i].SellIn - 1 + } + + if items[i].SellIn < 0 { + if items[i].Name != "Aged Brie" { + if items[i].Name != "Backstage passes to a TAFKAL80ETC concert" { + if items[i].Quality > 0 { + if items[i].Name != "Sulfuras, Hand of Ragnaros" { + items[i].Quality = items[i].Quality - 1 + } + } + } else { + items[i].Quality = items[i].Quality - items[i].Quality + } + } else { + if items[i].Quality < 50 { + items[i].Quality = items[i].Quality + 1 + } + } + } + } + +} diff --git a/go/gildedrose/gildedrose_test.go b/go/gildedrose/gildedrose_test.go new file mode 100644 index 00000000..ec1bf49b --- /dev/null +++ b/go/gildedrose/gildedrose_test.go @@ -0,0 +1,19 @@ +package gildedrose_test + +import ( + "testing" + + "github.com/emilybache/gildedrose-refactoring-kata/gildedrose" +) + +func Test_Foo(t *testing.T) { + var items = []*gildedrose.Item{ + {"foo", 0, 0}, + } + + gildedrose.UpdateQuality(items) + + if items[0].Name != "fixme" { + t.Errorf("Name: Expected %s but got %s ", "fixme", items[0].Name) + } +} diff --git a/go/go.mod b/go/go.mod new file mode 100644 index 00000000..00f9da4e --- /dev/null +++ b/go/go.mod @@ -0,0 +1,3 @@ +module github.com/emilybache/gildedrose-refactoring-kata + +go 1.18 diff --git a/go/texttest_fixture.go b/go/texttest_fixture.go new file mode 100644 index 00000000..5c965064 --- /dev/null +++ b/go/texttest_fixture.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "os" + "strconv" + + "github.com/emilybache/gildedrose-refactoring-kata/gildedrose" +) + +func main() { + fmt.Println("OMGHAI!") + + var items = []*gildedrose.Item{ + {"+5 Dexterity Vest", 10, 20}, + {"Aged Brie", 2, 0}, + {"Elixir of the Mongoose", 5, 7}, + {"Sulfuras, Hand of Ragnaros", 0, 80}, + {"Sulfuras, Hand of Ragnaros", -1, 80}, + {"Backstage passes to a TAFKAL80ETC concert", 15, 20}, + {"Backstage passes to a TAFKAL80ETC concert", 10, 49}, + {"Backstage passes to a TAFKAL80ETC concert", 5, 49}, + {"Conjured Mana Cake", 3, 6}, // <-- :O + } + + days := 2 + var err error + if len(os.Args) > 1 { + days, err = strconv.Atoi(os.Args[1]) + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + days++ + } + + for day := 0; day < days; day++ { + fmt.Printf("-------- day %d --------\n", day) + fmt.Println("Name, SellIn, Quality") + for i := 0; i < len(items); i++ { + fmt.Println(items[i]) + } + fmt.Println("") + gildedrose.UpdateQuality(items) + } +} diff --git a/haskell/.gitignore b/haskell/.gitignore new file mode 100644 index 00000000..1be183c3 --- /dev/null +++ b/haskell/.gitignore @@ -0,0 +1,11 @@ +*.hi +*.ho +TAGS +*.log +*.profile +/dist +/cabal.config + +/.cabal-sandbox/ +/cabal.sandbox.config + diff --git a/haskell/README.md b/haskell/README.md new file mode 100644 index 00000000..835ea5f5 --- /dev/null +++ b/haskell/README.md @@ -0,0 +1,20 @@ +# Haskell port of the Gilded-Rose Kata + +This is a Haskell port of the *Gilded-Rose-Kata*. + +## Prerequisite + +If you don't have a recent Stack version installed in your system, follow the +[installation instructions](https://docs.haskellstack.org/en/stable/install_and_upgrade/) +for your operating system. + +## Building and Running + +Run `stack build` initially, then `stack test` to execute the tests after +each refactoring. + +To execute the program run `stack run [days]` where `[days]` denotes an optional +parameter for the number of days to simulate. + +Tests are in `test/GildedRoseSpec.hs`. Refer to http://hspec.github.io/ for +more information about writing tests using `Hspec`. diff --git a/haskell/gilded-rose.cabal b/haskell/gilded-rose.cabal new file mode 100644 index 00000000..dc5458e7 --- /dev/null +++ b/haskell/gilded-rose.cabal @@ -0,0 +1,35 @@ +name: gilded-rose +version: 0.1.0.0 +synopsis: Haskell-port of the gilded rose kata +license: GPL +author: Sven Heyll +maintainer: sven.heyll@gmail.com +category: System +build-type: Simple +cabal-version: >=1.10 + +library + exposed-modules: GildedRose + build-depends: base >=4.7 + hs-source-dirs: src + default-language: Haskell2010 + +executable gilded-rose + main-is: Main.hs + build-depends: gilded-rose, base >=4.7 + hs-source-dirs: src + default-language: Haskell2010 + other-modules: GildedRose + +test-suite spec + type: exitcode-stdio-1.0 + ghc-options: -Wall + hs-source-dirs: test + default-language: Haskell2010 + main-is: Spec.hs + other-modules: GildedRoseSpec + build-depends: base >=4.7 + , gilded-rose + , hspec + , hspec-expectations + , QuickCheck diff --git a/haskell/src/GildedRose.hs b/haskell/src/GildedRose.hs new file mode 100644 index 00000000..8e710c8e --- /dev/null +++ b/haskell/src/GildedRose.hs @@ -0,0 +1,70 @@ +module GildedRose where + +type GildedRose = [Item] + +data Item = Item String Int Int + deriving (Eq) + +instance Show Item where + show (Item name sellIn quality) = + name ++ ", " ++ show sellIn ++ ", " ++ show quality + +updateQuality :: GildedRose -> GildedRose +updateQuality = map updateQualityItem + where + updateQualityItem (Item name sellIn quality) = + let + quality' = + if name /= "Aged Brie" + && name /= "Backstage passes to a TAFKAL80ETC concert" + then + if quality > 0 + then + if name /= "Sulfuras, Hand of Ragnaros" + then quality - 1 + else quality + else quality + else + if quality < 50 + then + quality + 1 + + (if name == "Backstage passes to a TAFKAL80ETC concert" + then + if sellIn < 11 + then + if quality < 49 + then + 1 + (if sellIn < 6 + then + if quality < 48 + then 1 + else 0 + else 0) + else 0 + else 0 + else 0) + else quality + + sellIn' = + if name /= "Sulfuras, Hand of Ragnaros" + then sellIn - 1 + else sellIn + in + if sellIn' < 0 + then + if name /= "Aged Brie" + then + if name /= "Backstage passes to a TAFKAL80ETC concert" + then + if quality' > 0 + then + if name /= "Sulfuras, Hand of Ragnaros" + then (Item name sellIn' (quality' - 1)) + else (Item name sellIn' quality') + else (Item name sellIn' quality') + else (Item name sellIn' (quality' - quality')) + else + if quality' < 50 + then (Item name sellIn' (quality' + 1)) + else (Item name sellIn' quality') + else (Item name sellIn' quality') diff --git a/haskell/src/Main.hs b/haskell/src/Main.hs new file mode 100644 index 00000000..a5fad66c --- /dev/null +++ b/haskell/src/Main.hs @@ -0,0 +1,44 @@ +module Main where + +import System.Environment +import GildedRose + + +main :: IO () +main = do + putStrLn "OMGHAI!" + + let inventoriesWithDay = inventories `zip` [0..] + inventories = iterate updateQuality initialInventory + + numberOfDays <- daysFromArg + mapM_ printUpdate (take numberOfDays inventoriesWithDay) + + where + printUpdate :: (GildedRose, Int) -> IO () + printUpdate (items, day) = do + putStrLn ("-------- day " ++ show day ++ " --------") + putStrLn "name, sellIn, quality" + mapM_ (putStrLn . show) items + putStrLn "" + + daysFromArg :: IO Int + daysFromArg = do + args <- getArgs + return $ if length args > 0 + then read (head args) + else 20 + + initialInventory :: GildedRose + initialInventory = + [ Item "+5 Dexterity Vest" 10 20 + , Item "Aged Brie" 2 0 + , Item "Elixir of the Mongoose" 5 7 + , Item "Sulfuras, Hand of Ragnaros" 0 80 + , Item "Sulfuras, Hand of Ragnaros" (-1) 80 + , Item "Backstage passes to a TAFKAL80ETC concert" 15 20 + , Item "Backstage passes to a TAFKAL80ETC concert" 10 49 + , Item "Backstage passes to a TAFKAL80ETC concert" 5 49 + -- this conjured item does not work properly yet + , Item "Conjured Mana Cake" 3 6 + ] diff --git a/haskell/stack.yaml b/haskell/stack.yaml new file mode 100644 index 00000000..3e184f84 --- /dev/null +++ b/haskell/stack.yaml @@ -0,0 +1,3 @@ +resolver: lts-12.14 +packages: +- . diff --git a/haskell/test/GildedRoseSpec.hs b/haskell/test/GildedRoseSpec.hs new file mode 100644 index 00000000..cdcaaddd --- /dev/null +++ b/haskell/test/GildedRoseSpec.hs @@ -0,0 +1,14 @@ +module GildedRoseSpec (spec) where + +import Test.Hspec +import GildedRose + +spec :: Spec +spec = + describe "updateQuality" $ do + + it "fixme" $ + let inventory = [Item "foo" 0 0] + actual = updateQuality inventory + expected = [] + in actual `shouldBe` expected diff --git a/haskell/test/Spec.hs b/haskell/test/Spec.hs new file mode 100644 index 00000000..a824f8c3 --- /dev/null +++ b/haskell/test/Spec.hs @@ -0,0 +1 @@ +{-# OPTIONS_GHC -F -pgmF hspec-discover #-} diff --git a/janet/.gitignore b/janet/.gitignore new file mode 100644 index 00000000..6743d20f --- /dev/null +++ b/janet/.gitignore @@ -0,0 +1,10 @@ +* + +!.gitignore +!project.janet +!README.md + +!src/ +!src/main.janet +!test/ +!test/*.janet diff --git a/janet/README.md b/janet/README.md new file mode 100644 index 00000000..7cdffd9b --- /dev/null +++ b/janet/README.md @@ -0,0 +1,39 @@ +# Gilded Rose + +This is the Gilded Rose kata in [janet](https://janet-lang.org). + +## Getting started + +We'll need to install [janet and jpm](https://janet-lang.org/docs/index.html). + +## Running texttest + +To produce output suitable for texttest, we can run: + +```sh +janet test/texttest.janet +``` + +We can specify the number of days as an argument: + +```sh +janet test/texttest.janet 30 +``` + +## Running tests + +First, we install the test library dependency using `jpm -l deps`. Then we can run all the tests using: + +```sh +jpm -l test +``` + +The testing library being used is [judge](https://github.com/ianthehenry/judge), that enables the writing of inline snapshot tests. +Specifically, Judge has the ability to capture a snapshot of stdout, which gives us a similar experience to that offered by texttest, but natively in the janet tests. + +Having run `jpm -l deps`, we now have a (repository-local) installed executable of judge, which we can run directly using: + +```sh +./jpm_tree/bin/judge +``` +(pass `-h` for usage). diff --git a/janet/project.janet b/janet/project.janet new file mode 100644 index 00000000..789199d4 --- /dev/null +++ b/janet/project.janet @@ -0,0 +1,6 @@ +(declare-project + :name "gilded-rose" + :description "A program for updating inventory at the Gilded Rose" + :dependencies ["https://github.com/ianthehenry/judge.git"]) + +(task "test" [] (shell "jpm_tree/bin/judge")) diff --git a/janet/src/main.janet b/janet/src/main.janet new file mode 100644 index 00000000..1ec8400e --- /dev/null +++ b/janet/src/main.janet @@ -0,0 +1,46 @@ +(defn item [name quality sell-in] @{:name name :quality quality :sell-in sell-in}) + +(defn update-quality [items] + (for i 0 (length items) + (if (and + (not (= "Aged Brie" ((get items i) :name))) + (not (= "Backstage passes to a TAFKAL80ETC concert" ((get items i) :name)))) + (if (< 0 ((get items i) :quality)) + (if (not (= "Sulfuras, Hand of Ragnaros" ((get items i) :name))) + (put (get items i) :quality (- ((get items i) :quality) 1)))) + (if (> 50 ((get items i) :quality)) + (do + (put (get items i) :quality (+ ((get items i) :quality) 1)) + (if (= "Backstage passes to a TAFKAL80ETC concert" ((get items i) :name)) + (do + (if (> 11 ((get items i) :sell-in)) + (if (> 50 ((get items i) :quality)) + (put (get items i) :quality (+ ((get items i) :quality) 1)))) + (if (> 6 ((get items i) :sell-in)) + (if (> 50 ((get items i) :quality)) + (put (get items i) :quality (+ ((get items i) :quality) 1))))))))) + (if (not (= "Sulfuras, Hand of Ragnaros" ((get items i) :name))) + (put (get items i) :sell-in (- ((get items i) :sell-in) 1))) + (if (> 0 ((get items i) :sell-in)) + (if (not (= "Aged Brie" ((get items i) :name))) + (if (not (= "Backstage passes to a TAFKAL80ETC concert" ((get items i) :name))) + (if (< 0 ((get items i) :quality)) + (if (not (= "Sulfuras, Hand of Ragnaros" ((get items i) :name))) + (put (get items i) :quality (- ((get items i) :quality) 1)))) + (put (get items i) :quality (- ((get items i) :quality) ((get items i) :quality)))) + (if (> 50 ((get items i) :quality)) + (put (get items i) :quality (+ ((get items i) :quality) 1))))))) + +(defn- item-to-str [x] + (string (get x :name) ", " (get x :sell-in) ", " (get x :quality))) + +(defn run [days items] + (print "OMGHAI!") + (for i 0 (+ 1 days) + (do + (print (string "-------- day " i " --------")) + (print "name, sellIn, quality") + (for j 0 (length items) + (print (item-to-str (get items j)))) + (print "") + (update-quality items)))) diff --git a/janet/test/gilded-rose.janet b/janet/test/gilded-rose.janet new file mode 100644 index 00000000..789f3f50 --- /dev/null +++ b/janet/test/gilded-rose.janet @@ -0,0 +1,7 @@ +(import ../src/main :as shop) +(use judge) + +(do + (def items [(shop/item "foo" 0 0)]) + (shop/update-quality items) + (test ((first items) :name) "fixme")) diff --git a/janet/test/texttest.janet b/janet/test/texttest.janet new file mode 100644 index 00000000..a8e3be2e --- /dev/null +++ b/janet/test/texttest.janet @@ -0,0 +1,22 @@ +(import ../src/main :as shop) + +(defn get-items [] + [ (shop/item "+5 Dexterity Vest" 20 10) + (shop/item "Aged Brie" 0 2) + (shop/item "Elixir of the Mongoose" 7 5) + (shop/item "Sulfuras, Hand of Ragnaros" 80 0) + (shop/item "Sulfuras, Hand of Ragnaros" 80 -1) + (shop/item "Backstage passes to a TAFKAL80ETC concert" 20 15) + (shop/item "Backstage passes to a TAFKAL80ETC concert" 49 10) + (shop/item "Backstage passes to a TAFKAL80ETC concert" 49 5) + (shop/item "Conjured Mana Cake" 6 3)]) + +# judge allows for snapshot testing of stdout, much like the texttest tool. +# So something like the following would create a janet-native test of the output of shop/run: +# +# (test-stdout (shop/run 30 (get-items))) + +(defn main [& args] + (def num-days (scan-number (or (get args 1) "-1"))) + (def items (get-items)) + (shop/run num-days items)) diff --git a/jq/README.md b/jq/README.md new file mode 100644 index 00000000..1e823968 --- /dev/null +++ b/jq/README.md @@ -0,0 +1,21 @@ +# Requirements + +jq + +# Failing Unit Test + +```shell +./test-gilded-rose.sh +``` + +# TextTest Fixture + +```shell +jq -nr "$(cat gilded-rose.jq) $(cat texttest_fixture.jq)" +``` + +Specify days (e.g. 10 days): + +```shell +jq --arg days 10 -nr "$(cat gilded-rose.jq) $(cat texttest_fixture.jq)" +``` diff --git a/jq/gilded-rose.jq b/jq/gilded-rose.jq new file mode 100644 index 00000000..e9c7d941 --- /dev/null +++ b/jq/gilded-rose.jq @@ -0,0 +1,52 @@ +def update_quality: + [ + .[] + | + if .name != "Aged Brie" and .name != "Backstage passes to a TAFKAL80ETC concert" then + if .quality > 0 then + if .name != "Sulfuras, Hand of Ragnaros" then + .quality = .quality - 1 + end + end + else + if .quality < 50 then + .quality = .quality + 1 + | + if .name == "Backstage passes to a TAFKAL80ETC concert" then + if .sell_in < 11 then + if .quality < 50 then + .quality = .quality + 1 + end + end + | + if .sell_in < 6 then + if .quality < 50 then + .quality = .quality + 1 + end + end + end + end + end + | + if .name != "Sulfuras, Hand of Ragnaros" then + .sell_in = .sell_in - 1 + end + | + if .sell_in < 0 then + if .name != "Aged Brie" then + if .name != "Backstage passes to a TAFKAL80ETC concert" then + if .quality > 0 then + if .name != "Sulfuras, Hand of Ragnaros" then + .quality = .quality - 1 + end + end + else + .quality = .quality - .quality + end + else + if .quality < 50 then + .quality = .quality + 1 + end + end + end + ]; diff --git a/jq/test-gilded-rose.sh b/jq/test-gilded-rose.sh new file mode 100755 index 00000000..b667d26f --- /dev/null +++ b/jq/test-gilded-rose.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +jq -en "$(cat gilded-rose.jq)"'[{name:"foo",sell_in:0,quality:0}] | update_quality | .[].name == "fixme"' diff --git a/jq/texttest_fixture.jq b/jq/texttest_fixture.jq new file mode 100644 index 00000000..9f153790 --- /dev/null +++ b/jq/texttest_fixture.jq @@ -0,0 +1,22 @@ +"OMGHAI!", +( + [ + { name: "+5 Dexterity Vest", sell_in: 10, quality: 20 }, + { name: "Aged Brie", sell_in: 2, quality: 0 }, + { name: "Elixir of the Mongoose", sell_in: 5, quality: 7 }, + { name: "Sulfuras, Hand of Ragnaros", sell_in: 0, quality: 80 }, + { name: "Sulfuras, Hand of Ragnaros", sell_in: -1, quality: 80 }, + { name: "Backstage passes to a TAFKAL80ETC concert", sell_in: 15, quality: 20 }, + { name: "Backstage passes to a TAFKAL80ETC concert", sell_in: 10, quality: 49 }, + { name: "Backstage passes to a TAFKAL80ETC concert", sell_in: 5, quality: 49 }, + { name: "Conjured Mana Cake", sell_in: 3, quality: 6} # <-- :O + ] | + { items: ., day: 0 } | + recurse(.day += 1 | .items = (.items | update_quality); .day <= ($ARGS.named.days // 2 | tonumber)) | + ( + (["-------- day ", (.day | tostring), " --------"] | add), + ("name, sellIn, quality"), + (.items[] | [.name, .sell_in, .quality | tostring] | join(", ")), + "" + ) +) diff --git a/js-jasmine/.gitignore b/js-jasmine/.gitignore new file mode 100644 index 00000000..2ccbe465 --- /dev/null +++ b/js-jasmine/.gitignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/js-jasmine/package.json b/js-jasmine/package.json new file mode 100644 index 00000000..7a053281 --- /dev/null +++ b/js-jasmine/package.json @@ -0,0 +1,42 @@ +{ + "name": "gilded-rose-kata", + "version": "1.0.0", + "description": "Gilded Rose kata in Javascript with Jasmine", + "scripts": { + "test": "jasmine" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/emilybache/GildedRose-Refactoring-Kata.git" + }, + "keywords": [ + "kata", + "refactor", + "gilded-rose" + ], + "license": "MIT", + "private": true, + "bugs": { + "url": "https://github.com/emilybache/GildedRose-Refactoring-Kata/issues" + }, + "homepage": "https://github.com/emilybache/GildedRose-Refactoring-Kata", + "devDependencies": { + "@babel/core": "^7.14.0", + "@babel/preset-env": "7.14.0", + "@babel/register": "^7.13.0", + "jasmine": "^3.7.0" + }, + "babel": { + "presets": [ + [ + "env", + { + "targets": { + "node": "current" + } + } + ] + ], + "plugins": [] + } +} diff --git a/js-jasmine/spec/gilded_rose_spec.js b/js-jasmine/spec/gilded_rose_spec.js new file mode 100644 index 00000000..9f267c13 --- /dev/null +++ b/js-jasmine/spec/gilded_rose_spec.js @@ -0,0 +1,10 @@ +var {Shop, Item} = require('../src/gilded_rose.js'); +describe("Gilded Rose", function() { + + it("should foo", function() { + const gildedRose = new Shop([ new Item("foo", 0, 0) ]); + const items = gildedRose.updateQuality(); + expect(items[0].name).toEqual("fixme"); + }); + +}); diff --git a/js-jasmine/spec/support/jasmine.json b/js-jasmine/spec/support/jasmine.json new file mode 100644 index 00000000..cfed10fd --- /dev/null +++ b/js-jasmine/spec/support/jasmine.json @@ -0,0 +1,11 @@ +{ + "spec_dir": "spec", + "spec_files": [ + "**/*[sS]pec.js" + ], + "helpers": [ + "../node_modules/babel-register/lib/node.js" + ], + "oneFailurePerSpec": false, + "randomizeTests": true +} diff --git a/js-jasmine/spec/texttest_fixture.js b/js-jasmine/spec/texttest_fixture.js new file mode 100644 index 00000000..a62ede3f --- /dev/null +++ b/js-jasmine/spec/texttest_fixture.js @@ -0,0 +1,27 @@ + +const { Shop, Item } = require("../src/gilded_rose"); + +const items = [ + new Item("+5 Dexterity Vest", 10, 20), + new Item("Aged Brie", 2, 0), + new Item("Elixir of the Mongoose", 5, 7), + new Item("Sulfuras, Hand of Ragnaros", 0, 80), + new Item("Sulfuras, Hand of Ragnaros", -1, 80), + new Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + + // This Conjured item does not work properly yet + new Item("Conjured Mana Cake", 3, 6), +]; + +const days = Number(process.argv[2]) || 2; +const gildedRose = new Shop(items); + +console.log("OMGHAI!"); +for (let day = 0; day < days; day++) { + console.log(`\n-------- day ${day} --------`); + console.log("name, sellIn, quality"); + items.forEach(item => console.log(`${item.name}, ${item.sellIn}, ${item.quality}`)); + gildedRose.updateQuality(); +} diff --git a/js-jasmine/src/gilded_rose.js b/js-jasmine/src/gilded_rose.js new file mode 100644 index 00000000..acb33985 --- /dev/null +++ b/js-jasmine/src/gilded_rose.js @@ -0,0 +1,66 @@ +class Item { + constructor(name, sellIn, quality){ + this.name = name; + this.sellIn = sellIn; + this.quality = quality; + } +} + +class Shop { + constructor(items=[]){ + this.items = items; + } + updateQuality() { + for (var i = 0; i < this.items.length; i++) { + if (this.items[i].name != 'Aged Brie' && this.items[i].name != 'Backstage passes to a TAFKAL80ETC concert') { + if (this.items[i].quality > 0) { + if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') { + this.items[i].quality = this.items[i].quality - 1; + } + } + } else { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1; + if (this.items[i].name == 'Backstage passes to a TAFKAL80ETC concert') { + if (this.items[i].sellIn < 11) { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1; + } + } + if (this.items[i].sellIn < 6) { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1; + } + } + } + } + } + if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') { + this.items[i].sellIn = this.items[i].sellIn - 1; + } + if (this.items[i].sellIn < 0) { + if (this.items[i].name != 'Aged Brie') { + if (this.items[i].name != 'Backstage passes to a TAFKAL80ETC concert') { + if (this.items[i].quality > 0) { + if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') { + this.items[i].quality = this.items[i].quality - 1; + } + } + } else { + this.items[i].quality = this.items[i].quality - this.items[i].quality; + } + } else { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1; + } + } + } + } + + return this.items; + } +} +module.exports = { + Item, + Shop +} diff --git a/js-jest/.gitignore b/js-jest/.gitignore new file mode 100644 index 00000000..2ccbe465 --- /dev/null +++ b/js-jest/.gitignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/js-jest/README.md b/js-jest/README.md new file mode 100644 index 00000000..b60a7294 --- /dev/null +++ b/js-jest/README.md @@ -0,0 +1,47 @@ +# Gilded Rose in Javascript with Jest + +## Getting started + +Install dependencies + +```sh +npm install +``` + +## Run the unit tests from the Command-Line + +To run all tests + +```sh +npm test +``` + +To run all tests in watch mode + +```sh +npm run test:watch +``` + +To generate test coverage report + +```sh +npm run test:coverage +``` + +## Run the TextTest fixture from the Command-Line + +For e.g. 10 days: + +``` +node test/texttest_fixture.js 10 +``` + +You should make sure the command shown above works when you execute it in a terminal before trying to use TextTest (see below). + + +## Run the TextTest approval test that comes with this project + +There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. You will need to specify the Javascript-Jest executable and interpreter in [config.gr](../texttests/config.gr). Uncomment these lines: + + executable:${TEXTTEST_HOME}/js-jest/test/texttest_fixture.js + interpreter:node diff --git a/js-jest/package-lock.json b/js-jest/package-lock.json new file mode 100644 index 00000000..93a221bf --- /dev/null +++ b/js-jest/package-lock.json @@ -0,0 +1,3608 @@ +{ + "name": "gilded-rose-kata", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gilded-rose-kata", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "jest": "^29.7.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "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.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", + "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", + "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.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.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0", + "@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.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "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.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "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.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "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.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "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.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@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.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@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/@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/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/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/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.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "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==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "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.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "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/@types/babel__core": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", + "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", + "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.5", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", + "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", + "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", + "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz", + "integrity": "sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/node": { + "version": "20.8.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.6.tgz", + "integrity": "sha512-eWO4K2Ji70QzKUqRy6oyJWUeB7+g2cRagT3T/nxYibYcT4y2BDL8lqolRXjTHmkZCdJfIPaY73KbJAZmcryxTQ==", + "dev": true, + "dependencies": { + "undici-types": "~5.25.1" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.28", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.28.tgz", + "integrity": "sha512-N3e3fkS86hNhtk6BEnc0rj3zcehaxx8QWhCROJkqpl5Zaoi7nAic3jH8q94jVD3zu5LGk+PUB6KAiDmimYOEQw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==", + "dev": true + }, + "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/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/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/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/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/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/browserslist": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "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.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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/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.30001549", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz", + "integrity": "sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==", + "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/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/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/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/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/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/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-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/electron-to-chromium": { + "version": "1.4.556", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.556.tgz", + "integrity": "sha512-6RPN0hHfzDU8D56E72YkDvnLw5Cj2NMXZGg3UkgyoHxjVhG99KZpsKgBWMmTy0Ei89xwan+rbRsVB9yzATmYzQ==", + "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.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/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/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/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/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/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-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": "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/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": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "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/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/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-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-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-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/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.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "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": "^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.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "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.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "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-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-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-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.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "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": "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/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/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.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "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/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/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": "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/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/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-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.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "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/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/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/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/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/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/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/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/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-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/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/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/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/undici-types": { + "version": "5.25.3", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", + "dev": true + }, + "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-to-istanbul": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", + "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", + "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/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": "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/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/js-jest/package.json b/js-jest/package.json new file mode 100644 index 00000000..f1713f02 --- /dev/null +++ b/js-jest/package.json @@ -0,0 +1,29 @@ +{ + "name": "gilded-rose-kata", + "version": "1.0.0", + "description": "Gilded Rose kata in JavaScript with Jest", + "scripts": { + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage", + "texttest": "node test/texttest_fixture.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/emilybache/GildedRose-Refactoring-Kata.git" + }, + "keywords": [ + "kata", + "refactor", + "gilded-rose" + ], + "license": "MIT", + "private": true, + "bugs": { + "url": "https://github.com/emilybache/GildedRose-Refactoring-Kata/issues" + }, + "homepage": "https://github.com/emilybache/GildedRose-Refactoring-Kata", + "devDependencies": { + "jest": "^29.7.0" + } +} diff --git a/js-jest/src/gilded_rose.js b/js-jest/src/gilded_rose.js new file mode 100644 index 00000000..48746965 --- /dev/null +++ b/js-jest/src/gilded_rose.js @@ -0,0 +1,67 @@ +class Item { + constructor(name, sellIn, quality){ + this.name = name; + this.sellIn = sellIn; + this.quality = quality; + } +} + +class Shop { + constructor(items=[]){ + this.items = items; + } + updateQuality() { + for (let i = 0; i < this.items.length; i++) { + if (this.items[i].name != 'Aged Brie' && this.items[i].name != 'Backstage passes to a TAFKAL80ETC concert') { + if (this.items[i].quality > 0) { + if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') { + this.items[i].quality = this.items[i].quality - 1; + } + } + } else { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1; + if (this.items[i].name == 'Backstage passes to a TAFKAL80ETC concert') { + if (this.items[i].sellIn < 11) { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1; + } + } + if (this.items[i].sellIn < 6) { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1; + } + } + } + } + } + if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') { + this.items[i].sellIn = this.items[i].sellIn - 1; + } + if (this.items[i].sellIn < 0) { + if (this.items[i].name != 'Aged Brie') { + if (this.items[i].name != 'Backstage passes to a TAFKAL80ETC concert') { + if (this.items[i].quality > 0) { + if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') { + this.items[i].quality = this.items[i].quality - 1; + } + } + } else { + this.items[i].quality = this.items[i].quality - this.items[i].quality; + } + } else { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1; + } + } + } + } + + return this.items; + } +} + +module.exports = { + Item, + Shop +} diff --git a/js-jest/test/gilded_rose.test.js b/js-jest/test/gilded_rose.test.js new file mode 100644 index 00000000..c8e0e3d7 --- /dev/null +++ b/js-jest/test/gilded_rose.test.js @@ -0,0 +1,9 @@ +const {Shop, Item} = require("../src/gilded_rose"); + +describe("Gilded Rose", function() { + it("should foo", function() { + const gildedRose = new Shop([new Item("foo", 0, 0)]); + const items = gildedRose.updateQuality(); + expect(items[0].name).toBe("fixme"); + }); +}); diff --git a/js-jest/test/texttest_fixture.js b/js-jest/test/texttest_fixture.js new file mode 100644 index 00000000..6f1a8e05 --- /dev/null +++ b/js-jest/test/texttest_fixture.js @@ -0,0 +1,28 @@ + +const { Shop, Item } = require("../src/gilded_rose"); + +const items = [ + new Item("+5 Dexterity Vest", 10, 20), + new Item("Aged Brie", 2, 0), + new Item("Elixir of the Mongoose", 5, 7), + new Item("Sulfuras, Hand of Ragnaros", 0, 80), + new Item("Sulfuras, Hand of Ragnaros", -1, 80), + new Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + + // This Conjured item does not work properly yet + new Item("Conjured Mana Cake", 3, 6), +]; + +const days = Number(process.argv[2]) || 2; +const gildedRose = new Shop(items); + +console.log("OMGHAI!"); +for (let day = 0; day < days + 1; day++) { + console.log(`-------- day ${day} --------`); + console.log("name, sellIn, quality"); + items.forEach(item => console.log(`${item.name}, ${item.sellIn}, ${item.quality}`)); + gildedRose.updateQuality(); + console.log("") +} diff --git a/js-mocha/.gitignore b/js-mocha/.gitignore new file mode 100644 index 00000000..2ccbe465 --- /dev/null +++ b/js-mocha/.gitignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/js-mocha/README.md b/js-mocha/README.md new file mode 100644 index 00000000..645c6bb4 --- /dev/null +++ b/js-mocha/README.md @@ -0,0 +1,47 @@ +# Gilded Rose in Javascript with Mocha + +## Getting started + +Install dependencies + +```sh +npm install +``` + +## Run the unit tests from the Command-Line + +To run all tests + +```sh +npm test +``` + +To run all tests in watch mode + +```sh +npm run test:watch +``` + +To generate test coverage report + +```sh +npm run test:coverage +``` + +## Run the TextTest fixture from the Command-Line + +For e.g. 10 days: + +``` +node test/texttest_fixture.js 10 +``` + +You should make sure the command shown above works when you execute it in a terminal before trying to use TextTest (see below). + + +## Run the TextTest approval test that comes with this project + +There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. You will need to specify the Javascript-Jest executable and interpreter in [config.gr](../texttests/config.gr). Uncomment these lines: + + executable:${TEXTTEST_HOME}/js-mocha/test/texttest_fixture.js + interpreter:node diff --git a/js-mocha/package.json b/js-mocha/package.json new file mode 100644 index 00000000..6c6c349e --- /dev/null +++ b/js-mocha/package.json @@ -0,0 +1,30 @@ +{ + "name": "gilded-rose-kata", + "version": "1.0.0", + "description": "Gilded Rose kata in Javascript with Mocha", + "scripts": { + "test": "mocha --require @babel/register" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/emilybache/GildedRose-Refactoring-Kata.git" + }, + "keywords": [ + "kata", + "refactor", + "gilded-rose" + ], + "license": "MIT", + "private": true, + "bugs": { + "url": "https://github.com/emilybache/GildedRose-Refactoring-Kata/issues" + }, + "homepage": "https://github.com/emilybache/GildedRose-Refactoring-Kata", + "devDependencies": { + "@babel/core": "^7.14.0", + "@babel/preset-env": "^7.14.0", + "@babel/register": "^7.13.0", + "chai": "^4.2.0", + "mocha": "^8.4.0" + } +} diff --git a/js-mocha/src/gilded_rose.js b/js-mocha/src/gilded_rose.js new file mode 100644 index 00000000..acb33985 --- /dev/null +++ b/js-mocha/src/gilded_rose.js @@ -0,0 +1,66 @@ +class Item { + constructor(name, sellIn, quality){ + this.name = name; + this.sellIn = sellIn; + this.quality = quality; + } +} + +class Shop { + constructor(items=[]){ + this.items = items; + } + updateQuality() { + for (var i = 0; i < this.items.length; i++) { + if (this.items[i].name != 'Aged Brie' && this.items[i].name != 'Backstage passes to a TAFKAL80ETC concert') { + if (this.items[i].quality > 0) { + if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') { + this.items[i].quality = this.items[i].quality - 1; + } + } + } else { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1; + if (this.items[i].name == 'Backstage passes to a TAFKAL80ETC concert') { + if (this.items[i].sellIn < 11) { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1; + } + } + if (this.items[i].sellIn < 6) { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1; + } + } + } + } + } + if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') { + this.items[i].sellIn = this.items[i].sellIn - 1; + } + if (this.items[i].sellIn < 0) { + if (this.items[i].name != 'Aged Brie') { + if (this.items[i].name != 'Backstage passes to a TAFKAL80ETC concert') { + if (this.items[i].quality > 0) { + if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') { + this.items[i].quality = this.items[i].quality - 1; + } + } + } else { + this.items[i].quality = this.items[i].quality - this.items[i].quality; + } + } else { + if (this.items[i].quality < 50) { + this.items[i].quality = this.items[i].quality + 1; + } + } + } + } + + return this.items; + } +} +module.exports = { + Item, + Shop +} diff --git a/js-mocha/test/test_gilded_rose.js b/js-mocha/test/test_gilded_rose.js new file mode 100644 index 00000000..b367571c --- /dev/null +++ b/js-mocha/test/test_gilded_rose.js @@ -0,0 +1,11 @@ +var {expect} = require('chai'); +var {Shop, Item} = require('../src/gilded_rose.js'); +describe("Gilded Rose", function() { + + it("should foo", function() { + const gildedRose = new Shop([ new Item("foo", 0, 0) ]); + const items = gildedRose.updateQuality(); + expect(items[0].name).to.equal("fixme"); + }); + +}); diff --git a/js-mocha/test/texttest_fixture.js b/js-mocha/test/texttest_fixture.js new file mode 100644 index 00000000..6f1a8e05 --- /dev/null +++ b/js-mocha/test/texttest_fixture.js @@ -0,0 +1,28 @@ + +const { Shop, Item } = require("../src/gilded_rose"); + +const items = [ + new Item("+5 Dexterity Vest", 10, 20), + new Item("Aged Brie", 2, 0), + new Item("Elixir of the Mongoose", 5, 7), + new Item("Sulfuras, Hand of Ragnaros", 0, 80), + new Item("Sulfuras, Hand of Ragnaros", -1, 80), + new Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + + // This Conjured item does not work properly yet + new Item("Conjured Mana Cake", 3, 6), +]; + +const days = Number(process.argv[2]) || 2; +const gildedRose = new Shop(items); + +console.log("OMGHAI!"); +for (let day = 0; day < days + 1; day++) { + console.log(`-------- day ${day} --------`); + console.log("name, sellIn, quality"); + items.forEach(item => console.log(`${item.name}, ${item.sellIn}, ${item.quality}`)); + gildedRose.updateQuality(); + console.log("") +} diff --git a/julia/.gitignore b/julia/.gitignore new file mode 100644 index 00000000..20fe29d1 --- /dev/null +++ b/julia/.gitignore @@ -0,0 +1,5 @@ +*.jl.*.cov +*.jl.cov +*.jl.mem +/Manifest.toml +/docs/build/ diff --git a/julia/gilded_rose.jl b/julia/gilded_rose.jl new file mode 100644 index 00000000..77180a90 --- /dev/null +++ b/julia/gilded_rose.jl @@ -0,0 +1,90 @@ +import Base + +mutable struct Item{T<:Integer} + name::String + sellin::T + quality::T +end + +Base.show(io::IO, x::Item) = print(io, "$(x.name), $(x.sellin), $(x.quality)") + +struct GildedRose + items +end + +function update_quality!(gr::GildedRose) + for item in gr.items + if item.name != "Aged Brie" && item.name != "Backstage passes to a TAFKAL80ETC concert" + if item.quality > 0 + if item.name != "Sulfuras, Hand of Ragnaros" + item.quality = item.quality - 1 + end + end + else + if item.quality < 50 + item.quality = item.quality + 1 + if item.name == "Backstage passes to a TAFKAL80ETC concert" + if item.sellin < 11 + if item.quality < 50 + item.quality = item.quality + 1 + end + end + if item.sellin < 6 + if item.quality < 50 + item.quality = item.quality + 1 + end + end + end + end + end + if item.name != "Sulfuras, Hand of Ragnaros" + item.sellin = item.sellin - 1 + end + if item.sellin < 0 + if item.name != "Aged Brie" + if item.name != "Backstage passes to a TAFKAL80ETC concert" + if item.quality > 0 + if item.name != "Sulfuras, Hand of Ragnaros" + item.quality = item.quality - 1 + end + end + else + item.quality = item.quality - item.quality + end + else + if item.quality < 50 + item.quality = item.quality + 1 + end + end + end + end + return nothing +end + +# Technically, julia espouses a REPL-driven workflow, so the preferred way to run this +# would be from the REPL. However, if you'd like to run this function from the +# commandline, run `$ julia -e 'include("gilded_rose.jl"); main(;days=3)'` or whatever +# number you want for `days`. +function main(; days::Int64=2) + println("OMGHAI!") + items = [ + Item("+5 Dexterity Vest", 10, 20), + Item("Aged Brie", 2, 0), + Item("Elixir of the Mongoose", 5, 7), + Item("Sulfuras, Hand of Ragnaros", 0, 80), + Item("Sulfuras, Hand of Ragnaros", -1, 80), + Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + Item("Conjured Mana Cake", 3, 6), + ] + for day in 0:days + println("-------- day $day --------") + println("name, sellIn, quality") + for item in items + println(item) + end + println() + update_quality!(GildedRose(items)) + end +end diff --git a/julia/runtests.jl b/julia/runtests.jl new file mode 100644 index 00000000..dca44f05 --- /dev/null +++ b/julia/runtests.jl @@ -0,0 +1,16 @@ +using Test + +# To run the tests, include this file into your REPL +# julia> include("runtests.jl") + +include("gilded_rose.jl") + +@testset "gilded_rose.jl" begin + + # Test foo + items = [Item("foo", 0, 0)] + gildedrose = GildedRose(items) + update_quality!(gildedrose) + @test items[1].name == "fixme" + +end diff --git a/julia/texttest_fixture.jl b/julia/texttest_fixture.jl new file mode 100644 index 00000000..440a5a5e --- /dev/null +++ b/julia/texttest_fixture.jl @@ -0,0 +1,9 @@ +include("gilded_rose.jl") + +if length(ARGS) > 0 + days = parse(Int64, ARGS[1]) +else + days = 2 +end + +main(days=days) diff --git a/lfe/README.md b/lfe/README.md new file mode 100644 index 00000000..872a6b7a --- /dev/null +++ b/lfe/README.md @@ -0,0 +1,10 @@ +GildedRose +===== + +It is assumed you have installed Erlang and rebar3. + +To run the tests do: `rebar3 as test lfe ltest`. + +Checkout the [LFEUnit Emacs plugin](https://github.com/mdbergmann/emacs-lfeunit) when you're on Emacs. + +Otherwise the usual rebar3 things apply. diff --git a/lfe/include/gilded-rose-item.lfe b/lfe/include/gilded-rose-item.lfe new file mode 100644 index 00000000..f9ab5191 --- /dev/null +++ b/lfe/include/gilded-rose-item.lfe @@ -0,0 +1,4 @@ +;;; Item + +(defrecord item + name sellin quality) diff --git a/lfe/rebar.config b/lfe/rebar.config new file mode 100644 index 00000000..5a7ed9a4 --- /dev/null +++ b/lfe/rebar.config @@ -0,0 +1,49 @@ +{erl_opts, [debug_info]}. + +{deps, [ + {lfe, "2.0.1"} +]}. + +{plugins, [ + {rebar3_lfe, "0.4.0"} +]}. + +{lfe, [ + {main, "scripts/main.lfe"} +]}. + +{provider_hooks, [ + {pre, [{compile, {lfe, compile}}]} +]}. + +{xref_checks,[ + undefined_function_calls,undefined_functions,locals_not_used, + deprecated_function_calls,deprecated_functions +]}. + +{profiles, [ + {test, [ + {deps, [ + {proper, "1.3.0"} + ]}, + {plugins, [ + {rebar3_proper, "0.12.0"} + ]}, + {eunit_opts, [verbose]}, + {erl_opts, [{src_dirs, ["src", "test"]}]} + ]} +]}. + +{alias, [ + {coverage, [ + {proper, "-c"}, + {cover, "-v --min_coverage=0"} + ]}, + {check, [ + compile, + xref, + %%dialyzer, + eunit, + coverage + ]} +]}. diff --git a/lfe/scripts/main.lfe b/lfe/scripts/main.lfe new file mode 100644 index 00000000..b9c4c96e --- /dev/null +++ b/lfe/scripts/main.lfe @@ -0,0 +1,10 @@ +#! /usr/bin/env lfescript + +;;; -------------------- +;;; entry point function +;;; -------------------- + +(defun main (args) + (let ((script-name (escript:script_name))) + (io:format "Running script '~s' with args ~p ...~n" `(,script-name ,args)) + (io:format "~p~n" `(,(gilded-rose:my-fun))))) diff --git a/lfe/src/gilded-rose.app.src b/lfe/src/gilded-rose.app.src new file mode 100644 index 00000000..04d9b067 --- /dev/null +++ b/lfe/src/gilded-rose.app.src @@ -0,0 +1,14 @@ +{application, 'gilded-rose', [ + {description, "A run-ready LFE script"}, + {vsn, "0.1"}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/lfe/src/gilded-rose.lfe b/lfe/src/gilded-rose.lfe new file mode 100644 index 00000000..a135ecc7 --- /dev/null +++ b/lfe/src/gilded-rose.lfe @@ -0,0 +1,133 @@ +;; Hi and welcome to team Gilded Rose. As you know, we are a small inn +;; with a prime location in a prominent city ran by a friendly +;; innkeeper named Allison. We also buy and sell only the finest goods. +;; Unfortunately, our goods are constantly degrading in (item-quality item) as they +;; approach their sell by date. We have a system in place that updates +;; our inventory for us. It was developed by a no-nonsense type named +;; Leeroy, who has moved on to new adventures. Your task is to add the +;; new feature to our system so that we can begin selling a new +;; category of items. + +;; First an introduction to our system: +;; All items have a SellIn value which denotes the number of days we have to sell the item +;; All items have a Quality value which denotes how valuable the item is +;; At the end of each day our system lowers both values for every item +;; Pretty simple, right? Well this is where it gets interesting: +;; Once the sell by date has passed, Quality degrades twice as fast +;; The Quality of an item is never negative +;; "Aged Brie" actually increases in Quality the older it gets +;; The Quality of an item is never more than 50 +;; "Sulfuras", being a legendary item, never has to be sold or decreases in Quality +;; "Backstage passes", like aged brie, increases in Quality as it's +;; SellIn value approaches; Quality increases by 2 when there are 10 +;; days or less and by 3 when there are 5 days or less but Quality +;; drops to 0 after the concert + +;; We have recently signed a supplier of conjured items. This requires an update to our system: +;; "Conjured" items degrade in Quality twice as fast as normal items +;; Feel free to make any changes to the UpdateQuality method and add +;; any new code as long as everything still works correctly. However, +;; do not alter the Item class or Items property as those belong to the +;; goblin in the corner who will insta-rage and one-shot you as he +;; doesn't believe in shared code ownership (you can make the +;; UpdateQuality method and Items property static if you like, we'll +;; cover for you). +;; Just for clarification, an item can never have its Quality increase +;; above 50, however "Sulfuras" is a legendary item and as such its +;; Quality is 80 and it never alters. + +;; https://github.com/emilybache/GildedRose-Refactoring-Kata + +;; LFE version by Manfred Bergmann, 2022 + +;;; ================================================================ +;;; Code + +(defmodule gilded-rose + (export all)) + +(include-lib "include/gilded-rose-item.lfe") + +;; update-quality + +(defun update-quality (items) + (lists:map #'update-item/1 items)) + +(defun update-item (item) + (let* ((item (if (and (not (string:equal (item-name item) + "Aged Brie")) + (not (string:equal (item-name item) + "Backstage passes to a TAFKAL80ETC concert"))) + (if (> (item-quality item) 0) + (if (not (string:equal (item-name item) + "Sulfuras, Hand of Ragnaros")) + (set-item-quality item (- (item-quality item) 1)) + item) + item) + (if (< (item-quality item) 50) + (set-item-quality item (+ (item-quality item) 1)) + (if (string:equal (item-name item) + "Backstage passes to a TAFKAL80ETC concert") + (if (< (item-sellin item) 11) + (if (< (item-quality item) 50) + (set-item-quality item (+ (item-quality item) 1)) + item) + item) + (if (< (item-sellin item) 6) + (if (< (item-quality item) 50) + (set-item-quality item (+ (item-quality item) 1)) + item) + item))))) + (item (if (not (string:equal (item-name item) + "Sulfuras, Hand of Ragnaros")) + (set-item-sellin item (- (item-sellin item) 1)) + item))) + (if (< (item-sellin item) 0) + (if (not (string:equal (item-name item) + "Aged Brie")) + (if (not (string:equal (item-name item) + "Backstage passes to a TAFKAL80ETC concert")) + (if (> (item-quality item) 0) + (if (not (string:equal (item-name item) + "Sulfuras, Hand of Ragnaros")) + (set-item-quality item (- (item-quality item) 1)) + item) + item) + (set-item-quality item (- (item-quality item) (item-quality item)))) + (if (< (item-quality item) 50) + (set-item-quality item (+ (item-quality item) 1)) + item)) + item))) + +;;; Example + +(defun print-item (item) + (io:format "~s ~b ~b~n" (list (item-name item) (item-sellin item) (item-quality item)))) + +(defun print-day (day items) + (io:format "-------- day ~b --------~n" (list day)) + (io:format "name, sellin, quality~n") + (lists:foreach #'print-item/1 items) + (io:format "~n")) + +(defrecord gilded-rose items) + +(defun run-gilded-rose (days) + (io:format "OMGHAI!~n") + (let ((items (list (make-item name "+5 Dexterity Vest" sellin 10 quality 20) + (make-item name "Aged Brie" sellin 2 quality 0) + (make-item name "Elixir of the Mongoose" sellin 5 quality 7) + (make-item name "Sulfuras, Hand of Ragnaros" sellin 0 quality 80) + (make-item name "Sulfuras, Hand of Ragnaros" sellin -1 quality 80) + (make-item name "Backstage passes to a TAFKAL80ETC concert" sellin 15 quality 20) + (make-item name "Backstage passes to a TAFKAL80ETC concert" sellin 10 quality 49) + (make-item name "Backstage passes to a TAFKAL80ETC concert" sellin 5 quality 49) + ;; this conjured item does not work properly yet + (make-item name "Conjured Mana Cake" sellin 3 quality 6)))) + + (lists:foldl (lambda (day gr) + (print-day day (gilded-rose-items gr)) + (make-gilded-rose items (update-quality (gilded-rose-items gr)))) + (make-gilded-rose items items) + (lists:seq 0 days)))) + diff --git a/lfe/test/gilded-rose-test.lfe b/lfe/test/gilded-rose-test.lfe new file mode 100644 index 00000000..2e17714a --- /dev/null +++ b/lfe/test/gilded-rose-test.lfe @@ -0,0 +1,13 @@ +(defmodule gilded-rose-test + (behaviour ltest-unit) + (export all) + (import (from gilded-rose + (update-quality 1)))) + +(include-lib "ltest/include/ltest-macros.lfe") + +(include-lib "include/gilded-rose-item.lfe") + +(deftest update-quality-test + (is-equal "fixme" (item-name (car (update-quality + (list (make-item name "foo" sellin 0 quality 0))))))) diff --git a/license.txt b/license.txt new file mode 100644 index 00000000..1eebd817 --- /dev/null +++ b/license.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 @emilybache + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/nim/.gitignore b/nim/.gitignore new file mode 100644 index 00000000..32e1dc0f --- /dev/null +++ b/nim/.gitignore @@ -0,0 +1,3 @@ +nimcache/ +nimblecache/ +htmldocs/ diff --git a/nim/src/gildedrose.nim b/nim/src/gildedrose.nim new file mode 100644 index 00000000..69129341 --- /dev/null +++ b/nim/src/gildedrose.nim @@ -0,0 +1,35 @@ +import items + +proc updateQuality*(items: var seq[Item]) = + for i in 0 ..< items.len: + if items[i].name != "Aged Brie" and items[i].name != "Backstage passes to a TAFKAL80ETC concert": + if items[i].quality > 0: + if items[i].name != "Sulfuras, Hand of Ragnaros": + items[i].quality = items[i].quality - 1 + else: + if items[i].quality < 50: + items[i].quality = items[i].quality + 1 + + if items[i].name == "Backstage passes to a TAFKAL80ETC concert": + if items[i].sellIn < 11: + if items[i].quality < 50: + items[i].quality = items[i].quality + 1 + + if items[i].sellIn < 6: + if items[i].quality < 50: + items[i].quality = items[i].quality + 1 + + if items[i].name != "Sulfuras, Hand of Ragnaros": + items[i].sellIn = items[i].sellIn - 1 + + if items[i].sellIn < 0: + if items[i].name != "Aged Brie": + if items[i].name != "Backstage passes to a TAFKAL80ETC concert": + if items[i].quality > 0: + if items[i].name != "Sulfuras, Hand of Ragnaros": + items[i].quality = items[i].quality - 1 + else: + items[i].quality = items[i].quality - items[i].quality + else: + if items[i].quality < 50: + items[i].quality = items[i].quality + 1 diff --git a/nim/src/items.nim b/nim/src/items.nim new file mode 100644 index 00000000..bb9527dd --- /dev/null +++ b/nim/src/items.nim @@ -0,0 +1,12 @@ +import strformat + +type + Item* = object + name*: string + sellIn*, quality*: int + +proc initItem*(name: string, sellIn, quality: int): Item = + Item(name: name, sellIn: sellIn, quality: quality) + +proc `$`*(item: Item): string = + &"{item.name}, {item.sellIn}, {item.quality}" diff --git a/nim/test/gilded.nim b/nim/test/gilded.nim new file mode 100644 index 00000000..fa441583 --- /dev/null +++ b/nim/test/gilded.nim @@ -0,0 +1,10 @@ +import unittest +import ../src/items +import ../src/gildedrose + +suite "Gilded Rose": + + test "foo": + var items = @[initItem("foo", 0, 0)] + items.updateQuality() + check items[0].name == "fixme" diff --git a/ocaml/.ocamlformat b/ocaml/.ocamlformat new file mode 100644 index 00000000..37525ae3 --- /dev/null +++ b/ocaml/.ocamlformat @@ -0,0 +1 @@ +profile = default diff --git a/ocaml/README.md b/ocaml/README.md new file mode 100644 index 00000000..97ef4bfc --- /dev/null +++ b/ocaml/README.md @@ -0,0 +1,30 @@ +## Gilded Rose Kata for OCaml + +### Requirements + +To run the project, the following package must be available on your computer: +- `opam` `>= 2.0` + +### Installation + +At the root of the _ocaml_ directory, execute: +```sh +opam switch create . --deps-only +eval $(opam env) +``` + +It will install all the required dependencies for the project to run. + +### Running + +This project relies on `dune`. To build it, run this command in your terminal: +```sh +dune exec gilded_rose +``` + +### Testing + +The test suite is built with `Alcostest`. To launch the tests, just type: +```sh +dune runtest +``` diff --git a/ocaml/dune-project b/ocaml/dune-project new file mode 100644 index 00000000..8d314b1d --- /dev/null +++ b/ocaml/dune-project @@ -0,0 +1,20 @@ +(lang dune 3.11) +(generate_opam_files true) + +(name gilded_rose) +(source + (github emilybache/GildedRose-Refactoring-Kata)) +(authors "Maiste ") +(maintainers "Maiste ") +(license MIT) +(documentation https://github.com/emilybache/GildedRose-Refactoring-Kata) + +(package + (name gilded_rose) + (synopsis "Gilded Rose Refactoring Kata") + (description "The Gilded Rose Refactoring Kata in OCaml") + (depends + (ocaml (>= 4.08)) + dune + (alcotest (>= 1.7.0)))) + diff --git a/ocaml/gilded_rose.opam b/ocaml/gilded_rose.opam new file mode 100644 index 00000000..7408f6cb --- /dev/null +++ b/ocaml/gilded_rose.opam @@ -0,0 +1,32 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "Gilded Rose Refactoring Kata" +description: "The Gilded Rose Refactoring Kata in OCaml" +maintainer: ["Maiste "] +authors: ["Maiste "] +license: "MIT" +homepage: "https://github.com/emilybache/GildedRose-Refactoring-Kata" +doc: "https://github.com/emilybache/GildedRose-Refactoring-Kata" +bug-reports: + "https://github.com/emilybache/GildedRose-Refactoring-Kata/issues" +depends: [ + "ocaml" {>= "4.08"} + "dune" {>= "3.11"} + "alcotest" {>= "1.7.0"} + "odoc" {with-doc} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/emilybache/GildedRose-Refactoring-Kata.git" diff --git a/ocaml/lib/dune b/ocaml/lib/dune new file mode 100644 index 00000000..2ef54c3f --- /dev/null +++ b/ocaml/lib/dune @@ -0,0 +1,2 @@ +(library + (name gilded_rose)) diff --git a/ocaml/lib/gilded_rose.ml b/ocaml/lib/gilded_rose.ml new file mode 100644 index 00000000..0087f654 --- /dev/null +++ b/ocaml/lib/gilded_rose.ml @@ -0,0 +1,63 @@ +module Item = struct + type t = { name : string; sell_in : int; quality : int } + + let show { name; sell_in; quality } = + Printf.printf "%s, %d, %d" name sell_in quality + + let v name sell_in quality = { name; sell_in; quality } +end + +module Items = struct + type items = Item.t list + + let v ?(items = []) () = items + + let show items = + List.iter + (fun item -> + Item.show item; + Printf.printf "\n") + items + + let update_quality items = + let update_quality_items ({ name; sell_in; quality } as item : Item.t) = + let quality' = + if + name <> "Aged Brie" + && name <> "Backstage passes to a TAFKAL80ETC concert" + then + if quality > 0 then + if name <> "Sulfuras, Hand of Ragnaros" then quality - 1 + else quality + else quality + else if quality < 50 then + quality + 1 + + + if name = "Backstage passes to a TAFKAL80ETC concert" then + if sell_in < 11 then + if quality < 49 then + 1 + if sell_in < 6 then if quality < 48 then 1 else 0 else 0 + else 0 + else 0 + else 0 + else quality + in + let sell_in' = + if name <> "Sulfuras, Hand of Ragnaros" then sell_in - 1 else sell_in + in + if sell_in' < 0 then + if name <> "Aged Brie" then + if name <> "Backstage passes to a TAFKAL80ETC concert" then + if quality' > 0 then + if name <> "Sulfuras, Hand of Ragnaros" then + { item with sell_in = sell_in'; quality = quality' - 1 } + else { item with sell_in = sell_in'; quality = quality' } + else { item with sell_in = sell_in'; quality = quality' } + else { item with sell_in = sell_in'; quality = quality' - quality' } + else if quality' < 50 then + { item with sell_in = sell_in'; quality = quality' + 1 } + else { item with sell_in = sell_in'; quality = quality' } + else { item with sell_in = sell_in'; quality = quality' } + in + List.map update_quality_items items +end diff --git a/ocaml/test/dune b/ocaml/test/dune new file mode 100644 index 00000000..93d58f02 --- /dev/null +++ b/ocaml/test/dune @@ -0,0 +1,3 @@ +(test + (name gilded_rose) + (libraries gilded_rose alcotest)) diff --git a/ocaml/test/gilded_rose.ml b/ocaml/test/gilded_rose.ml new file mode 100644 index 00000000..7be406ff --- /dev/null +++ b/ocaml/test/gilded_rose.ml @@ -0,0 +1,11 @@ +open Gilded_rose + +let test () = + let items = Items.v ~items:[ Item.v "Foo" 10 20 ] () in + Alcotest.(check string) + "Broken test" "Fixme" + (List.hd items |> fun item -> item.name) + +let () = + let open Alcotest in + run "Gilded Rose" [ ("Our first test", [ test_case "fixme" `Quick test ]) ] diff --git a/pascal/.gitignore b/pascal/.gitignore new file mode 100644 index 00000000..76fb8f67 --- /dev/null +++ b/pascal/.gitignore @@ -0,0 +1,11 @@ + +syntax: glob +*.bak +*.BAK +*.tpu +*.TPU +*.exe +*.EXE +*.$$$ +*.DSK +*.TP diff --git a/pascal/README.md b/pascal/README.md new file mode 100644 index 00000000..ef3e3985 --- /dev/null +++ b/pascal/README.md @@ -0,0 +1,19 @@ +# Pascal port of the Gilded-Rose Kata + +This is a (Turbo) Pascal port of the *Gilded-Rose-Kata*. + +## Building and Running + +* Compile the unit `ROSE.PAS`, this is the Gilded Rose Logic. +* Compile the application `TEXTTEST.PAS` for the Texttest fixture. +* Run `TEXTTEST`. + +## Unit Test + +`TPUNIT.PAS` is a minimalist implementation of xUnit in Pascal style. +There are several assertions available, e.g. `AssertEquals`, `AssertEqualsStr`, `AssertTrue` etc. +It needs _Far Calls_ enabled in compiler options. + +* First compile the unit `TPUNIT.PAS`. +* Then compile application `ROSE_T.PAS`. +* Run `ROSE_T` to run the tests. diff --git a/pascal/ROSE.PAS b/pascal/ROSE.PAS new file mode 100644 index 00000000..f78f3057 --- /dev/null +++ b/pascal/ROSE.PAS @@ -0,0 +1,137 @@ +unit Rose; + +interface + +type + Item = record { 260b memory } + Name: string; + SellIn: Integer; + Quality: Integer; + end; + + Items = array [0..251] of Item; { 64kb memory } + + ListOfItems = record + Elements: ^Items; + Length: Word; + end; + +procedure ResizeList(var List: ListOfItems; Size: Word); + +procedure ClearList(var List: ListOfItems); + +procedure InitItem(var Item: Item; Name: string; SellIn: Integer; Quality: Integer); + +function StrItem(Item: Item): string; + +procedure UpdateQuality(Items: ListOfItems); + +implementation + +procedure ResizeList(var List: ListOfItems; Size: Word); +begin + List.Length := Size; + GetMem(List.Elements, Size * SizeOf(Item)); +end; + +procedure ClearList(var List: ListOfItems); +begin + FreeMem(List.Elements, List.Length * SizeOf(Item)); + List.Length := 0; +end; + +procedure InitItem(var Item: Item; Name: string; SellIn: Integer; Quality: Integer); +begin + Item.Name := Name; + Item.SellIn := SellIn; + Item.Quality := Quality; +end; + +function StrItem(Item: Item): string; +var SellInStr: string; + QualityStr: string; +begin + Str(Item.SellIn, SellInStr); + Str(Item.Quality, QualityStr); + StrItem := Item.Name + ', ' + SellInStr + ', ' + QualityStr; +end; + +procedure UpdateQuality(Items: ListOfItems); +var I: Word; +begin + for I := 0 to Items.Length-1 do + begin + if (Items.Elements^[I].Name <> 'Aged Brie') and + (Items.Elements^[I].Name <> 'Backstage passes to a TAFKAL80ETC concert') then + begin + if Items.Elements^[I].Quality > 0 then + begin + if Items.Elements^[I].Name <> 'Sulfuras, Hand of Ragnaros' then + begin + Items.Elements^[I].Quality := Items.Elements^[I].Quality - 1; + end; + end; + end + else + begin + if Items.Elements^[I].Quality < 50 then + begin + Items.Elements^[I].Quality := Items.Elements^[I].Quality + 1; + + if Items.Elements^[I].Name = 'Backstage passes to a TAFKAL80ETC concert' then + begin + if Items.Elements^[I].SellIn < 11 then + begin + if Items.Elements^[I].Quality < 50 then + begin + Items.Elements^[I].Quality := Items.Elements^[I].Quality + 1; + end; + end; + + if Items.Elements^[I].SellIn < 6 then + begin + if Items.Elements^[I].Quality < 50 then + begin + Items.Elements^[I].Quality := Items.Elements^[I].Quality + 1; + end; + end; + end; + end; + end; + + if Items.Elements^[I].Name <> 'Sulfuras, Hand of Ragnaros' then + begin + Items.Elements^[I].SellIn := Items.Elements^[I].SellIn - 1; + end; + + if Items.Elements^[I].SellIn < 0 then + begin + if Items.Elements^[I].Name <> 'Aged Brie' then + begin + if Items.Elements^[I].Name <> 'Backstage passes to a TAFKAL80ETC concert' then + begin + if Items.Elements^[I].Quality > 0 then + begin + if Items.Elements^[I].Name <> 'Sulfuras, Hand of Ragnaros' then + begin + Items.Elements^[I].Quality := Items.Elements^[I].Quality - 1; + end; + end; + end + else + begin + Items.Elements^[I].Quality := Items.Elements^[I].Quality - Items.Elements^[I].Quality; + end; + end + else + begin + if Items.Elements^[I].Quality < 50 then + begin + Items.Elements^[I].Quality := Items.Elements^[I].Quality + 1; + end; + end; + end; + end; +end; + +end. diff --git a/pascal/ROSE_T.PAS b/pascal/ROSE_T.PAS new file mode 100644 index 00000000..87b6679e --- /dev/null +++ b/pascal/ROSE_T.PAS @@ -0,0 +1,29 @@ +{F+} { need to set Far Calls in Compiler Options too } +program Rose_T; + +uses TPUnit, Rose; + +var Items: ListOfItems; + +procedure CreateItem; +begin + ResizeList(Items, 1); +end; + +procedure DisposeItem; +begin + ClearList(Items); +end; + +procedure Foo; +begin + InitItem(Items.Elements^[0], 'foo', 0, 0); + + UpdateQuality(Items); + + AssertEqualsStr('name', 'fixme', Items.Elements^[0].Name); +end; + +begin + RunFixtures('foo', CreateItem, Foo, DisposeItem); +end. diff --git a/pascal/TEXTTEST.PAS b/pascal/TEXTTEST.PAS new file mode 100644 index 00000000..dc06febc --- /dev/null +++ b/pascal/TEXTTEST.PAS @@ -0,0 +1,60 @@ +program TextTests; + +uses Rose; + +var Items: ListOfItems; + Last: Word; + Days, Day: Integer; + ErrorCode: Integer; + DayStr: string; + I: Word; +begin + WriteLn('OMGHAI!'); + + ResizeList(Items, 9); + + Last := 0; + InitItem(Items.Elements^[Last], '+5 Dexterity Vest', 10, 20); + Inc(Last); + InitItem(Items.Elements^[Last], 'Aged Brie', 2, 0); + Inc(Last); + InitItem(Items.Elements^[Last], 'Elixir of the Mongoose', 5, 7); + Inc(Last); + InitItem(Items.Elements^[Last], 'Sulfuras, Hand of Ragnaros', 0, 80); + Inc(Last); + InitItem(Items.Elements^[Last], 'Sulfuras, Hand of Ragnaros', -1, 80); + Inc(Last); + InitItem(Items.Elements^[Last], 'Backstage passes to a TAFKAL80ETC concert', 15, 20); + Inc(Last); + InitItem(Items.Elements^[Last], 'Backstage passes to a TAFKAL80ETC concert', 10, 49); + Inc(Last); + InitItem(Items.Elements^[Last], 'Backstage passes to a TAFKAL80ETC concert', 5, 49); + Inc(Last); + { this Conjured item doesn't yet work properly } + InitItem(Items.Elements^[Last], 'Conjured Mana Cake', 3, 6); + Inc(Last); + Items.Length := Last; + + Days := 2; + if ParamCount > 0 then + begin + Val(ParamStr(1), Days, ErrorCode); + Inc(Days); + end; + + for Day := 0 to Days-1 do + begin + Str(Day, DayStr); + WriteLn('-------- day ' + DayStr + ' --------'); + WriteLn('name, sellIn, quality'); + for I := 0 to Items.Length-1 do + begin + WriteLn(StrItem(Items.Elements^[I])); + end; + WriteLn(''); + + UpdateQuality(Items); + end; + + ClearList(Items); +end. diff --git a/pascal/TPUNIT.PAS b/pascal/TPUNIT.PAS new file mode 100644 index 00000000..01235de4 --- /dev/null +++ b/pascal/TPUNIT.PAS @@ -0,0 +1,129 @@ +{F+} { need to set Far Calls in Compiler Options too } +(* ------------------------------------------------------------------ *) +(* Minimalist xUnit implementation for Turbo Pascal in TP style. *) +(* Version: 2.01 *) +(* Language: Turbo Pascal 6.01 *) +(* Copyright: (c) 2010 Peter Kofler, www.code-cop.org *) +(* License: BSD, http://www.opensource.org/licenses/bsd-license.php *) +(* ------------------------------------------------------------------ *) +unit TPUnit; + +interface + +{ + uses TPUnit; + + Tests are added as methods without arguments to the test + program as usual and use asserts provided by the unit. + The first failed assertion stops program execution. + + procedure TestAddition; + begin + AssertEquals('use asserts in tests', 2, 1 + 1); + end; + + Due to the lack of introspection each test has to + be called manually in the main body. + + begin + RunTest('TestAddition', TestAddition); + end. +} + +type + TestMethod = procedure; + +{ Asserts } +procedure AssertEquals(Message: string; Expected, Actual: Longint); +procedure AssertEqualsStr(Message: string; Expected, Actual: string); +procedure AssertNotNil(Message: string; Actual: Pointer); +procedure AssertNil(Message: string; Actual: Pointer); +procedure AssertTrue(Message: string; Actual: Boolean); +procedure AssertFalse(Message: string; Actual: Boolean); +procedure Fail(Message: string); + +{ Test Runner } +procedure RunTest(Name: string; Test: TestMethod); +procedure RunFixtures(Name: string; SetUp, Test, TearDown: TestMethod); +procedure Empty; + +implementation + +uses Crt; + +procedure AssertEquals(Message: string; Expected, Actual: Longint); +var ExpectedStr, ActualStr: string; +begin + if Expected <> Actual then + begin + Str(Expected, ExpectedStr); + Str(Actual, ActualStr); + Fail(Concat(Message, ' Expected ', ExpectedStr, ' but was ', ActualStr)); + end; +end; + +procedure AssertEqualsStr(Message: string; Expected, Actual: string); +begin + if Expected <> Actual then + begin + Fail(Concat(Message, ' Expected ', Expected, ' but was ', Actual)); + end; +end; + +procedure AssertNotNil(Message: string; Actual: Pointer); +begin + AssertFalse(Message, Actual = nil); +end; + +procedure AssertNil(Message: string; Actual: Pointer); +begin + AssertTrue(Message, Actual = nil); +end; + +procedure AssertTrue(Message: string; Actual: Boolean); +begin + if not Actual then + begin + Fail(Message); + end; +end; + +procedure AssertFalse(Message: string; Actual: Boolean); +begin + AssertTrue(Message, not Actual); +end; + +procedure Fail(Message: string); +begin + TextColor(Red); + WriteLn(' - FAILED'); + NormVideo; + WriteLn(Message); + + Halt(1); +end; + +procedure Empty; +begin +end; + +procedure RunTest(Name: string; Test: TestMethod); +begin + RunFixtures(Name, Empty, Test, Empty); +end; + +procedure RunFixtures(Name: string; SetUp, Test, TearDown: TestMethod); +begin + Write('TEST ', Name); + SetUp; + Test; + TearDown; + + TextColor(Green); + WriteLn(' - OK'); + NormVideo; +end; + +begin + Crt.ClrScr; +end. diff --git a/perl/GildedRose.pm b/perl/GildedRose.pm new file mode 100644 index 00000000..7cc74a1e --- /dev/null +++ b/perl/GildedRose.pm @@ -0,0 +1,74 @@ +package GildedRose; + +use strict; +use warnings; + +sub new { + my ( $class, %attrs ) = @_; + return bless \%attrs, $class; +} + +sub update_quality { + my $self = shift; + for my $item ( @{ $self->{items} } ) { + if ( $item->{name} ne 'Aged Brie' + && $item->{name} ne 'Backstage passes to a TAFKAL80ETC concert' ) + { + if ( $item->{quality} > 0 ) { + if ( $item->{name} ne 'Sulfuras, Hand of Ragnaros' ) { + $item->{quality} = $item->{quality} - 1; + } + } + } + else { + if ( $item->{quality} < 50 ) { + $item->{quality} = $item->{quality} + 1; + + if ( $item->{name} eq + 'Backstage passes to a TAFKAL80ETC concert' ) + { + if ( $item->{sell_in} < 11 ) { + if ( $item->{quality} < 50 ) { + $item->{quality} = $item->{quality} + 1; + } + } + + if ( $item->{sell_in} < 6 ) { + if ( $item->{quality} < 50 ) { + $item->{quality} = $item->{quality} + 1; + } + } + } + } + } + + if ( $item->{name} ne 'Sulfuras, Hand of Ragnaros' ) { + $item->{sell_in} = $item->{sell_in} - 1; + } + + if ( $item->{sell_in} < 0 ) { + if ( $item->{name} ne 'Aged Brie' ) { + if ( $item->{name} ne + 'Backstage passes to a TAFKAL80ETC concert' ) + { + if ( $item->{quality} > 0 ) { + if ( $item->{name} ne 'Sulfuras, Hand of Ragnaros' ) { + $item->{quality} = $item->{quality} - 1; + } + } + } + else { + $item->{quality} = $item->{quality} - $item->{quality}; + } + } + else { + if ( $item->{quality} < 50 ) { + $item->{quality} = $item->{quality} + 1; + } + } + } + } + return; +} + +1; diff --git a/perl/Item.pm b/perl/Item.pm new file mode 100644 index 00000000..556d215a --- /dev/null +++ b/perl/Item.pm @@ -0,0 +1,16 @@ +package Item; + +use strict; +use warnings; + +sub new { + my ( $class, %attrs ) = @_; + return bless \%attrs, $class; +} + +sub to_string { + my ($self) = @_; + return $self->{name} . ', ' . $self->{sell_in} . ', ' . $self->{quality}; +} + +1; diff --git a/perl/test.pl b/perl/test.pl new file mode 100755 index 00000000..0ffecaf1 --- /dev/null +++ b/perl/test.pl @@ -0,0 +1,18 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Test::More 0.96; + +use_ok 'GildedRose'; +use_ok 'Item'; + +subtest 'foo' => sub { + my $items = [ Item->new( name => 'foo', sell_in => 0, quality => 0 ) ]; + my $app = GildedRose->new( items => $items ); + $app->update_quality(); + is( $app->{items}->[0]->{name}, 'fixme' ); +}; + +done_testing(); diff --git a/perl/texttest_fixture.pl b/perl/texttest_fixture.pl new file mode 100755 index 00000000..565a8b61 --- /dev/null +++ b/perl/texttest_fixture.pl @@ -0,0 +1,72 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use GildedRose; +use Item; + +print 'OMGHAI!', "\n"; +my $items = [ + Item->new( + name => '+5 Dexterity Vest', + sell_in => 10, + quality => 20 + ), + Item->new( + name => 'Aged Brie', + sell_in => 2, + quality => 0 + ), + Item->new( + name => 'Elixir of the Mongoose', + sell_in => 5, + quality => 7 + ), + Item->new( + name => 'Sulfuras, Hand of Ragnaros', + sell_in => 0, + quality => 80 + ), + Item->new( + name => 'Sulfuras, Hand of Ragnaros', + sell_in => -1, + quality => 80 + ), + Item->new( + name => 'Backstage passes to a TAFKAL80ETC concert', + sell_in => 15, + quality => 20 + ), + Item->new( + name => 'Backstage passes to a TAFKAL80ETC concert', + sell_in => 10, + quality => 49 + ), + Item->new( + name => 'Backstage passes to a TAFKAL80ETC concert', + sell_in => 5, + quality => 49 + ), + Item->new( # This Conjured item does not work properly yet + name => 'Conjured Mana Cake', + sell_in => 3, + quality => 6 + ), +]; + +my $days = 2; +if ( $#ARGV >= 0 ) { + $days = $ARGV[0]; +} + +my $gilded_rose = GildedRose->new( items => $items ); +for my $day ( 0 .. $days ) { + print "-------- day $day --------", "\n"; + print 'name, sellIn, quality', "\n"; + for my $item ( @{$items} ) { + print $item->to_string(), "\n"; + } + print "\n"; + $gilded_rose->update_quality(); +} diff --git a/perl6/.gitignore b/perl6/.gitignore new file mode 100644 index 00000000..4a5e4c71 --- /dev/null +++ b/perl6/.gitignore @@ -0,0 +1 @@ +lib/.precomp diff --git a/perl6/lib/GildedRose.pm6 b/perl6/lib/GildedRose.pm6 new file mode 100644 index 00000000..eae84d95 --- /dev/null +++ b/perl6/lib/GildedRose.pm6 @@ -0,0 +1,66 @@ +use v6; + +use Item; + +class GildedRose { + has Item @.items; + + method update_quality { + for @!items -> $item { + if ($item.name ne 'Aged Brie' && $item.name ne 'Backstage passes to a TAFKAL80ETC concert') { + if ( $item.quality > 0 ) { + if ( $item.name ne 'Sulfuras, Hand of Ragnaros' ) { + $item.quality = $item.quality - 1; + } + } + } + else { + if ( $item.quality < 50 ) { + $item.quality = $item.quality + 1; + + if ( $item.name eq 'Backstage passes to a TAFKAL80ETC concert' ) + { + if ( $item.sell_in < 11 ) { + if ( $item.quality < 50 ) { + $item.quality = $item.quality + 1; + } + } + + if ( $item.sell_in < 6 ) { + if ( $item.quality < 50 ) { + $item.quality = $item.quality + 1; + } + } + } + } + } + + if ( $item.name ne 'Sulfuras, Hand of Ragnaros' ) { + $item.sell_in = $item.sell_in - 1; + } + + if ( $item.sell_in < 0 ) { + if ( $item.name ne 'Aged Brie' ) { + if ( $item.name ne + 'Backstage passes to a TAFKAL80ETC concert' ) + { + if ( $item.quality > 0 ) { + if ( $item.name ne 'Sulfuras, Hand of Ragnaros' ) { + $item.quality = $item.quality - 1; + } + } + } + else { + $item.quality = $item.quality - $item.quality; + } + } + else { + if ( $item.quality < 50 ) { + $item.quality = $item.quality + 1; + } + } + } + } + return; + } +}; diff --git a/perl6/lib/Item.pm6 b/perl6/lib/Item.pm6 new file mode 100644 index 00000000..d2e5dd1a --- /dev/null +++ b/perl6/lib/Item.pm6 @@ -0,0 +1,11 @@ +use v6; + +class Item { + has Str $.name; + has Int $.sell_in is rw = 0; + has Int $.quality is rw = 0; + + method Str { + "{$!name}, {$!sell_in}, {$!quality}" + } +}; diff --git a/perl6/test.p6 b/perl6/test.p6 new file mode 100755 index 00000000..5bb61261 --- /dev/null +++ b/perl6/test.p6 @@ -0,0 +1,16 @@ +#!/usr/bin/env perl6 + +use v6; +use Test; +use lib 'lib'; + +use GildedRose; +use Item; + +my $item = Item.new(:name); +my $app = GildedRose.new(items => ($item)); + +$app.update_quality(); +is $app.items[0].name, 'fixme', "first day pass"; + +done-testing; diff --git a/perl6/texttest_fixture.p6 b/perl6/texttest_fixture.p6 new file mode 100755 index 00000000..d3e405d7 --- /dev/null +++ b/perl6/texttest_fixture.p6 @@ -0,0 +1,70 @@ +#!/usr/bin/env perl6 + +use v6; + +use lib 'lib'; + +use GildedRose; +use Item; + +print 'OMGHAI!', "\n"; +my @items = ( + Item.new( + name => '+5 Dexterity Vest', + sell_in => 10, + quality => 20 + ), + Item.new( + name => 'Aged Brie', + sell_in => 2, + quality => 0 + ), + Item.new( + name => 'Elixir of the Mongoose', + sell_in => 5, + quality => 7 + ), + Item.new( + name => 'Sulfuras, Hand of Ragnaros', + sell_in => 0, + quality => 80 + ), + Item.new( + name => 'Sulfuras, Hand of Ragnaros', + sell_in => -1, + quality => 80 + ), + Item.new( + name => 'Backstage passes to a TAFKAL80ETC concert', + sell_in => 15, + quality => 20 + ), + Item.new( + name => 'Backstage passes to a TAFKAL80ETC concert', + sell_in => 10, + quality => 49 + ), + Item.new( + name => 'Backstage passes to a TAFKAL80ETC concert', + sell_in => 5, + quality => 49 + ), + Item.new( # This Conjured item does not work properly yet + name => 'Conjured Mana Cake', + sell_in => 3, + quality => 6 + ), +); + +sub MAIN(Int $days = 2) { + my $gilded-rose = GildedRose.new( items => @items ); + for ^$days -> $day { + say "-------- day $day --------"; + say 'name, sellIn, quality'; + + .Str.say for $gilded-rose.items; + + "".say; + $gilded-rose.update_quality(); + } +} diff --git a/php/.gitignore b/php/.gitignore new file mode 100644 index 00000000..179636ca --- /dev/null +++ b/php/.gitignore @@ -0,0 +1,6 @@ +/.idea +/build +/vendor +/.editorconfig +/.phpunit.result.cache +/composer.lock diff --git a/php/README.md b/php/README.md new file mode 100644 index 00000000..7757ea12 --- /dev/null +++ b/php/README.md @@ -0,0 +1,148 @@ +# GildedRose Kata - PHP Version + +See the [top level readme](../README.md) for general information about this exercise. This is the PHP version of the +GildedRose Kata. + +## Installation + +The kata uses: + +- [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/GildedRose-Refactoring-Kata.git +``` + +or + +```shell script +git clone https://github.com/emilybache/GildedRose-Refactoring-Kata.git +``` + +Install all the dependencies using composer + +```shell script +cd ./GildedRose-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 two classes: + - `Item.php` - this class should not be changed + - `GildedRose.php` - this class needs to be refactored, and the new feature added +- `tests` - contains the tests + - `GildedRoseTest.php` - starter test. + - Tip: ApprovalTests has been included as a dev dependency, see the PHP version of + the [Theatrical Players Refactoring Kata](https://github.com/emilybache/Theatrical-Players-Refactoring-Kata/) + for an example +- `Fixture` + - `texttest_fixture.php` this could be used by an ApprovalTests, or run from the command line + +## Fixture + +To run the fixture from the php directory: + +```shell +php .\fixtures\texttest_fixture.php 10 +``` + +Change **10** to the required days. + +## Testing + +PHPUnit is configured for testing, a composer script has been provided. To run the unit tests, from the root of the PHP +project run: + +```shell script +composer tests +``` + +A Windows a batch file has been created, like an alias on Linux/Mac (e.g. `alias pu="composer tests"`), the same +PHPUnit `composer tests` 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 test-coverage report will be created in /builds, it is best viewed by opening /builds/**index.html** in your +browser. + +The [XDEbug](https://xdebug.org/download) extension is required for generating the coverage report. + +## Code Standard + +Easy Coding Standard (ECS) is configured for style and code standards, **PSR-12** is used. The current code is not upto +standard! + +### 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 +PHPUnit `composer check-cs` can be run: + +```shell script +cc.bat +``` + +### Fix Code + +ECS provides may code fixes, automatically, 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 +PHPUnit `composer fix-cs` can be run: + +```shell script +fc.bat +``` + +## Static Analysis + +PHPStan is used to run static analysis checks: + +```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 +PHPUnit `composer phpstan` can be run: + +```shell script +ps.bat +``` + +**Happy coding**! diff --git a/php/cc.bat b/php/cc.bat new file mode 100644 index 00000000..fa81b658 --- /dev/null +++ b/php/cc.bat @@ -0,0 +1 @@ +composer check-cs diff --git a/php/composer.json b/php/composer.json new file mode 100644 index 00000000..5c4459d8 --- /dev/null +++ b/php/composer.json @@ -0,0 +1,33 @@ +{ + "name": "emilybache/gilded-rose-refactoring-kata", + "description": "A kata to practice refactoring, tests and polymorphism", + "license": "MIT", + "require": { + "php": "^8.0" + }, + "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" + }, + "autoload": { + "psr-4": { + "GildedRose\\": "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 new file mode 100644 index 00000000..c023401b --- /dev/null +++ b/php/ecs.php @@ -0,0 +1,47 @@ +paths([ + __DIR__ . '/src', + __DIR__ . '/tests', + __DIR__ . '/ecs.php', // check this file too! + ]); + + $ecsConfig->skip([ + // rules to skip + ]); + + // 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 new file mode 100644 index 00000000..c571652e --- /dev/null +++ b/php/fc.bat @@ -0,0 +1 @@ +composer fix-cs diff --git a/php/fixtures/texttest_fixture.php b/php/fixtures/texttest_fixture.php new file mode 100644 index 00000000..4f8a5b78 --- /dev/null +++ b/php/fixtures/texttest_fixture.php @@ -0,0 +1,40 @@ + 1) { + $days = (int) $argv[1]; +} + +for ($i = 0; $i < $days; $i++) { + echo "-------- day {$i} --------" . PHP_EOL; + echo 'name, sellIn, quality' . PHP_EOL; + foreach ($items as $item) { + echo $item . PHP_EOL; + } + echo PHP_EOL; + $app->updateQuality(); +} diff --git a/php/phpstan.neon b/php/phpstan.neon new file mode 100644 index 00000000..95e29c1c --- /dev/null +++ b/php/phpstan.neon @@ -0,0 +1,16 @@ +includes: + - vendor/symplify/phpstan-extensions/config/config.neon + - vendor/phpstan/phpstan-phpunit/extension.neon + +parameters: + paths: + - src + - tests + - fixtures + + # max is the highest level + level: max + + checkGenericClassInNonGenericObjectType: false + + checkMissingIterableValueType: false diff --git a/php/phpunit.xml b/php/phpunit.xml new file mode 100644 index 00000000..03f801e1 --- /dev/null +++ b/php/phpunit.xml @@ -0,0 +1,13 @@ + + + + + ./src + + + + + ./tests + + + diff --git a/php/ps.bat b/php/ps.bat new file mode 100644 index 00000000..8bda091e --- /dev/null +++ b/php/ps.bat @@ -0,0 +1 @@ +composer phpstan diff --git a/php/pu.bat b/php/pu.bat new file mode 100644 index 00000000..38a0c5e9 --- /dev/null +++ b/php/pu.bat @@ -0,0 +1 @@ +composer tests diff --git a/php/src/GildedRose.php b/php/src/GildedRose.php new file mode 100644 index 00000000..db81ee3b --- /dev/null +++ b/php/src/GildedRose.php @@ -0,0 +1,67 @@ +items as $item) { + if ($item->name != 'Aged Brie' and $item->name != 'Backstage passes to a TAFKAL80ETC concert') { + if ($item->quality > 0) { + if ($item->name != 'Sulfuras, Hand of Ragnaros') { + $item->quality = $item->quality - 1; + } + } + } else { + if ($item->quality < 50) { + $item->quality = $item->quality + 1; + if ($item->name == 'Backstage passes to a TAFKAL80ETC concert') { + if ($item->sellIn < 11) { + if ($item->quality < 50) { + $item->quality = $item->quality + 1; + } + } + if ($item->sellIn < 6) { + if ($item->quality < 50) { + $item->quality = $item->quality + 1; + } + } + } + } + } + + if ($item->name != 'Sulfuras, Hand of Ragnaros') { + $item->sellIn = $item->sellIn - 1; + } + + if ($item->sellIn < 0) { + if ($item->name != 'Aged Brie') { + if ($item->name != 'Backstage passes to a TAFKAL80ETC concert') { + if ($item->quality > 0) { + if ($item->name != 'Sulfuras, Hand of Ragnaros') { + $item->quality = $item->quality - 1; + } + } + } else { + $item->quality = $item->quality - $item->quality; + } + } else { + if ($item->quality < 50) { + $item->quality = $item->quality + 1; + } + } + } + } + } +} diff --git a/php/src/Item.php b/php/src/Item.php new file mode 100644 index 00000000..732c41fc --- /dev/null +++ b/php/src/Item.php @@ -0,0 +1,20 @@ +name}, {$this->sellIn}, {$this->quality}"; + } +} diff --git a/php/tests/ApprovalTest.php b/php/tests/ApprovalTest.php new file mode 100644 index 00000000..ef906bc3 --- /dev/null +++ b/php/tests/ApprovalTest.php @@ -0,0 +1,44 @@ +"foo" is more similar to the unit test from the 'Java' version + *
  • "thirtyDays" is more similar to the TextTest from the 'Java' version + * + * I suggest choosing one style to develop and deleting the other. + */ +class ApprovalTest extends TestCase +{ + + public function testFoo(): void + { + $items = [new Item('foo', 0, 0)]; + $app = new GildedRose($items); + $app->updateQuality(); + + Approvals::verifyList($items); + } + + public function testThirtyDays(): void + { + ob_start(); + + $argv = ["", "30"]; + include(__DIR__ . '/../fixtures/texttest_fixture.php'); + + $output = ob_get_clean(); + + Approvals::verifyString($output); + } +} diff --git a/php/tests/GildedRoseTest.php b/php/tests/GildedRoseTest.php new file mode 100644 index 00000000..52e7ddc6 --- /dev/null +++ b/php/tests/GildedRoseTest.php @@ -0,0 +1,20 @@ +updateQuality(); + $this->assertSame('fixme', $items[0]->name); + } +} diff --git a/plantuml/gilded-rose.png b/plantuml/gilded-rose.png new file mode 100644 index 00000000..3f79be92 Binary files /dev/null and b/plantuml/gilded-rose.png differ diff --git a/plantuml/gilded-rose.puml b/plantuml/gilded-rose.puml new file mode 100644 index 00000000..1d36cb1d --- /dev/null +++ b/plantuml/gilded-rose.puml @@ -0,0 +1,72 @@ +@startuml + +skinparam backgroundColor transparent + +start +note left + Do this for each item in the list. + An item has a name, a sell in value and a quality value +end note + +if (not "Aged Brie" and not "Backstage passes to a TAFKAL80ETC concert") then (yes) + if (quality > 0) then (yes) + if (not "Sulfuras, Hand of Ragnaros") then (yes) + :lower quality with 1; + else (no) + endif + else (no) + endif +else (no) + if (quality < 50) then (yes) + :increase quality with 1; + if ("Backstage passes to a TAFKAL80ETC concert") then (yes) + if (sell in < 11) then (yes) + if (quality < 50) then (yes) + :increase quality with 1; + else (no) + endif + else (no) + endif + if (sell in < 6) then (yes) + if (quality < 50) then (yes) + :increase quality with 1; + else (no) + endif + else (no) + endif + else (no) + endif + else (no) + endif +endif + +if (not "Sulfuras, Hand of Ragnaros") then (yes) + :lower sell in with 1; +else (no) +endif + +if (sell in < 0) then (yes) + if (not "Aged Brie") then (yes) + if (not "Backstage passes to a TAFKAL80ETC concert") then (yes) + if (quality > 0) then (yes) + if (not "Sulfuras, Hand of Ragnaros") then (yes) + :lower quality with 1; + else (no) + endif + else (no) + endif + else (no) + :lower quality with quality; + endif + else (no) + if (quality < 50) then (yes) + :increase quality with 1; + else (no) + endif + endif +else (no) +endif + +stop + +@enduml diff --git a/plantuml/history/01_lower-both-values.puml b/plantuml/history/01_lower-both-values.puml new file mode 100644 index 00000000..8fa637bc --- /dev/null +++ b/plantuml/history/01_lower-both-values.puml @@ -0,0 +1,12 @@ +@startuml + +skinparam backgroundColor transparent + +start + +:lower quality with 1; +:lower sell in with 1; + +stop + +@enduml diff --git a/plantuml/history/02_sell-by-passed.puml b/plantuml/history/02_sell-by-passed.puml new file mode 100644 index 00000000..3b2c317d --- /dev/null +++ b/plantuml/history/02_sell-by-passed.puml @@ -0,0 +1,17 @@ +@startuml + +skinparam backgroundColor transparent + +start + +:lower quality with 1; +:lower sell in with 1; + +if (sell in < 0) then (yes) + :lower quality with 1; +else (no) +endif + +stop + +@enduml diff --git a/plantuml/history/03_quality-never-negative.puml b/plantuml/history/03_quality-never-negative.puml new file mode 100644 index 00000000..b2b9ed7e --- /dev/null +++ b/plantuml/history/03_quality-never-negative.puml @@ -0,0 +1,24 @@ +@startuml + +skinparam backgroundColor transparent + +start + +if (quality > 0) then (yes) + :lower quality with 1; +else (no) +endif + +:lower sell in with 1; + +if (sell in < 0) then (yes) + if (quality > 0) then (yes) + :lower quality with 1; + else (no) + endif +else (no) +endif + +stop + +@enduml diff --git a/plantuml/history/04_aged-brie.puml b/plantuml/history/04_aged-brie.puml new file mode 100644 index 00000000..7b74e874 --- /dev/null +++ b/plantuml/history/04_aged-brie.puml @@ -0,0 +1,32 @@ +@startuml + +skinparam backgroundColor transparent + +start + +if (not "Aged Brie") then (yes) + if (quality > 0) then (yes) + :lower quality with 1; + else (no) + endif +else (no) + :increase quality with 1; +endif + +:lower sell in with 1; + +if (sell in < 0) then (yes) + if (not "Aged Brie") then (yes) + if (quality > 0) then (yes) + :lower quality with 1; + else (no) + endif + else (no) + :increase quality with 1; + endif +else (no) +endif + +stop + +@enduml diff --git a/plantuml/history/05_quality-max-50.puml b/plantuml/history/05_quality-max-50.puml new file mode 100644 index 00000000..69cc86d0 --- /dev/null +++ b/plantuml/history/05_quality-max-50.puml @@ -0,0 +1,38 @@ +@startuml + +skinparam backgroundColor transparent + +start + +if (not "Aged Brie") then (yes) + if (quality > 0) then (yes) + :lower quality with 1; + else (no) + endif +else (no) + if (quality < 50) then (yes) + :increase quality with 1; + else (no) + endif +endif + +:lower sell in with 1; + +if (sell in < 0) then (yes) + if (not "Aged Brie") then (yes) + if (quality > 0) then (yes) + :lower quality with 1; + else (no) + endif + else (no) + if (quality < 50) then (yes) + :increase quality with 1; + else (no) + endif + endif + else (no) +endif + +stop + +@enduml diff --git a/plantuml/history/06_sulfuras.puml b/plantuml/history/06_sulfuras.puml new file mode 100644 index 00000000..25350c1d --- /dev/null +++ b/plantuml/history/06_sulfuras.puml @@ -0,0 +1,47 @@ +@startuml + +skinparam backgroundColor transparent + +start + +if (not "Aged Brie") then (yes) + if (quality > 0) then (yes) + if (not "Sulfuras, Hand of Ragnaros") then (yes) + :lower quality with 1; + else (no) + endif + else (no) + endif +else (no) + if (quality < 50) then (yes) + :increase quality with 1; + else (no) + endif +endif + +if (not "Sulfuras, Hand of Ragnaros") then (yes) + :lower sell in with 1; +else (no) +endif + +if (sell in < 0) then (yes) + if (not "Aged Brie") then (yes) + if (quality > 0) then (yes) + if (not "Sulfuras, Hand of Ragnaros") then (yes) + :lower quality with 1; + else (no) + endif + else (no) + endif + else (no) + if (quality < 50) then (yes) + :increase quality with 1; + else (no) + endif + endif +else (no) +endif + +stop + +@enduml diff --git a/plantuml/history/07_backstage-passes.puml b/plantuml/history/07_backstage-passes.puml new file mode 100644 index 00000000..5d6f2fa3 --- /dev/null +++ b/plantuml/history/07_backstage-passes.puml @@ -0,0 +1,68 @@ +@startuml + +skinparam backgroundColor transparent + +start + +if (not "Aged Brie" and not "Backstage passes to a TAFKAL80ETC concert") then (yes) + if (quality > 0) then (yes) + if (not "Sulfuras, Hand of Ragnaros") then (yes) + :lower quality with 1; + else (no) + endif + else (no) + endif +else (no) + if (quality < 50) then (yes) + :increase quality with 1; + if ("Backstage passes to a TAFKAL80ETC concert") then (yes) + if (sell in < 11) then (yes) + if (quality < 50) then (yes) + :increase quality with 1; + else (no) + endif + else (no) + endif + if (sell in < 6) then (yes) + if (quality < 50) then (yes) + :increase quality with 1; + else (no) + endif + else (no) + endif + else (no) + endif + else (no) + endif +endif + +if (not "Sulfuras, Hand of Ragnaros") then (yes) + :lower sell in with 1; +else (no) +endif + +if (sell in < 0) then (yes) + if (not "Aged Brie") then (yes) + if (not "Backstage passes to a TAFKAL80ETC concert") then (yes) + if (quality > 0) then (yes) + if (not "Sulfuras, Hand of Ragnaros") then (yes) + :lower quality with 1; + else (no) + endif + else (no) + endif + else (no) + :lower quality with quality; + endif + else (no) + if (quality < 50) then (yes) + :increase quality with 1; + else (no) + endif + endif +else (no) +endif + +stop + +@enduml diff --git a/plantuml/readme.md b/plantuml/readme.md new file mode 100644 index 00000000..9c577adb --- /dev/null +++ b/plantuml/readme.md @@ -0,0 +1,22 @@ +# PlantUML +This UML refactoring exercise is aimed at less technical people like analysts, testers and other non-programmers. The key takeaway message is clarifying the need for continuous refactoring to people who do not code on a daily basis. + +## Running and rendering +If you want to get started quickly, just copy the contents of `plantuml/gilded-rose.puml`, and paste it in https://www.planttext.com/ + +Several alternatives are available to render PlantUML, see https://plantuml.com/running. + +## Syntax +For the syntax of activity diagrams in PlantUML, please refer to https://plantuml.com/activity-diagram-beta + +## Workshop +You can run this exercise as a workshop. + +During this workshop, you start off with a presentation explaining the domain, while filling out the testcases found under `plantuml/workshop`. After that, participants are invited to refactor the UML Activity Diagram, using the examples that were discovered during the presentation. +[The google slides can be found here](https://docs.google.com/presentation/d/1kkRnVQjZELcfuGoFXaiSY_HxbO4lPQnSgHRGQZIH2nU/edit?usp=sharing). + +## History +If you want to illustrate how this activity diagram got to this state, you can show the requirements and the files under the /history directory. These individual files show the evolution of the activity diagram. + +## Original state +![The original activity diagram](./gilded-rose.png) diff --git a/plantuml/workshop-materials/testcases.pdf b/plantuml/workshop-materials/testcases.pdf new file mode 100644 index 00000000..be311e2d Binary files /dev/null and b/plantuml/workshop-materials/testcases.pdf differ diff --git a/plpgsql/Dockerfile b/plpgsql/Dockerfile new file mode 100644 index 00000000..d0f42b3b --- /dev/null +++ b/plpgsql/Dockerfile @@ -0,0 +1,45 @@ +FROM postgres:12.1 as base +WORKDIR /app + +ENV PGHOST=localhost +ENV PGDATABASE=kata +ENV PGPASSWORD=admin +ENV PGUSER=postgres +ENV POSTGRES_PASSWORD=admin +ENV PGDATA /var/lib/postgresql/data_local + +RUN mkdir -p "$PGDATA" && chown -R postgres:postgres "$PGDATA" && chmod 777 "$PGDATA" + +ADD ./*.sh /app/ +ADD ./src/item.sql /app/src/ +ADD ./src/new_item.sql /app/src/ + +# PGUNIT +FROM base as pgunit + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates wget \ + && rm -rf /var/lib/apt/lists/* + +ADD ./pgunit/initialize.sh /app/ +ADD ./pgunit/*.sql /app/ +RUN chmod +x ./*.sh \ + && ./initializeDocker.sh + +# PGTAP +FROM base as pgtap + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates build-essential git-core libv8-dev curl postgresql-server-dev-12 \ + && rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /tmp/pgtap \ + && cd /tmp/pgtap \ + && git clone https://github.com/theory/pgtap.git /tmp/pgtap \ + && make \ + && make install \ + && cpan TAP::Parser::SourceHandler::pgTAP + +ADD ./pgtap/initialize.sh /app/ +RUN chmod +x ./*.sh \ + && ./initializeDocker.sh diff --git a/plpgsql/README.md b/plpgsql/README.md new file mode 100644 index 00000000..93337289 --- /dev/null +++ b/plpgsql/README.md @@ -0,0 +1,63 @@ +# Requirements +You'll need: +- PostgreSQL database, version >= 11 because PROCEDURE keyword isn't supported before [version 11](https://www.postgresql.org/docs/11/release-11.html) +- OS user with local connection privilege to database on standard 5432 port + +To use remote / local dockerized database, add ``` --host --port --username``` parameters to plsql invocation. + +# Setup +## With docker +Run `docker-compose up -d ` to start, and `docker-compose exec bash` to enter in container. +`` is the testing framework name. Two values are possible: `pgunit` or `pgtap` + +## Without docker +In shell: +- create database: ```createdb kata``` +- create item table (structure): ```psql -d kata -f ./src/item.sql``` +- create procedure to help to add item: ```psql -d kata -f ./src/new_item.sql``` +- load code into database: ```psql -d kata -f ./src/update_quality.sql ``` + +If you get this message```LINE 1: CREATE OR REPLACE PROCEDURE public.update_quality()```, your PostgreSQL version may under 11, consider upgrading. + +# Run +In shell: +- connect to CLI: ```psql -d kata``` +- add item: ```CALL new_item('+5 Dexterity Vest', 10, 20);``` +- check item state: ```SELECT * FROM item;``` +- execute item update: ```CALL update_quality();``` +- check item state: ```SELECT * FROM item;``` +- empty table : ```TRUNCATE TABLE item;``` + +# Kata +`src/update_quality.sql` contains code to refactor. + +# Test + +## Using pgTAP +### Requirements +Install pgTAP [instructions here](https://pgtap.org/documentation.html#installation) +It's already installed with docker + +```item``` table is supposed to be empty. +If not, it would cause a (false positive)[https://en.wikipedia.org/wiki/False_positives_and_false_negatives] + +### Execute +Run `docker-compose up -d pgtap` to start, and `docker-compose exec pgtap bash` to enter in container. +In shell, execute ```psql -d kata -f src/update_quality.sql && pg_prove pgtap/test_*.sql```. +You should get ```pgtap/test_case_update_quality.sql .. ok All tests successful.``` + +If you get this message ```ERROR: function plan(integer) does not exist LINE 1: SELECT PLAN(1);```, pgTAP is not working => check your pgTAP installation. +If you get this message ```Failed test: 2 Parse errors: Bad plan. You planned 1 tests but ran 2.```, the item table contains data, interfering with the test => empty it, then run test again. + +`pgtap/test_case_update_quality.sql` contains test examples. + +## Using pgunit +### Requirement +Unit test framework used : pgunit (https://github.com/adrianandrei-ca/pgunit) +It's already installed with docker + +### Execute +Run `docker-compose up -d pgunit` to start, and `docker-compose exec pgunit bash` to enter in container. +You can run `cat src/update_quality.sql pgunit/run_tests.sql | psql -d kata -f -` + +`pgunit/run_tests.sql` contains test examples. diff --git a/plpgsql/docker-compose.yml b/plpgsql/docker-compose.yml new file mode 100644 index 00000000..b753b164 --- /dev/null +++ b/plpgsql/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.4' + +services: + pgunit: + # image: fpellet/gildedrose-refactoring-kata:pgunit + build: + context: . + target: pgunit + ports: + - "5432:5432" + volumes: + - .:/app/:z + + pgtap: + # image: fpellet/gildedrose-refactoring-kata:pgtap + build: + context: . + target: pgtap + ports: + - "5432:5432" + volumes: + - .:/app/:z diff --git a/plpgsql/initializeDatabase.sh b/plpgsql/initializeDatabase.sh new file mode 100644 index 00000000..09b6202a --- /dev/null +++ b/plpgsql/initializeDatabase.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -ex + +echo "Create database" +psql -d postgres -c 'DROP DATABASE IF EXISTS kata;' +psql -d postgres -c 'CREATE DATABASE kata;' + +./initialize.sh + +echo "Add current code" +psql -d kata -f src/item.sql +psql -d kata -f src/new_item.sql diff --git a/plpgsql/initializeDocker.sh b/plpgsql/initializeDocker.sh new file mode 100644 index 00000000..e962623a --- /dev/null +++ b/plpgsql/initializeDocker.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +wait_database() +{ + while : + do + (echo > /dev/tcp/127.0.0.1/5432) >/dev/null 2>&1 + result=$? + + if [[ $result -eq 0 ]]; then + break + fi + sleep 1 + done + return $result +} + +nohup docker-entrypoint.sh postgres > /dev/null 2>&1 & +wait_database + +set -ex + +./initializeDatabase.sh + +echo "Stop database" +disown %1 diff --git a/plpgsql/pgtap/initialize.sh b/plpgsql/pgtap/initialize.sh new file mode 100644 index 00000000..7646180d --- /dev/null +++ b/plpgsql/pgtap/initialize.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -ex + +echo "Enable extension" +psql -d kata -c 'CREATE EXTENSION IF NOT EXISTS pgtap;' diff --git a/plpgsql/pgtap/test_case_update_quality.sql b/plpgsql/pgtap/test_case_update_quality.sql new file mode 100644 index 00000000..5d06a167 --- /dev/null +++ b/plpgsql/pgtap/test_case_update_quality.sql @@ -0,0 +1,18 @@ +BEGIN; +-- Plan count should match the number of tests. If it does not then pg_prove will fail the test +SELECT plan(1); + +-- Run the tests. +-- Given +TRUNCATE TABLE item; +CALL new_item('foo', 0, 0); + +-- When +CALL update_quality(); + +-- Then +SELECT is( name, 'fixme', 'name did change' ) FROM item; + +-- Finish the tests and clean up. +SELECT * FROM finish(); +ROLLBACK; diff --git a/plpgsql/pgunit/asserts.sql b/plpgsql/pgunit/asserts.sql new file mode 100644 index 00000000..d51b5b6a --- /dev/null +++ b/plpgsql/pgunit/asserts.sql @@ -0,0 +1,59 @@ +CREATE OR REPLACE FUNCTION test_assertEquals(message TEXT, expected ANYELEMENT, result ANYELEMENT) RETURNS VOID AS $$ +BEGIN + IF expected = result THEN + null; + ELSE + raise exception '%: expect ''%'' instead of ''%''', message, expected, result using errcode = 'triggered_action_exception'; + END IF; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION test_assertEquals(expected ANYELEMENT, result ANYELEMENT) RETURNS VOID AS $$ +BEGIN + perform test_assertEquals('assertEquals failure', expected, result); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION test_assertEqualsArray(expected VARCHAR[], result VARCHAR[]) RETURNS VOID AS $$ +DECLARE + line RECORD; + error_message text; +BEGIN + IF expected = result THEN + null; + ELSE + error_message := 'assertEqualsArray failure:'; + FOR line IN SELECT expected_item, result_item FROM (SELECT unnest(expected) AS expected_item, unnest(result) AS result_item) x + LOOP + IF line.expected_item = line.result_item THEN + error_message := CONCAT(error_message, E'\n', '= ', line.expected_item); + ELSE + error_message := CONCAT(error_message, E'\n', '- ', line.expected_item); + error_message := CONCAT(error_message, E'\n', '+ ', line.result_item); + END IF; + END LOOP; + + raise exception '%', error_message using errcode = 'triggered_action_exception'; + END IF; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION test_assertEquals_golden_master(expected VARCHAR[], result VARCHAR[]) RETURNS VOID as $$ +DECLARE + golden TEXT; + line VARCHAR; +BEGIN + perform test_assertEqualsArray(expected, result); +EXCEPTION + WHEN triggered_action_exception THEN + golden := CONCAT(SQLERRM, E'\n\n', E'For update, copy:\n'); + golden := CONCAT(golden, E'expected := ARRAY['); + FOREACH line IN ARRAY result + LOOP + golden := CONCAT(golden, E'\n', '''', line, ''','); + END LOOP; + golden := CONCAT(golden, E'\n', '];'); + + raise exception '%', golden using errcode = 'triggered_action_exception'; +END; +$$ LANGUAGE plpgsql; diff --git a/plpgsql/pgunit/asserts_tests.sql b/plpgsql/pgunit/asserts_tests.sql new file mode 100644 index 00000000..05cd77f1 --- /dev/null +++ b/plpgsql/pgunit/asserts_tests.sql @@ -0,0 +1,95 @@ +CREATE OR REPLACE FUNCTION test_case_assertEquals_numeric_should_fail_if_not_equals() RETURNS VOID AS $$ +DECLARE + expected_message VARCHAR; + error_message VARCHAR; + is_equals BOOLEAN; +BEGIN + BEGIN + perform test_assertEquals(7, 5); + EXCEPTION + WHEN triggered_action_exception THEN + expected_message := 'assertEquals failure: expect ''7'' instead of ''5'''; + error_message := SQLERRM; + perform test_assertTrue(format('Expect message ''%s'' instead of ''%s'' ', expected_message, error_message), error_message = expected_message); + END; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION test_case_assertEquals_numeric_should_success_if_equals() RETURNS VOID AS $$ +BEGIN + perform test_assertEquals(7, 7); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION test_case_assertEquals_text_should_fail_if_not_equals() RETURNS VOID AS $$ +DECLARE + expected_message VARCHAR; + error_message VARCHAR; + is_equals BOOLEAN; +BEGIN + BEGIN + perform test_assertEquals('hello'::VARCHAR, 'olleh'); + EXCEPTION + WHEN triggered_action_exception THEN + expected_message := 'assertEquals failure: expect ''hello'' instead of ''olleh'''; + error_message := SQLERRM; + perform test_assertTrue(format('Expect message ''%s'' instead of ''%s'' ', expected_message, error_message), error_message = expected_message); + END; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION test_case_assertEquals_text_should_success_if_equals() RETURNS VOID AS $$ +BEGIN + perform test_assertEquals('hello'::VARCHAR, 'hello'); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION test_case_assertEquals_should_display_custom_message_if_defined() RETURNS VOID AS $$ +DECLARE + expected_message VARCHAR; + error_message VARCHAR; + is_equals BOOLEAN; +BEGIN + BEGIN + perform test_assertEquals('Test with custom message', 'hello'::VARCHAR, 'olleh'); + EXCEPTION + WHEN triggered_action_exception THEN + expected_message := 'Test with custom message: expect ''hello'' instead of ''olleh'''; + error_message := SQLERRM; + perform test_assertTrue(format('Expect message ''%s'' instead of ''%s'' ', expected_message, error_message), error_message = expected_message); + END; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION test_case_assertEqualsArray_should_success_if_equals() RETURNS VOID AS $$ +BEGIN + perform test_assertEqualsArray(ARRAY['1','2'], ARRAY['1','2']); + perform test_assertEqualsArray(ARRAY['a','b'], ARRAY['a','b']); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION test_case_assertEqualsArray_should_display_diff_if_failed() RETURNS VOID AS $$ +DECLARE + expected_message VARCHAR; + error_message VARCHAR; + is_equals BOOLEAN; +BEGIN + BEGIN + perform test_assertEqualsArray(ARRAY['1','2','3','4'], ARRAY['1','2','4','4']); + EXCEPTION + WHEN triggered_action_exception THEN + expected_message := CONCAT( + 'assertEqualsArray failure:', E'\n', + '= 1', E'\n', + '= 2', E'\n', + '- 3', E'\n', + '+ 4', E'\n', + '= 4' + ); + error_message := SQLERRM; + perform test_assertTrue(format('Expect message ''%s'' instead of ''%s'' ', expected_message, error_message), error_message = expected_message); + END; +END; +$$ LANGUAGE plpgsql; + +SELECT * FROM test_run_all(); diff --git a/plpgsql/pgunit/initialize.sh b/plpgsql/pgunit/initialize.sh new file mode 100644 index 00000000..60557019 --- /dev/null +++ b/plpgsql/pgunit/initialize.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -ex + +echo "Enable DBLINK" +psql -d kata -c 'CREATE EXTENSION DBLINK;' + +echo "Initialize test framework" +wget https://raw.githubusercontent.com/adrianandrei-ca/pgunit/bc69dfc526ec3db55ff72af5d78eab55661502af/PGUnit.sql \ + && psql -d kata -f PGUnit.sql \ + && rm PGUnit.sql + +echo "Initialize custom asserts" +psql -d kata -f asserts.sql diff --git a/plpgsql/pgunit/run_tests.sql b/plpgsql/pgunit/run_tests.sql new file mode 100644 index 00000000..3c4e3c68 --- /dev/null +++ b/plpgsql/pgunit/run_tests.sql @@ -0,0 +1,95 @@ +CREATE OR REPLACE FUNCTION test_case_update_quality() RETURNS void AS $$ +DECLARE + name_result item.name%TYPE; +BEGIN + TRUNCATE TABLE item; + CALL new_item('foo', 0, 0); + + CALL update_quality(); + + SELECT name FROM item INTO name_result; + perform test_assertEquals('name did change', 'fixme', name_result); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION format_day(day INTEGER) RETURNS TEXT[] AS $$ +DECLARE + result TEXT[]; + item_result RECORD; +BEGIN + result := ARRAY[CONCAT('-------- day ', day, ' --------')]; + result := result || 'name, sellIn, quality'::TEXT; + + FOR item_result IN (SELECT name, sell_in, quality FROM item ORDER BY name ASC, sell_in ASC, quality ASC) + LOOP + result := result || format('%s, %s, %s', item_result.name, item_result.sell_in, item_result.quality); + END LOOP; + + RETURN result; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION test_case_update_quality_golden_master() RETURNS VOID AS $$ +DECLARE + sell_in_result item.sell_in%TYPE; + quality_result item.quality%TYPE; + days INTEGER; + result TEXT[]; + expected TEXT[]; + item_result RECORD; +BEGIN + -- given + TRUNCATE TABLE item; + CALL new_item('+5 Dexterity Vest', 10, 20); + CALL new_item('Aged Brie', 2, 0); + CALL new_item('Elixir of the Mongoose', 5, 7); + CALL new_item('Sulfuras, Hand of Ragnaros', 0, 80); + CALL new_item('Sulfuras, Hand of Ragnaros', -1, 80); + CALL new_item('Backstage passes to a TAFKAL80ETC concert', 15, 20); + CALL new_item('Backstage passes to a TAFKAL80ETC concert', 10, 49); + CALL new_item('Backstage passes to a TAFKAL80ETC concert', 5, 49); + -- this conjured item does not work properly yet ; + CALL new_item('Conjured Mana Cake', 3, 6); + days := 1; + + -- when + result := format_day(0); + FOR current_day IN 1 .. days + LOOP + CALL update_quality(); + + result := result || format_day(current_day); + END LOOP; + + -- then + expected := ARRAY[ + '-------- day 0 --------', + 'name, sellIn, quality', + '+5 Dexterity Vest, 10, 20', + 'Aged Brie, 2, 0', + 'Backstage passes to a TAFKAL80ETC concert, 5, 49', + 'Backstage passes to a TAFKAL80ETC concert, 10, 49', + 'Backstage passes to a TAFKAL80ETC concert, 15, 20', + 'Conjured Mana Cake, 3, 6', + 'Elixir of the Mongoose, 5, 7', + 'Sulfuras, Hand of Ragnaros, -1, 80', + 'Sulfuras, Hand of Ragnaros, 0, 80', + '-------- day 1 --------', + 'name, sellIn, quality', + '+5 Dexterity Vest, 9, 19', + 'Aged Brie, 1, 1', + 'Backstage passes to a TAFKAL80ETC concert, 4, 50', + 'Backstage passes to a TAFKAL80ETC concert, 9, 50', + 'Backstage passes to a TAFKAL80ETC concert, 14, 21', + 'Conjured Mana Cake, 2, 5', + 'Elixir of the Mongoose, 4, 6', + 'Sulfuras, Hand of Ragnaros, -1, 80', + 'Sulfuras, Hand of Ragnaros, 0, 80' + ]; + + perform test_assertEquals_golden_master(expected, result); +END; +$$ LANGUAGE plpgsql; + + +SELECT * FROM test_run_all(); \ No newline at end of file diff --git a/plpgsql/src/item.sql b/plpgsql/src/item.sql new file mode 100644 index 00000000..1b59331e --- /dev/null +++ b/plpgsql/src/item.sql @@ -0,0 +1,8 @@ +DROP TABLE IF EXISTS item; +CREATE TABLE item + ( + name character varying(100) NOT NULL, + sell_in numeric(6) NOT NULL, + quality numeric(6) NOT NULL + ); + diff --git a/plpgsql/src/new_item.sql b/plpgsql/src/new_item.sql new file mode 100644 index 00000000..e78d712f --- /dev/null +++ b/plpgsql/src/new_item.sql @@ -0,0 +1,12 @@ +DROP PROCEDURE IF EXISTS new_item; +CREATE PROCEDURE new_item( + name item.name%TYPE, + sell_in item.sell_in%TYPE, + quality item.quality%TYPE + ) +LANGUAGE plpgsql +AS $$ +BEGIN + INSERT INTO item (name, sell_in, quality) VALUES (name, sell_in, quality); +END; +$$; diff --git a/plpgsql/src/update_quality.sql b/plpgsql/src/update_quality.sql new file mode 100644 index 00000000..e19d6863 --- /dev/null +++ b/plpgsql/src/update_quality.sql @@ -0,0 +1,72 @@ +CREATE OR REPLACE PROCEDURE update_quality() +LANGUAGE plpgsql +AS $$ +DECLARE + c_items CURSOR FOR + SELECT name, sell_in, quality FROM item FOR UPDATE; + l_item RECORD; + l_name item.name%TYPE; + l_sell_in item.sell_in%TYPE; + l_quality item.quality%TYPE; +BEGIN + OPEN c_items; + LOOP + FETCH c_items INTO l_item; + EXIT WHEN NOT FOUND; + + l_name := l_item.name; + l_sell_in := l_item.sell_in; + l_quality := l_item.quality; + + IF l_name <> 'Aged Brie' AND l_name <> 'Backstage passes to a TAFKAL80ETC concert' THEN + IF l_quality > 0 THEN + IF l_name <> 'Sulfuras, Hand of Ragnaros' THEN + l_quality := l_quality - 1; + END IF; + END IF; + ELSE + IF (l_quality < 50) THEN + l_quality := l_quality + 1; + IF l_name = 'Backstage passes to a TAFKAL80ETC concert' THEN + IF l_sell_in < 11 THEN + IF l_quality < 50 THEN + l_quality := l_quality + 1; + END IF; + END IF; + IF l_sell_in < 6 THEN + IF l_quality < 50 THEN + l_quality := l_quality + 1; + END IF; + END IF; + END IF; + END IF; + END IF; + + IF l_name <> 'Sulfuras, Hand of Ragnaros' THEN + l_sell_in := l_sell_in - 1; + END IF; + + IF l_sell_in < 0 THEN + IF l_name <> 'Aged Brie' THEN + IF l_name <> 'Backstage passes to a TAFKAL80ETC concert' THEN + IF l_quality > 0 THEN + IF l_name <> 'Sulfuras, Hand of Ragnaros' THEN + l_quality := l_quality - 1; + END IF; + END IF; + ELSE + l_quality := l_quality - l_quality; + END IF; + ELSE + IF l_quality < 50 THEN + l_quality := l_quality + 1; + END IF; + END IF; + END IF; + + UPDATE item + SET name = l_name, sell_in = l_sell_in, quality = l_quality WHERE CURRENT OF c_items; + END LOOP; + CLOSE c_items; +END; +$$; diff --git a/plsql/create_user_if_needed.sql b/plsql/create_user_if_needed.sql new file mode 100644 index 00000000..59078dae --- /dev/null +++ b/plsql/create_user_if_needed.sql @@ -0,0 +1,7 @@ +PROMPT Creating User 'DOJO' +DROP USER dojo; +CREATE USER dojo IDENTIFIED BY pass + DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE temp; + GRANT RESOURCE TO dojo; + GRANT CONNECT TO dojo; +/ diff --git a/plsql/item.sql b/plsql/item.sql new file mode 100644 index 00000000..97bd9e83 --- /dev/null +++ b/plsql/item.sql @@ -0,0 +1,20 @@ +PROMPT Creating Table 'ITEM' + +BEGIN + EXECUTE IMMEDIATE 'DROP TABLE item'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -942 THEN + RAISE; + END IF; +END; + +CREATE TABLE item + ( + name VARCHAR2(100) NOT NULL, + sell_in NUMBER(6) NOT NULL, + quality NUMBER(6) NOT NULL + ); +/ + +SHOW ERRORS; diff --git a/plsql/new_item.sql b/plsql/new_item.sql new file mode 100644 index 00000000..63c46fe7 --- /dev/null +++ b/plsql/new_item.sql @@ -0,0 +1,9 @@ +CREATE OR REPLACE PROCEDURE new_item( + i_name item.name%TYPE, + i_sell_in item.sell_in%TYPE, + i_quality item.quality%TYPE) +IS +BEGIN + INSERT INTO item (name, sell_in, quality) VALUES (i_name, i_sell_in, i_quality); +END new_item; +/ diff --git a/plsql/run_tests.sql b/plsql/run_tests.sql new file mode 100644 index 00000000..a36d29c4 --- /dev/null +++ b/plsql/run_tests.sql @@ -0,0 +1,5 @@ +exec DBMS_SESSION.RESET_PACKAGE; +set serveroutput on; +exec DBMS_OUTPUT.ENABLE(1000000); + +exec ut.run(USER||':gilded_rose_tests'||''); \ No newline at end of file diff --git a/plsql/texttest.pkb b/plsql/texttest.pkb new file mode 100644 index 00000000..cbd2e0e3 --- /dev/null +++ b/plsql/texttest.pkb @@ -0,0 +1,78 @@ +CREATE OR REPLACE PACKAGE BODY texttest IS + co_lf CONSTANT VARCHAR2(1) := CHR(10); + + PROCEDURE put_line(p_buffer IN OUT NOCOPY VARCHAR2, p_line VARCHAR2) IS + BEGIN + p_buffer := p_buffer || p_line || co_lf; + END put_line; + + PROCEDURE setup IS + BEGIN + DELETE FROM ITEM; + + new_item('+5 Dexterity Vest', 10, 20); + new_item('Aged Brie', 2, 0); + new_item('Elixir of the Mongoose', 5, 7); + new_item('Sulfuras, Hand of Ragnaros', 0, 80); + new_item('Sulfuras, Hand of Ragnaros', -1, 80); + new_item('Backstage passes to a TAFKAL80ETC concert', 15, 20); + new_item('Backstage passes to a TAFKAL80ETC concert', 10, 49); + new_item('Backstage passes to a TAFKAL80ETC concert', 5, 49); + -- this conjured item does not work properly yet ; + new_item('Conjured Mana Cake', 3, 6); + END setup; + + PROCEDURE main_test IS + v_result VARCHAR2(4000) := ''; + + v_expected VARCHAR2(4000) := ''; + + l_days NUMBER(3); + + CURSOR c_items IS SELECT name, sell_in, quality FROM item; + + l_item c_items%ROWTYPE; + BEGIN + put_line(v_expected, 'OMGHAI!'); + put_line(v_expected, '-------- day 0 --------'); + put_line(v_expected, 'name, sellIn, quality'); + put_line(v_expected, '+5 Dexterity Vest, 10, 20' || co_lf || 'Aged Brie, 2, 0'); + put_line(v_expected, 'Elixir of the Mongoose, 5, 7'); + put_line(v_expected, 'Sulfuras, Hand of Ragnaros, 0, 80'); + put_line(v_expected, 'Sulfuras, Hand of Ragnaros, -1, 80'); + put_line(v_expected, 'Backstage passes to a TAFKAL80ETC concert, 15, 20'); + put_line(v_expected, 'Backstage passes to a TAFKAL80ETC concert, 10, 49'); + put_line(v_expected, 'Backstage passes to a TAFKAL80ETC concert, 5, 49'); + put_line(v_expected, 'Conjured Mana Cake, 3, 6'); + put_line(v_expected, '-------- day 1 --------'); + put_line(v_expected, 'name, sellIn, quality'); + put_line(v_expected, '+5 Dexterity Vest, 9, 19'); + put_line(v_expected, 'Aged Brie, 1, 1'); + put_line(v_expected, 'Elixir of the Mongoose, 4, 6'); + put_line(v_expected, 'Sulfuras, Hand of Ragnaros, 0, 80'); + put_line(v_expected, 'Sulfuras, Hand of Ragnaros, -1, 80'); + put_line(v_expected, 'Backstage passes to a TAFKAL80ETC concert, 14, 21'); + put_line(v_expected, 'Backstage passes to a TAFKAL80ETC concert, 9, 50'); + put_line(v_expected, 'Backstage passes to a TAFKAL80ETC concert, 4, 50'); + put_line(v_expected, 'Conjured Mana Cake, 2, 5'); + + put_line(v_result, 'OMGHAI!'); + l_days := 2; + + FOR i IN 0 .. l_days - 1 + LOOP + put_line(v_result, '-------- day ' || TO_CHAR(i) || ' --------'); + put_line(v_result, 'name, sellIn, quality'); + + FOR l_item IN c_items + LOOP + put_line(v_result, l_item.name || ', ' || l_item.sell_in || ', ' || l_item.quality); + END LOOP; + + update_quality(); + END LOOP; + + ut.expect(v_result).to_equal(v_expected); + END; +END texttest; +/ \ No newline at end of file diff --git a/plsql/texttest.pks b/plsql/texttest.pks new file mode 100644 index 00000000..7f65f2d9 --- /dev/null +++ b/plsql/texttest.pks @@ -0,0 +1,12 @@ +CREATE OR REPLACE PACKAGE texttest IS + -- %suite(texttest) + -- %suitepath(gilded_rose_tests) + -- %rollback(manual) + + -- %beforeall + PROCEDURE setup; + + -- %test(main test) + PROCEDURE main_test; +END texttest; +/ \ No newline at end of file diff --git a/plsql/update_quality.sql b/plsql/update_quality.sql new file mode 100644 index 00000000..372b6bdb --- /dev/null +++ b/plsql/update_quality.sql @@ -0,0 +1,66 @@ +CREATE OR REPLACE PROCEDURE update_quality +IS + CURSOR c_items IS + SELECT name, sell_in, quality FROM item FOR UPDATE; + l_item c_items%ROWTYPE; + l_name item.name%TYPE; + l_sell_in item.sell_in%TYPE; + l_quality item.quality%TYPE; +BEGIN + FOR l_item IN c_items + LOOP + l_name := l_item.name; + l_sell_in := l_item.sell_in; + l_quality := l_item.quality; + + IF l_name <> 'Aged Brie' AND l_name <> 'Backstage passes to a TAFKAL80ETC concert' THEN + IF l_quality > 0 THEN + IF l_name <> 'Sulfuras, Hand of Ragnaros' THEN + l_quality := l_quality - 1; + END IF; + END IF; + ELSE + IF (l_quality < 50) THEN + l_quality := l_quality + 1; + IF l_name = 'Backstage passes to a TAFKAL80ETC concert' THEN + IF l_sell_in < 11 THEN + IF l_quality < 50 THEN + l_quality := l_quality + 1; + END IF; + END IF; + IF l_sell_in < 6 THEN + IF l_quality < 50 THEN + l_quality := l_quality + 1; + END IF; + END IF; + END IF; + END IF; + END IF; + + IF l_name <> 'Sulfuras, Hand of Ragnaros' THEN + l_sell_in := l_sell_in - 1; + END IF; + + IF l_sell_in < 0 THEN + IF l_name <> 'Aged Brie' THEN + IF l_name <> 'Backstage passes to a TAFKAL80ETC concert' THEN + IF l_quality > 0 THEN + IF l_name <> 'Sulfuras, Hand of Ragnaros' THEN + l_quality := l_quality - 1; + END IF; + END IF; + ELSE + l_quality := l_quality - l_quality; + END IF; + ELSE + IF l_quality < 50 THEN + l_quality := l_quality + 1; + END IF; + END IF; + END IF; + + UPDATE item + SET name = l_name, sell_in = l_sell_in, quality = l_quality WHERE CURRENT OF c_items; + END LOOP; +END update_quality; +/ diff --git a/plsql/ut_update_quality.pkb b/plsql/ut_update_quality.pkb new file mode 100644 index 00000000..95e4b2e3 --- /dev/null +++ b/plsql/ut_update_quality.pkb @@ -0,0 +1,19 @@ +CREATE OR REPLACE PACKAGE BODY ut_update_quality IS + PROCEDURE cleanup_before_each IS + BEGIN + DELETE FROM item; + END; + + PROCEDURE ut_foo IS + l_name item.name%TYPE; + BEGIN + new_item('foo', 0, 0); + + update_quality(); + + SELECT name INTO l_name FROM item; + + ut.expect(l_name, a_message => 'name did change').to_equal('fixme'); + END ut_foo; +END ut_update_quality; +/ \ No newline at end of file diff --git a/plsql/ut_update_quality.pks b/plsql/ut_update_quality.pks new file mode 100644 index 00000000..167210c3 --- /dev/null +++ b/plsql/ut_update_quality.pks @@ -0,0 +1,13 @@ +CREATE OR REPLACE PACKAGE ut_update_quality +IS + -- %suite(UT_REGRESSION_TEST) + -- %suitepath(gilded_rose_tests) + -- %rollback(manual) + + -- %beforeeach + PROCEDURE cleanup_before_each; + + -- %test(Foo test) + PROCEDURE ut_foo; +END ut_update_quality; +/ diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 00000000..8a0ce6bc --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,5 @@ +*.pyc +.cache +.coverage +.idea/ +*.iml diff --git a/python/README.md b/python/README.md new file mode 100644 index 00000000..7504b97c --- /dev/null +++ b/python/README.md @@ -0,0 +1,29 @@ +# Gilded Rose starting position in Python + +For exercise instructions see [top level README](../README.md) + +Suggestion: create a python virtual environment for this project. See the [documentation](https://docs.python.org/3/library/venv.html) + +## Run the unit tests from the Command-Line + +``` +python test_gilded_rose.py +``` + +## Run the TextTest fixture from the Command-Line + +For e.g. 10 days: + +``` +python texttest_fixture.py 10 +``` + +You should make sure the command shown above works when you execute it in a terminal before trying to use TextTest (see below). + + +## Run the TextTest approval test that comes with this project + +There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. You will need to specify the Python executable and interpreter in [config.gr](../texttests/config.gr). Uncomment these lines: + + executable:${TEXTTEST_HOME}/python/texttest_fixture.py + interpreter:python diff --git a/python/gilded_rose.py b/python/gilded_rose.py new file mode 100755 index 00000000..4f21ea64 --- /dev/null +++ b/python/gilded_rose.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +class GildedRose(object): + + def __init__(self, items): + self.items = items + + def update_quality(self): + for item in self.items: + if item.name != "Aged Brie" and item.name != "Backstage passes to a TAFKAL80ETC concert": + if item.quality > 0: + if item.name != "Sulfuras, Hand of Ragnaros": + item.quality = item.quality - 1 + else: + if item.quality < 50: + item.quality = item.quality + 1 + if item.name == "Backstage passes to a TAFKAL80ETC concert": + if item.sell_in < 11: + if item.quality < 50: + item.quality = item.quality + 1 + if item.sell_in < 6: + if item.quality < 50: + item.quality = item.quality + 1 + if item.name != "Sulfuras, Hand of Ragnaros": + item.sell_in = item.sell_in - 1 + if item.sell_in < 0: + if item.name != "Aged Brie": + if item.name != "Backstage passes to a TAFKAL80ETC concert": + if item.quality > 0: + if item.name != "Sulfuras, Hand of Ragnaros": + item.quality = item.quality - 1 + else: + item.quality = item.quality - item.quality + else: + if item.quality < 50: + item.quality = item.quality + 1 + + +class Item: + def __init__(self, name, sell_in, quality): + self.name = name + self.sell_in = sell_in + self.quality = quality + + def __repr__(self): + return "%s, %s, %s" % (self.name, self.sell_in, self.quality) diff --git a/python/requirements.txt b/python/requirements.txt new file mode 100644 index 00000000..feb57930 --- /dev/null +++ b/python/requirements.txt @@ -0,0 +1,4 @@ +pytest +approvaltests +pytest-approvaltests +coverage diff --git a/python/test_gilded_rose.py b/python/test_gilded_rose.py new file mode 100644 index 00000000..1c92f638 --- /dev/null +++ b/python/test_gilded_rose.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +import unittest + +from gilded_rose import Item, GildedRose + + +class GildedRoseTest(unittest.TestCase): + def test_foo(self): + items = [Item("foo", 0, 0)] + gilded_rose = GildedRose(items) + gilded_rose.update_quality() + self.assertEqual("fixme", items[0].name) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/texttest_fixture.py b/python/texttest_fixture.py new file mode 100644 index 00000000..86af5ef7 --- /dev/null +++ b/python/texttest_fixture.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import print_function + +from gilded_rose import * + +if __name__ == "__main__": + print ("OMGHAI!") + items = [ + Item(name="+5 Dexterity Vest", sell_in=10, quality=20), + Item(name="Aged Brie", sell_in=2, quality=0), + Item(name="Elixir of the Mongoose", sell_in=5, quality=7), + Item(name="Sulfuras, Hand of Ragnaros", sell_in=0, quality=80), + Item(name="Sulfuras, Hand of Ragnaros", sell_in=-1, quality=80), + Item(name="Backstage passes to a TAFKAL80ETC concert", sell_in=15, quality=20), + Item(name="Backstage passes to a TAFKAL80ETC concert", sell_in=10, quality=49), + Item(name="Backstage passes to a TAFKAL80ETC concert", sell_in=5, quality=49), + Item(name="Conjured Mana Cake", sell_in=3, quality=6), # <-- :O + ] + + days = 2 + import sys + if len(sys.argv) > 1: + days = int(sys.argv[1]) + 1 + for day in range(days): + print("-------- day %s --------" % day) + print("name, sellIn, quality") + for item in items: + print(item) + print("") + GildedRose(items).update_quality() diff --git a/rescript/.gitignore b/rescript/.gitignore new file mode 100644 index 00000000..1dd11f93 --- /dev/null +++ b/rescript/.gitignore @@ -0,0 +1,27 @@ +*.exe +*.obj +*.out +*.compile +*.native +*.byte +*.cmo +*.annot +*.cmi +*.cmx +*.cmt +*.cmti +*.cma +*.a +*.cmxa +*.obj +*~ +*.annot +*.cmj +*.bak +lib/bs +*.mlast +*.mliast +.vscode +.merlin +.bsb.lock +/node_modules/ diff --git a/rescript/README.md b/rescript/README.md new file mode 100644 index 00000000..fb537a76 --- /dev/null +++ b/rescript/README.md @@ -0,0 +1,35 @@ +# Gilded Rose + +This is the Gilded Rose kata in Rescript with Jest + +## Getting started + +Install dependencies + +```sh +yarn install +``` + +## Running tests + +To run all tests + +```sh +yarn test +``` + +To run all tests in watch mode + +```sh +yarn test:watch +``` + +To start developing +```sh +yarn dev:rescript +``` + +To run the TextTest file +```sh +yarn texttest [days] +``` diff --git a/rescript/bsconfig.json b/rescript/bsconfig.json new file mode 100644 index 00000000..ba1378cf --- /dev/null +++ b/rescript/bsconfig.json @@ -0,0 +1,14 @@ +{ + "name": "rescript", + "version": "0.1.0", + "sources": { + "dir" : "src", + "subdirs" : true + }, + "package-specs": { + "module": "commonjs", + "in-source": true + }, + "suffix": ".bs.js", + "bs-dependencies": ["@glennsl/rescript-jest"] +} diff --git a/rescript/jest.config.js b/rescript/jest.config.js new file mode 100644 index 00000000..ba36cead --- /dev/null +++ b/rescript/jest.config.js @@ -0,0 +1,14 @@ +module.exports = { + moduleFileExtensions: [ + "js", + "mjs", + ], + testMatch: [ + "**/src/**/*_test.mjs", + "**/src/**/*_test.bs.js", + ], + transform: { + "^.+\\.jsx?$": "esbuild-jest" + }, + "transformIgnorePatterns": ["/node_modules/(?!(rescript|@glennsl/rescript-jest)/)"] +} diff --git a/rescript/package.json b/rescript/package.json new file mode 100644 index 00000000..4cf3ec97 --- /dev/null +++ b/rescript/package.json @@ -0,0 +1,22 @@ +{ + "devDependencies": { + "@babel/core": "^7.22.15", + "@babel/preset-env": "^7.22.15", + "@glennsl/rescript-jest": "^0.10.0", + "babel-jest": "^29.6.4", + "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", + "esbuild": "^0.19.2", + "esbuild-jest": "^0.5.0", + "jest": "^29.6.4", + "rescript": "^10.1.4" + }, + "scripts": { + "test": "jest", + "test:watch": "jest --watchAll", + "dev:rescript": "rescript build -w", + "texttest": "node src/TextTest.bs.js" + }, + "name": "rescript", + "version": "1.0.0", + "license": "MIT" +} diff --git a/rescript/src/GildedRose.bs.js b/rescript/src/GildedRose.bs.js new file mode 100644 index 00000000..4c43fdc7 --- /dev/null +++ b/rescript/src/GildedRose.bs.js @@ -0,0 +1,102 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE +'use strict'; + + +function make(name, sellIn, quality) { + return { + name: name, + sellIn: sellIn, + quality: quality + }; +} + +var Item = { + make: make +}; + +function updateQuality(items) { + return items.map(function (item) { + var newItem = item; + if (newItem.name !== "Aged Brie" && newItem.name !== "Backstage passes to a TAFKAL80ETC concert") { + if (newItem.quality > 0 && newItem.name !== "Sulfuras, Hand of Ragnaros") { + var init = newItem; + newItem = { + name: init.name, + sellIn: init.sellIn, + quality: newItem.quality - 1 | 0 + }; + } + + } else if (newItem.quality < 50) { + var init$1 = newItem; + newItem = { + name: init$1.name, + sellIn: init$1.sellIn, + quality: newItem.quality + 1 | 0 + }; + if (newItem.name === "Backstage passes to a TAFKAL80ETC concert") { + if (newItem.sellIn < 11 && newItem.quality < 50) { + var init$2 = newItem; + newItem = { + name: init$2.name, + sellIn: init$2.sellIn, + quality: newItem.quality + 1 | 0 + }; + } + if (newItem.sellIn < 6 && newItem.quality < 50) { + var init$3 = newItem; + newItem = { + name: init$3.name, + sellIn: init$3.sellIn, + quality: newItem.quality + 1 | 0 + }; + } + + } + + } + if (newItem.name !== "Sulfuras, Hand of Ragnaros") { + var init$4 = newItem; + newItem = { + name: init$4.name, + sellIn: newItem.sellIn - 1 | 0, + quality: init$4.quality + }; + } + if (newItem.sellIn < 0) { + if (newItem.name !== "Aged Brie") { + if (newItem.name !== "Backstage passes to a TAFKAL80ETC concert") { + if (newItem.quality > 0 && newItem.name !== "Sulfuras, Hand of Ragnaros") { + var init$5 = newItem; + newItem = { + name: init$5.name, + sellIn: init$5.sellIn, + quality: newItem.quality - 1 | 0 + }; + } + + } else { + var init$6 = newItem; + newItem = { + name: init$6.name, + sellIn: init$6.sellIn, + quality: newItem.quality - newItem.quality | 0 + }; + } + } else if (newItem.quality < 50) { + var init$7 = newItem; + newItem = { + name: init$7.name, + sellIn: init$7.sellIn, + quality: newItem.quality + 1 | 0 + }; + } + + } + return newItem; + }); +} + +exports.Item = Item; +exports.updateQuality = updateQuality; +/* No side effect */ diff --git a/rescript/src/GildedRose.res b/rescript/src/GildedRose.res new file mode 100644 index 00000000..87fe52ba --- /dev/null +++ b/rescript/src/GildedRose.res @@ -0,0 +1,71 @@ +module Item = { + type t = { + name: string, + sellIn: int, + quality: int, + } + + let make = (~name, ~sellIn, ~quality): t => { + name, + sellIn, + quality, + } +} + +let updateQuality = (items: array) => { + items->Js.Array2.map(item => { + let newItem = ref(item) + + if ( + newItem.contents.name != "Aged Brie" && + newItem.contents.name != "Backstage passes to a TAFKAL80ETC concert" + ) { + if newItem.contents.quality > 0 { + if newItem.contents.name != "Sulfuras, Hand of Ragnaros" { + newItem := {...newItem.contents, quality: newItem.contents.quality - 1} + } + } + } else if newItem.contents.quality < 50 { + newItem := {...newItem.contents, quality: newItem.contents.quality + 1} + + if newItem.contents.name == "Backstage passes to a TAFKAL80ETC concert" { + if newItem.contents.sellIn < 11 { + if newItem.contents.quality < 50 { + newItem := {...newItem.contents, quality: newItem.contents.quality + 1} + } + } + + if newItem.contents.sellIn < 6 { + if newItem.contents.quality < 50 { + newItem := {...newItem.contents, quality: newItem.contents.quality + 1} + } + } + } + } + + if newItem.contents.name != "Sulfuras, Hand of Ragnaros" { + newItem := {...newItem.contents, sellIn: newItem.contents.sellIn - 1} + } + + if newItem.contents.sellIn < 0 { + if newItem.contents.name != "Aged Brie" { + if newItem.contents.name != "Backstage passes to a TAFKAL80ETC concert" { + if newItem.contents.quality > 0 { + if newItem.contents.name != "Sulfuras, Hand of Ragnaros" { + newItem := {...newItem.contents, quality: newItem.contents.quality - 1} + } + } + } else { + newItem := { + ...newItem.contents, + quality: newItem.contents.quality - newItem.contents.quality, + } + } + } else if newItem.contents.quality < 50 { + newItem := {...newItem.contents, quality: newItem.contents.quality + 1} + } + } + + newItem.contents + }) +} diff --git a/rescript/src/GildedRose_test.bs.js b/rescript/src/GildedRose_test.bs.js new file mode 100644 index 00000000..522b02ac --- /dev/null +++ b/rescript/src/GildedRose_test.bs.js @@ -0,0 +1,20 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE +'use strict'; + +var Jest = require("@glennsl/rescript-jest/src/jest.bs.js"); +var Caml_array = require("rescript/lib/js/caml_array.js"); +var GildedRose = require("./GildedRose.bs.js"); + +Jest.describe("Gilded Rose", (function (param) { + Jest.test("should foo", (function (param) { + var items = [{ + name: "foo", + sellIn: 0, + quality: 0 + }]; + var updatedItems = GildedRose.updateQuality(items); + return Jest.Expect.toBe(Jest.Expect.expect(Caml_array.get(updatedItems, 0).name), "fixme"); + })); + })); + +/* Not a pure module */ diff --git a/rescript/src/GildedRose_test.res b/rescript/src/GildedRose_test.res new file mode 100644 index 00000000..6cf7c024 --- /dev/null +++ b/rescript/src/GildedRose_test.res @@ -0,0 +1,11 @@ +open Jest +open Expect +open GildedRose + +describe("Gilded Rose", () => { + test("should foo", () => { + let items: array = [{name: "foo", sellIn: 0, quality: 0}] + let updatedItems = updateQuality(items) + expect(updatedItems[0].name)->toBe("fixme") + }) +}) diff --git a/rescript/src/TextTest.bs.js b/rescript/src/TextTest.bs.js new file mode 100644 index 00000000..1f393649 --- /dev/null +++ b/rescript/src/TextTest.bs.js @@ -0,0 +1,42 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE +'use strict'; + +var Process = require("process"); +var Belt_Array = require("rescript/lib/js/belt_Array.js"); +var Caml_array = require("rescript/lib/js/caml_array.js"); +var GildedRose = require("./GildedRose.bs.js"); +var Belt_Option = require("rescript/lib/js/belt_Option.js"); +var Caml_format = require("rescript/lib/js/caml_format.js"); + +console.log("OMGHAI!"); + +var items = { + contents: [ + GildedRose.Item.make("+5 Dexterity Vest", 10, 20), + GildedRose.Item.make("Aged Brie", 2, 0), + GildedRose.Item.make("Elixir of the Mongoose", 5, 7), + GildedRose.Item.make("Sulfuras, Hand of Ragnaros", 0, 80), + GildedRose.Item.make("Sulfuras, Hand of Ragnaros", -1, 80), + GildedRose.Item.make("Backstage passes to a TAFKAL80ETC concert", 15, 20), + GildedRose.Item.make("Backstage passes to a TAFKAL80ETC concert", 10, 49), + GildedRose.Item.make("Backstage passes to a TAFKAL80ETC concert", 5, 49), + GildedRose.Item.make("Conjured Mana Cake", 3, 6) + ] +}; + +var days = Belt_Option.mapWithDefault(Belt_Array.get(Process.argv, 2), 31, Caml_format.int_of_string); + +for(var i = 0; i <= days; ++i){ + console.log("-------- day " + String(i) + " --------"); + console.log("name, sellIn, quality"); + for(var j = 0 ,j_finish = items.contents.length; j < j_finish; ++j){ + var item = Caml_array.get(items.contents, j); + console.log(item.name + ", " + String(item.sellIn) + ", " + String(item.quality)); + } + console.log(""); + items.contents = GildedRose.updateQuality(items.contents); +} + +exports.items = items; +exports.days = days; +/* Not a pure module */ diff --git a/rescript/src/TextTest.res b/rescript/src/TextTest.res new file mode 100644 index 00000000..b8f208cf --- /dev/null +++ b/rescript/src/TextTest.res @@ -0,0 +1,28 @@ +open GildedRose + +Js.log("OMGHAI!") + +let items: ref> = ref([ + Item.make(~name="+5 Dexterity Vest", ~sellIn=10, ~quality=20), + Item.make(~name="Aged Brie", ~sellIn=2, ~quality=0), + Item.make(~name="Elixir of the Mongoose", ~sellIn=5, ~quality=7), + Item.make(~name="Sulfuras, Hand of Ragnaros", ~sellIn=0, ~quality=80), + Item.make(~name="Sulfuras, Hand of Ragnaros", ~sellIn=-1, ~quality=80), + Item.make(~name="Backstage passes to a TAFKAL80ETC concert", ~sellIn=15, ~quality=20), + Item.make(~name="Backstage passes to a TAFKAL80ETC concert", ~sellIn=10, ~quality=49), + Item.make(~name="Backstage passes to a TAFKAL80ETC concert", ~sellIn=5, ~quality=49), + Item.make(~name="Conjured Mana Cake", ~sellIn=3, ~quality=6), +]) + +let days = Node.Process.argv->Belt.Array.get(2)->Belt.Option.mapWithDefault(31, int_of_string) + +for i in 0 to days { + Js.log("-------- day " ++ string_of_int(i) ++ " --------") + Js.log("name, sellIn, quality") + for j in 0 to Js.Array2.length(items.contents) - 1 { + let item = items.contents[j] + Js.log(item.name ++ ", " ++ string_of_int(item.sellIn) ++ ", " ++ string_of_int(item.quality)) + } + Js.log("") + items := updateQuality(items.contents) +} diff --git a/rescript/yarn.lock b/rescript/yarn.lock new file mode 100644 index 00000000..b4c5b864 --- /dev/null +++ b/rescript/yarn.lock @@ -0,0 +1,5268 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" + integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== + +"@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.17", "@babel/core@^7.12.3", "@babel/core@^7.22.15", "@babel/core@^7.7.2", "@babel/core@^7.8.0": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.15.tgz#15d4fd03f478a459015a4b94cfbb3bd42c48d2f4" + integrity sha512-PtZqMmgRrvj8ruoEOIwVA3yoF91O+Hgw9o7DAUTNBA6Mo2jpu31clx9a7Nz/9JznqetTR6zwfC4L3LAjKQXUwA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.22.15" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-module-transforms" "^7.22.15" + "@babel/helpers" "^7.22.15" + "@babel/parser" "^7.22.15" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.22.15" + "@babel/types" "^7.22.15" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.22.15", "@babel/generator@^7.7.2": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.15.tgz#1564189c7ec94cb8f77b5e8a90c4d200d21b2339" + integrity sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA== + dependencies: + "@babel/types" "^7.22.15" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" + integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" + integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.15" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.22.11", "@babel/helper-create-class-features-plugin@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz#97a61b385e57fe458496fad19f8e63b63c867de4" + integrity sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.15" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" + integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + regexpu-core "^5.3.1" + semver "^6.3.1" + +"@babel/helper-define-polyfill-provider@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz#82c825cadeeeee7aad237618ebbe8fa1710015d7" + integrity sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + +"@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.15.tgz#b95a144896f6d491ca7863576f820f3628818621" + integrity sha512-qLNsZbgrNh0fDQBCPocSL8guki1hcPvltGDv/NxvUoABwFq7GkKSu1nRXeJkVZc+wJvne2E0RKQz+2SQrz6eAA== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.22.15", "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.15.tgz#40ad2f6950f143900e9c1c72363c0b431a606082" + integrity sha512-l1UiX4UyHSFsYt17iQ3Se5pQQZZHa22zyIXURmvkmLCD4t/aU+dvNWHatKac/D9Vm9UES7nvIqHs4jZqKviUmQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@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.15" + +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-remap-async-to-generator@^7.22.5", "@babel/helper-remap-async-to-generator@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz#53a25b7484e722d7efb9c350c75c032d4628de82" + integrity sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-wrap-function" "^7.22.9" + +"@babel/helper-replace-supers@^7.22.5", "@babel/helper-replace-supers@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz#cbdc27d6d8d18cd22c81ae4293765a5d9afd0779" + integrity sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.22.15", "@babel/helper-validator-identifier@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz#601fa28e4cc06786c18912dca138cec73b882044" + integrity sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ== + +"@babel/helper-validator-option@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" + integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== + +"@babel/helper-wrap-function@^7.22.9": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.10.tgz#d845e043880ed0b8c18bd194a12005cb16d2f614" + integrity sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ== + dependencies: + "@babel/helper-function-name" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.10" + +"@babel/helpers@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.15.tgz#f09c3df31e86e3ea0b7ff7556d85cdebd47ea6f1" + integrity sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/highlight@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.13.tgz#9cda839e5d3be9ca9e8c26b6dd69e7548f0cbf16" + integrity sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.15.tgz#d34592bfe288a32e741aa0663dbc4829fcd55160" + integrity sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz#02dc8a03f613ed5fdc29fb2f728397c78146c962" + integrity sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz#2aeb91d337d4e1a1e7ce85b76a37f5301781200f" + integrity sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.15" + +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-import-assertions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" + integrity sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-attributes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz#ab840248d834410b829f569f5262b9e517555ecb" + integrity sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" + integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz#aac8d383b062c5072c647a31ef990c1d0af90272" + integrity sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-arrow-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" + integrity sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-async-generator-functions@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz#3b153af4a6b779f340d5b80d3f634f55820aefa3" + integrity sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-transform-async-to-generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz#c7a85f44e46f8952f6d27fe57c2ed3cc084c3775" + integrity sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ== + dependencies: + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.5" + +"@babel/plugin-transform-block-scoped-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" + integrity sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-block-scoping@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.15.tgz#494eb82b87b5f8b1d8f6f28ea74078ec0a10a841" + integrity sha512-G1czpdJBZCtngoK1sJgloLiOHUnkb/bLZwqVZD8kXmq0ZnVfTTWUcs9OWtp0mBtYJ+4LQY1fllqBkOIPhXmFmw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz#97a56e31ad8c9dc06a0b3710ce7803d5a48cca77" + integrity sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-static-block@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz#dc8cc6e498f55692ac6b4b89e56d87cec766c974" + integrity sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.11" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-transform-classes@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz#aaf4753aee262a232bbc95451b4bdf9599c65a0b" + integrity sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" + "@babel/helper-split-export-declaration" "^7.22.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" + integrity sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.5" + +"@babel/plugin-transform-destructuring@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.15.tgz#e7404ea5bb3387073b9754be654eecb578324694" + integrity sha512-HzG8sFl1ZVGTme74Nw+X01XsUTqERVQ6/RLHo3XjGRzm7XD6QTtfS3NJotVgCGy8BzkDqRjRBD8dAyJn5TuvSQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dotall-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz#dbb4f0e45766eb544e193fb00e65a1dd3b2a4165" + integrity sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-duplicate-keys@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz#b6e6428d9416f5f0bba19c70d1e6e7e0b88ab285" + integrity sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dynamic-import@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz#2c7722d2a5c01839eaf31518c6ff96d408e447aa" + integrity sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz#402432ad544a1f9a480da865fda26be653e48f6a" + integrity sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-export-namespace-from@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz#b3c84c8f19880b6c7440108f8929caf6056db26c" + integrity sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-transform-for-of@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz#f64b4ccc3a4f131a996388fae7680b472b306b29" + integrity sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" + integrity sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg== + dependencies: + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-json-strings@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz#689a34e1eed1928a40954e37f74509f48af67835" + integrity sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-transform-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" + integrity sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-logical-assignment-operators@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz#24c522a61688bde045b7d9bc3c2597a4d948fc9c" + integrity sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-transform-member-expression-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" + integrity sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-modules-amd@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz#4e045f55dcf98afd00f85691a68fc0780704f526" + integrity sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ== + dependencies: + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-modules-commonjs@^7.12.13", "@babel/plugin-transform-modules-commonjs@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.15.tgz#b11810117ed4ee7691b29bd29fd9f3f98276034f" + integrity sha512-jWL4eh90w0HQOTKP2MoXXUpVxilxsB2Vl4ji69rSjS3EcZ/v4sBmn+A3NpepuJzBhOaEBbR7udonlHHn5DWidg== + dependencies: + "@babel/helper-module-transforms" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + +"@babel/plugin-transform-modules-systemjs@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.11.tgz#3386be5875d316493b517207e8f1931d93154bb1" + integrity sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA== + dependencies: + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + +"@babel/plugin-transform-modules-umd@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz#4694ae40a87b1745e3775b6a7fe96400315d4f98" + integrity sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ== + dependencies: + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-new-target@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz#1b248acea54ce44ea06dfd37247ba089fcf9758d" + integrity sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz#debef6c8ba795f5ac67cd861a81b744c5d38d9fc" + integrity sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-transform-numeric-separator@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz#498d77dc45a6c6db74bb829c02a01c1d719cbfbd" + integrity sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-transform-object-rest-spread@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz#21a95db166be59b91cde48775310c0df6e1da56f" + integrity sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.22.15" + +"@babel/plugin-transform-object-super@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" + integrity sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + +"@babel/plugin-transform-optional-catch-binding@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz#461cc4f578a127bb055527b3e77404cad38c08e0" + integrity sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-transform-optional-chaining@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.15.tgz#d7a5996c2f7ca4ad2ad16dbb74444e5c4385b1ba" + integrity sha512-ngQ2tBhq5vvSJw2Q2Z9i7ealNkpDMU0rGWnHPKqRZO0tzZ5tlaoz4hDvhXioOoaE0X2vfNss1djwg0DXlfu30A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz#719ca82a01d177af358df64a514d64c2e3edb114" + integrity sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-methods@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz#21c8af791f76674420a147ae62e9935d790f8722" + integrity sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-property-in-object@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz#ad45c4fc440e9cb84c718ed0906d96cf40f9a4e1" + integrity sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.11" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-transform-property-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" + integrity sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-regenerator@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz#8ceef3bd7375c4db7652878b0241b2be5d0c3cca" + integrity sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + regenerator-transform "^0.15.2" + +"@babel/plugin-transform-reserved-words@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz#832cd35b81c287c4bcd09ce03e22199641f964fb" + integrity sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-shorthand-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" + integrity sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-spread@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" + integrity sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + +"@babel/plugin-transform-sticky-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz#295aba1595bfc8197abd02eae5fc288c0deb26aa" + integrity sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-template-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" + integrity sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-typeof-symbol@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz#5e2ba478da4b603af8673ff7c54f75a97b716b34" + integrity sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-escapes@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz#c723f380f40a2b2f57a62df24c9005834c8616d9" + integrity sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-property-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz#098898f74d5c1e86660dc112057b2d11227f1c81" + integrity sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz#ce7e7bb3ef208c4ff67e02a22816656256d7a183" + integrity sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-sets-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz#77788060e511b708ffc7d42fdfbc5b37c3004e91" + integrity sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/preset-env@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.15.tgz#142716f8e00bc030dae5b2ac6a46fbd8b3e18ff8" + integrity sha512-tZFHr54GBkHk6hQuVA8w4Fmq+MSPsfvMG0vPnOYyTnJpyfMqybL8/MbNCPRT9zc2KBO2pe4tq15g6Uno4Jpoag== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.15" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.15" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.22.5" + "@babel/plugin-syntax-import-attributes" "^7.22.5" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@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-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.22.5" + "@babel/plugin-transform-async-generator-functions" "^7.22.15" + "@babel/plugin-transform-async-to-generator" "^7.22.5" + "@babel/plugin-transform-block-scoped-functions" "^7.22.5" + "@babel/plugin-transform-block-scoping" "^7.22.15" + "@babel/plugin-transform-class-properties" "^7.22.5" + "@babel/plugin-transform-class-static-block" "^7.22.11" + "@babel/plugin-transform-classes" "^7.22.15" + "@babel/plugin-transform-computed-properties" "^7.22.5" + "@babel/plugin-transform-destructuring" "^7.22.15" + "@babel/plugin-transform-dotall-regex" "^7.22.5" + "@babel/plugin-transform-duplicate-keys" "^7.22.5" + "@babel/plugin-transform-dynamic-import" "^7.22.11" + "@babel/plugin-transform-exponentiation-operator" "^7.22.5" + "@babel/plugin-transform-export-namespace-from" "^7.22.11" + "@babel/plugin-transform-for-of" "^7.22.15" + "@babel/plugin-transform-function-name" "^7.22.5" + "@babel/plugin-transform-json-strings" "^7.22.11" + "@babel/plugin-transform-literals" "^7.22.5" + "@babel/plugin-transform-logical-assignment-operators" "^7.22.11" + "@babel/plugin-transform-member-expression-literals" "^7.22.5" + "@babel/plugin-transform-modules-amd" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.15" + "@babel/plugin-transform-modules-systemjs" "^7.22.11" + "@babel/plugin-transform-modules-umd" "^7.22.5" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" + "@babel/plugin-transform-new-target" "^7.22.5" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.11" + "@babel/plugin-transform-numeric-separator" "^7.22.11" + "@babel/plugin-transform-object-rest-spread" "^7.22.15" + "@babel/plugin-transform-object-super" "^7.22.5" + "@babel/plugin-transform-optional-catch-binding" "^7.22.11" + "@babel/plugin-transform-optional-chaining" "^7.22.15" + "@babel/plugin-transform-parameters" "^7.22.15" + "@babel/plugin-transform-private-methods" "^7.22.5" + "@babel/plugin-transform-private-property-in-object" "^7.22.11" + "@babel/plugin-transform-property-literals" "^7.22.5" + "@babel/plugin-transform-regenerator" "^7.22.10" + "@babel/plugin-transform-reserved-words" "^7.22.5" + "@babel/plugin-transform-shorthand-properties" "^7.22.5" + "@babel/plugin-transform-spread" "^7.22.5" + "@babel/plugin-transform-sticky-regex" "^7.22.5" + "@babel/plugin-transform-template-literals" "^7.22.5" + "@babel/plugin-transform-typeof-symbol" "^7.22.5" + "@babel/plugin-transform-unicode-escapes" "^7.22.10" + "@babel/plugin-transform-unicode-property-regex" "^7.22.5" + "@babel/plugin-transform-unicode-regex" "^7.22.5" + "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" + "@babel/preset-modules" "0.1.6-no-external-plugins" + "@babel/types" "^7.22.15" + babel-plugin-polyfill-corejs2 "^0.4.5" + babel-plugin-polyfill-corejs3 "^0.8.3" + babel-plugin-polyfill-regenerator "^0.5.2" + core-js-compat "^3.31.0" + semver "^6.3.1" + +"@babel/preset-modules@0.1.6-no-external-plugins": + version "0.1.6-no-external-plugins" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + +"@babel/runtime@^7.8.4": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8" + integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/template@^7.22.15", "@babel/template@^7.22.5", "@babel/template@^7.3.3": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.22.15", "@babel/traverse@^7.7.2": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.15.tgz#75be4d2d6e216e880e93017f4e2389aeb77ef2d9" + integrity sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.22.15" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.15.tgz#266cb21d2c5fd0b3931e7a91b6dd72d2f617d282" + integrity sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.15" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@esbuild/android-arm64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz#bc35990f412a749e948b792825eef7df0ce0e073" + integrity sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw== + +"@esbuild/android-arm@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.2.tgz#edd1c8f23ba353c197f5b0337123c58ff2a56999" + integrity sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q== + +"@esbuild/android-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.2.tgz#2dcdd6e6f1f2d82ea1b746abd8da5b284960f35a" + integrity sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w== + +"@esbuild/darwin-arm64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz#55b36bc06d76f5c243987c1f93a11a80d8fc3b26" + integrity sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA== + +"@esbuild/darwin-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz#982524af33a6424a3b5cb44bbd52559623ad719c" + integrity sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw== + +"@esbuild/freebsd-arm64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz#8e478a0856645265fe79eac4b31b52193011ee06" + integrity sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ== + +"@esbuild/freebsd-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz#01b96604f2540db023c73809bb8ae6cd1692d6f3" + integrity sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw== + +"@esbuild/linux-arm64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz#7e5d2c7864c5c83ec789b59c77cd9c20d2594916" + integrity sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg== + +"@esbuild/linux-arm@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz#c32ae97bc0246664a1cfbdb4a98e7b006d7db8ae" + integrity sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg== + +"@esbuild/linux-ia32@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz#3fc4f0fa026057fe885e4a180b3956e704f1ceaa" + integrity sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ== + +"@esbuild/linux-loong64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz#633bcaea443f3505fb0ed109ab840c99ad3451a4" + integrity sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw== + +"@esbuild/linux-mips64el@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz#e0bff2898c46f52be7d4dbbcca8b887890805823" + integrity sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg== + +"@esbuild/linux-ppc64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz#d75798da391f54a9674f8c143b9a52d1dbfbfdde" + integrity sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw== + +"@esbuild/linux-riscv64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz#012409bd489ed1bb9b775541d4a46c5ded8e6dd8" + integrity sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw== + +"@esbuild/linux-s390x@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz#ece3ed75c5a150de8a5c110f02e97d315761626b" + integrity sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g== + +"@esbuild/linux-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz#dea187019741602d57aaf189a80abba261fbd2aa" + integrity sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ== + +"@esbuild/netbsd-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz#bbfd7cf9ab236a23ee3a41b26f0628c57623d92a" + integrity sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ== + +"@esbuild/openbsd-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz#fa5c4c6ee52a360618f00053652e2902e1d7b4a7" + integrity sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw== + +"@esbuild/sunos-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz#52a2ac8ac6284c02d25df22bb4cfde26fbddd68d" + integrity sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw== + +"@esbuild/win32-arm64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz#719ed5870855de8537aef8149694a97d03486804" + integrity sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg== + +"@esbuild/win32-ia32@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz#24832223880b0f581962c8660f8fb8797a1e046a" + integrity sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA== + +"@esbuild/win32-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz#1205014625790c7ff0e471644a878a65d1e34ab0" + integrity sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw== + +"@glennsl/rescript-jest@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@glennsl/rescript-jest/-/rescript-jest-0.10.0.tgz#8b8ddb768ac65a1d9792bd0a9243a55999625678" + integrity sha512-rUhCuyNi8Em0N7ubrmdz2ZEXo95SvSxkPPVIHpj/UcxAayxr5uo/hoBs0XRVjpAbuCXcoHtvhWt314lcAhwZ8g== + dependencies: + jest "^27.3.1" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + 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" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" + integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + +"@jest/console@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.6.4.tgz#a7e2d84516301f986bba0dd55af9d5fe37f46527" + integrity sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.6.3" + jest-util "^29.6.3" + slash "^3.0.0" + +"@jest/core@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" + integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== + dependencies: + "@jest/console" "^27.5.1" + "@jest/reporters" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.8.1" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^27.5.1" + jest-config "^27.5.1" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-resolve-dependencies "^27.5.1" + jest-runner "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + jest-watcher "^27.5.1" + micromatch "^4.0.4" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/core@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.6.4.tgz#265ebee05ec1ff3567757e7a327155c8d6bdb126" + integrity sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg== + dependencies: + "@jest/console" "^29.6.4" + "@jest/reporters" "^29.6.4" + "@jest/test-result" "^29.6.4" + "@jest/transform" "^29.6.4" + "@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.6.3" + jest-config "^29.6.4" + jest-haste-map "^29.6.4" + jest-message-util "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.6.4" + jest-resolve-dependencies "^29.6.4" + jest-runner "^29.6.4" + jest-runtime "^29.6.4" + jest-snapshot "^29.6.4" + jest-util "^29.6.3" + jest-validate "^29.6.3" + jest-watcher "^29.6.4" + micromatch "^4.0.4" + pretty-format "^29.6.3" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" + integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== + dependencies: + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + +"@jest/environment@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.6.4.tgz#78ec2c9f8c8829a37616934ff4fea0c028c79f4f" + integrity sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ== + dependencies: + "@jest/fake-timers" "^29.6.4" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.6.3" + +"@jest/expect-utils@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.4.tgz#17c7dfe6cec106441f218b0aff4b295f98346679" + integrity sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg== + dependencies: + jest-get-type "^29.6.3" + +"@jest/expect@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.6.4.tgz#1d6ae17dc68d906776198389427ab7ce6179dba6" + integrity sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA== + dependencies: + expect "^29.6.4" + jest-snapshot "^29.6.4" + +"@jest/fake-timers@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" + integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== + dependencies: + "@jest/types" "^27.5.1" + "@sinonjs/fake-timers" "^8.0.1" + "@types/node" "*" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-util "^27.5.1" + +"@jest/fake-timers@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.6.4.tgz#45a27f093c43d5d989362a3e7a8c70c83188b4f6" + integrity sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw== + dependencies: + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.6.3" + jest-mock "^29.6.3" + jest-util "^29.6.3" + +"@jest/globals@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" + integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/types" "^27.5.1" + expect "^27.5.1" + +"@jest/globals@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.6.4.tgz#4f04f58731b062b44ef23036b79bdb31f40c7f63" + integrity sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA== + dependencies: + "@jest/environment" "^29.6.4" + "@jest/expect" "^29.6.4" + "@jest/types" "^29.6.3" + jest-mock "^29.6.3" + +"@jest/reporters@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" + integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-haste-map "^27.5.1" + jest-resolve "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^8.1.0" + +"@jest/reporters@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.6.4.tgz#9d6350c8a2761ece91f7946e97ab0dabc06deab7" + integrity sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.6.4" + "@jest/test-result" "^29.6.4" + "@jest/transform" "^29.6.4" + "@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.6.3" + jest-util "^29.6.3" + jest-worker "^29.6.4" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" + integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.9" + source-map "^0.6.0" + +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + +"@jest/test-result@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" + integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== + dependencies: + "@jest/console" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-result@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.6.4.tgz#adf5c79f6e1fb7405ad13d67d9e2b6ff54b54c6b" + integrity sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ== + dependencies: + "@jest/console" "^29.6.4" + "@jest/types" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" + integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== + dependencies: + "@jest/test-result" "^27.5.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-runtime "^27.5.1" + +"@jest/test-sequencer@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz#86aef66aaa22b181307ed06c26c82802fb836d7b" + integrity sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg== + dependencies: + "@jest/test-result" "^29.6.4" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.4" + slash "^3.0.0" + +"@jest/transform@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" + integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^26.6.2" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-regex-util "^26.0.0" + jest-util "^26.6.2" + micromatch "^4.0.2" + pirates "^4.0.1" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/transform@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" + integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^27.5.1" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-regex-util "^27.5.1" + jest-util "^27.5.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/transform@^29.6.4": + version "29.6.4" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.6.4.tgz#a6bc799ef597c5d85b2e65a11fd96b6b239bab5a" + integrity sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA== + 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.6.4" + jest-regex-util "^29.6.3" + jest-util "^29.6.3" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" + integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + 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" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sinonjs/commons@^1.7.0": + version "1.8.6" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" + integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + +"@sinonjs/fake-timers@^8.0.1": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" + integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.7": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.1.tgz#916ecea274b0c776fec721e333e55762d3a9614b" + integrity sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" + integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.1" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" + integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.1.tgz#dd6f1d2411ae677dcb2db008c962598be31d6acf" + integrity sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg== + dependencies: + "@babel/types" "^7.20.7" + +"@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/node@*": + version "20.5.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.9.tgz#a70ec9d8fa0180a314c3ede0e20ea56ff71aed9a" + integrity sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ== + +"@types/prettier@^2.1.5": + version "2.7.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" + integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== + +"@types/stack-utils@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + +"@types/yargs-parser@*": + version "21.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + +"@types/yargs@^15.0.0": + version "15.0.15" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.15.tgz#e609a2b1ef9e05d90489c2f5f45bbfb2be092158" + integrity sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^16.0.0": + version "16.0.5" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3" + integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^17.0.8": + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== + dependencies: + "@types/yargs-parser" "*" + +abab@^2.0.3, abab@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.2.4: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@^3.0.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g== + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-jest@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" + integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== + dependencies: + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/babel__core" "^7.1.7" + babel-plugin-istanbul "^6.0.0" + babel-preset-jest "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + slash "^3.0.0" + +babel-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" + integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== + dependencies: + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-jest@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.6.4.tgz#98dbc45d1c93319c82a8ab4a478b670655dd2585" + integrity sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw== + dependencies: + "@jest/transform" "^29.6.4" + "@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" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w== + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-istanbul@^6.0.0, babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + 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" + +babel-plugin-jest-hoist@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" + integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-plugin-jest-hoist@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" + integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + +babel-plugin-polyfill-corejs2@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz#8097b4cb4af5b64a1d11332b6fb72ef5e64a054c" + integrity sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg== + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.4.2" + semver "^6.3.1" + +babel-plugin-polyfill-corejs3@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz#b4f719d0ad9bb8e0c23e3e630c0c8ec6dd7a1c52" + integrity sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.2" + core-js-compat "^3.31.0" + +babel-plugin-polyfill-regenerator@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz#80d0f3e1098c080c8b5a65f41e9427af692dc326" + integrity sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.2" + +babel-plugin-transform-es2015-modules-commonjs@^6.26.2: + version "6.26.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" + integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q== + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + integrity sha512-j3KtSpjyLSJxNoCDrhwiJad8kw0gJ9REGj8/CqL0HeRyLnvUNYV9zcqluL6QJSXh3nfsLEmSLvwRfGzrgR96Pw== + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + 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" + +babel-preset-jest@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" + integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== + dependencies: + babel-plugin-jest-hoist "^26.6.2" + babel-preset-current-node-syntax "^1.0.0" + +babel-preset-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" + integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== + dependencies: + babel-plugin-jest-hoist "^27.5.1" + babel-preset-current-node-syntax "^1.0.0" + +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== + dependencies: + babel-plugin-jest-hoist "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + +babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g== + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + integrity sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg== + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA== + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g== + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browserslist@^4.21.10, browserslist@^4.21.9: + version "4.21.10" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" + integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== + dependencies: + caniuse-lite "^1.0.30001517" + electron-to-chromium "^1.4.477" + node-releases "^2.0.13" + update-browserslist-db "^1.0.11" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001517: + version "1.0.30001527" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz#813826554828245ccee776c850566dce12bdeaba" + integrity sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +ci-info@^3.2.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + +cjs-module-lexer@^1.0.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collect-v8-coverage@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== + +core-js-compat@^3.31.0: + version "3.32.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.32.1.tgz#55f9a7d297c0761a8eb1d31b593e0f5b6ffae964" + integrity sha512-GSvKDv4wE0bPnQtjklV101juQ85g6H3rm5PDP20mqlS5j0kXF3pP97YvAu5hl+uFHqMictp3b2VxOHljWMAtuA== + dependencies: + browserslist "^4.21.10" + +core-js@^2.4.0: + version "2.6.12" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" + integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== + +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + +debug@4, debug@^4.1.0, debug@^4.1.1: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +decimal.js@^10.2.1: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + +decode-uri-component@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== + +dedent@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" + integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + +electron-to-chromium@^1.4.477: + version "1.4.508" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz#5641ff2f5ba11df4bd960fe6a2f9f70aa8b9af96" + integrity sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg== + +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +esbuild-jest@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/esbuild-jest/-/esbuild-jest-0.5.0.tgz#7a9964bfdecafca3b675a8aeb08193bcdba8b9d7" + integrity sha512-AMZZCdEpXfNVOIDvURlqYyHwC8qC1/BFjgsrOiSL1eyiIArVtHL8YAC83Shhn16cYYoAWEW17yZn0W/RJKJKHQ== + dependencies: + "@babel/core" "^7.12.17" + "@babel/plugin-transform-modules-commonjs" "^7.12.13" + babel-jest "^26.6.3" + +esbuild@^0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.2.tgz#b1541828a89dfb6f840d38538767c6130dca2aac" + integrity sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg== + optionalDependencies: + "@esbuild/android-arm" "0.19.2" + "@esbuild/android-arm64" "0.19.2" + "@esbuild/android-x64" "0.19.2" + "@esbuild/darwin-arm64" "0.19.2" + "@esbuild/darwin-x64" "0.19.2" + "@esbuild/freebsd-arm64" "0.19.2" + "@esbuild/freebsd-x64" "0.19.2" + "@esbuild/linux-arm" "0.19.2" + "@esbuild/linux-arm64" "0.19.2" + "@esbuild/linux-ia32" "0.19.2" + "@esbuild/linux-loong64" "0.19.2" + "@esbuild/linux-mips64el" "0.19.2" + "@esbuild/linux-ppc64" "0.19.2" + "@esbuild/linux-riscv64" "0.19.2" + "@esbuild/linux-s390x" "0.19.2" + "@esbuild/linux-x64" "0.19.2" + "@esbuild/netbsd-x64" "0.19.2" + "@esbuild/openbsd-x64" "0.19.2" + "@esbuild/sunos-x64" "0.19.2" + "@esbuild/win32-arm64" "0.19.2" + "@esbuild/win32-ia32" "0.19.2" + "@esbuild/win32-x64" "0.19.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escodegen@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +exec-sh@^0.3.2: + version "0.3.6" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + 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" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" + integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== + dependencies: + "@jest/types" "^27.5.1" + jest-get-type "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + +expect@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.4.tgz#a6e6f66d4613717859b2fe3da98a739437b6f4b8" + integrity sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA== + dependencies: + "@jest/expect-utils" "^29.6.4" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.6.4" + jest-message-util "^29.6.3" + jest-util "^29.6.3" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== + dependencies: + map-cache "^0.2.2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@^2.1.2, fsevents@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== + +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + 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" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== + +graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A== + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg== + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +isarray@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + 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" + +istanbul-lib-instrument@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz#7a8af094cbfff1d5bb280f62ce043695ae8dd5b8" + integrity sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + +istanbul-lib-report@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" + integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" + integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== + dependencies: + "@jest/types" "^27.5.1" + execa "^5.0.0" + throat "^6.0.1" + +jest-changed-files@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.6.3.tgz#97cfdc93f74fb8af2a1acb0b78f836f1fb40c449" + integrity sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg== + dependencies: + execa "^5.0.0" + jest-util "^29.6.3" + p-limit "^3.1.0" + +jest-circus@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" + integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" + +jest-circus@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.6.4.tgz#f074c8d795e0cc0f2ebf0705086b1be6a9a8722f" + integrity sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw== + dependencies: + "@jest/environment" "^29.6.4" + "@jest/expect" "^29.6.4" + "@jest/test-result" "^29.6.4" + "@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.6.3" + jest-matcher-utils "^29.6.4" + jest-message-util "^29.6.3" + jest-runtime "^29.6.4" + jest-snapshot "^29.6.4" + jest-util "^29.6.3" + p-limit "^3.1.0" + pretty-format "^29.6.3" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" + integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== + dependencies: + "@jest/core" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + prompts "^2.0.1" + yargs "^16.2.0" + +jest-cli@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.6.4.tgz#ad52f2dfa1b0291de7ec7f8d7c81ac435521ede0" + integrity sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ== + dependencies: + "@jest/core" "^29.6.4" + "@jest/test-result" "^29.6.4" + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^29.6.4" + jest-util "^29.6.3" + jest-validate "^29.6.3" + prompts "^2.0.1" + yargs "^17.3.1" + +jest-config@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" + integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== + dependencies: + "@babel/core" "^7.8.0" + "@jest/test-sequencer" "^27.5.1" + "@jest/types" "^27.5.1" + babel-jest "^27.5.1" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.9" + jest-circus "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-get-type "^27.5.1" + jest-jasmine2 "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runner "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^27.5.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-config@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.6.4.tgz#eff958ee41d4e1ee7a6106d02b74ad9fc427d79e" + integrity sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.6.4" + "@jest/types" "^29.6.3" + babel-jest "^29.6.4" + 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.6.4" + jest-environment-node "^29.6.4" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.6.4" + jest-runner "^29.6.4" + jest-util "^29.6.3" + jest-validate "^29.6.3" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.6.3" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-diff@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.4.tgz#85aaa6c92a79ae8cd9a54ebae8d5b6d9a513314a" + integrity sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.6.3" + +jest-docblock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" + integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== + dependencies: + detect-newline "^3.0.0" + +jest-docblock@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.6.3.tgz#293dca5188846c9f7c0c2b1bb33e5b11f21645f2" + integrity sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ== + dependencies: + detect-newline "^3.0.0" + +jest-each@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" + integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== + dependencies: + "@jest/types" "^27.5.1" + chalk "^4.0.0" + jest-get-type "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + +jest-each@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.6.3.tgz#1956f14f5f0cb8ae0b2e7cabc10bb03ec817c142" + integrity sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + jest-get-type "^29.6.3" + jest-util "^29.6.3" + pretty-format "^29.6.3" + +jest-environment-jsdom@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" + integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + jsdom "^16.6.0" + +jest-environment-node@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" + integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + +jest-environment-node@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.6.4.tgz#4ce311549afd815d3cafb49e60a1e4b25f06d29f" + integrity sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ== + dependencies: + "@jest/environment" "^29.6.4" + "@jest/fake-timers" "^29.6.4" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.6.3" + jest-util "^29.6.3" + +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-haste-map@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== + dependencies: + "@jest/types" "^26.6.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^26.0.0" + jest-serializer "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + micromatch "^4.0.2" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.1.2" + +jest-haste-map@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" + integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== + dependencies: + "@jest/types" "^27.5.1" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^27.5.1" + jest-serializer "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + micromatch "^4.0.4" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" + +jest-haste-map@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.6.4.tgz#97143ce833829157ea7025204b08f9ace609b96a" + integrity sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog== + 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.6.3" + jest-worker "^29.6.4" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-jasmine2@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" + integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + throat "^6.0.1" + +jest-leak-detector@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" + integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== + dependencies: + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-leak-detector@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz#b9661bc3aec8874e59aff361fa0c6d7cd507ea01" + integrity sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q== + dependencies: + jest-get-type "^29.6.3" + pretty-format "^29.6.3" + +jest-matcher-utils@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== + dependencies: + chalk "^4.0.0" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-matcher-utils@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz#327db7ababea49455df3b23e5d6109fe0c709d24" + integrity sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ== + dependencies: + chalk "^4.0.0" + jest-diff "^29.6.4" + jest-get-type "^29.6.3" + pretty-format "^29.6.3" + +jest-message-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" + integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.5.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-message-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.3.tgz#bce16050d86801b165f20cfde34dc01d3cf85fbf" + integrity sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA== + 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.6.3" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" + integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + +jest-mock@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.6.3.tgz#433f3fd528c8ec5a76860177484940628bdf5e0a" + integrity sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-util "^29.6.3" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== + +jest-regex-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" + integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== + +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== + +jest-resolve-dependencies@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" + integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== + dependencies: + "@jest/types" "^27.5.1" + jest-regex-util "^27.5.1" + jest-snapshot "^27.5.1" + +jest-resolve-dependencies@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz#20156b33c7eacbb6bb77aeba4bed0eab4a3f8734" + integrity sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA== + dependencies: + jest-regex-util "^29.6.3" + jest-snapshot "^29.6.4" + +jest-resolve@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" + integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== + dependencies: + "@jest/types" "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-pnp-resolver "^1.2.2" + jest-util "^27.5.1" + jest-validate "^27.5.1" + resolve "^1.20.0" + resolve.exports "^1.1.0" + slash "^3.0.0" + +jest-resolve@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.6.4.tgz#e34cb06f2178b429c38455d98d1a07572ac9faa3" + integrity sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.4" + jest-pnp-resolver "^1.2.2" + jest-util "^29.6.3" + jest-validate "^29.6.3" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + +jest-runner@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" + integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== + dependencies: + "@jest/console" "^27.5.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.8.1" + graceful-fs "^4.2.9" + jest-docblock "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-haste-map "^27.5.1" + jest-leak-detector "^27.5.1" + jest-message-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runtime "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + source-map-support "^0.5.6" + throat "^6.0.1" + +jest-runner@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.6.4.tgz#b3b8ccb85970fde0fae40c73ee11eb75adccfacf" + integrity sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw== + dependencies: + "@jest/console" "^29.6.4" + "@jest/environment" "^29.6.4" + "@jest/test-result" "^29.6.4" + "@jest/transform" "^29.6.4" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.6.3" + jest-environment-node "^29.6.4" + jest-haste-map "^29.6.4" + jest-leak-detector "^29.6.3" + jest-message-util "^29.6.3" + jest-resolve "^29.6.4" + jest-runtime "^29.6.4" + jest-util "^29.6.3" + jest-watcher "^29.6.4" + jest-worker "^29.6.4" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" + integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/globals" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + execa "^5.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-runtime@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.6.4.tgz#b0bc495c9b6b12a0a7042ac34ca9bb85f8cd0ded" + integrity sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA== + dependencies: + "@jest/environment" "^29.6.4" + "@jest/fake-timers" "^29.6.4" + "@jest/globals" "^29.6.4" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.6.4" + "@jest/transform" "^29.6.4" + "@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.6.4" + jest-message-util "^29.6.3" + jest-mock "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.6.4" + jest-snapshot "^29.6.4" + jest-util "^29.6.3" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-serializer@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-serializer@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" + integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.9" + +jest-snapshot@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" + integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== + dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.0.0" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^27.5.1" + graceful-fs "^4.2.9" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + jest-haste-map "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-util "^27.5.1" + natural-compare "^1.4.0" + pretty-format "^27.5.1" + semver "^7.3.2" + +jest-snapshot@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.6.4.tgz#9833eb6b66ff1541c7fd8ceaa42d541f407b4876" + integrity sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA== + 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.6.4" + "@jest/transform" "^29.6.4" + "@jest/types" "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.6.4" + graceful-fs "^4.2.9" + jest-diff "^29.6.4" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.6.4" + jest-message-util "^29.6.3" + jest-util "^29.6.3" + natural-compare "^1.4.0" + pretty-format "^29.6.3" + semver "^7.5.3" + +jest-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^2.0.0" + micromatch "^4.0.2" + +jest-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" + integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.3.tgz#e15c3eac8716440d1ed076f09bc63ace1aebca63" + integrity sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA== + 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" + +jest-validate@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" + integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== + dependencies: + "@jest/types" "^27.5.1" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^27.5.1" + leven "^3.1.0" + pretty-format "^27.5.1" + +jest-validate@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.6.3.tgz#a75fca774cfb1c5758c70d035d30a1f9c2784b4d" + integrity sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg== + 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.6.3" + +jest-watcher@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" + integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== + dependencies: + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^27.5.1" + string-length "^4.0.1" + +jest-watcher@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.6.4.tgz#633eb515ae284aa67fd6831f1c9d1b534cf0e0ba" + integrity sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ== + dependencies: + "@jest/test-result" "^29.6.4" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.6.3" + string-length "^4.0.1" + +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest-worker@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.6.4.tgz#f34279f4afc33c872b470d4af21b281ac616abd3" + integrity sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q== + dependencies: + "@types/node" "*" + jest-util "^29.6.3" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^27.3.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" + integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== + dependencies: + "@jest/core" "^27.5.1" + import-local "^3.0.2" + jest-cli "^27.5.1" + +jest@^29.6.4: + version "29.6.4" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.6.4.tgz#7c48e67a445ba264b778253b5d78d4ebc9d0a622" + integrity sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw== + dependencies: + "@jest/core" "^29.6.4" + "@jest/types" "^29.6.3" + import-local "^3.0.2" + jest-cli "^29.6.4" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsdom@^16.6.0: + version "16.7.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" + integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== + dependencies: + abab "^2.0.5" + acorn "^8.2.4" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.3.0" + data-urls "^2.0.0" + decimal.js "^10.2.1" + domexception "^2.0.1" + escodegen "^2.0.0" + form-data "^3.0.0" + html-encoding-sniffer "^2.0.1" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.5.0" + ws "^7.4.6" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash@^4.17.4, lodash@^4.7.0: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== + dependencies: + object-visit "^1.0.0" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.1, minimist@^1.2.0: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nwsapi@^2.2.0: + version "2.2.7" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" + integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== + dependencies: + isobject "^3.0.0" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== + dependencies: + isobject "^3.0.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + 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" + +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pirates@^4.0.1, pirates@^4.0.4: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== + +pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +pretty-format@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.3.tgz#d432bb4f1ca6f9463410c3fb25a0ba88e594ace7" + integrity sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +prompts@^2.0.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +pure-rand@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.3.tgz#3c9e6b53c09e52ac3cedffc85ab7c1c7094b38cb" + integrity sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w== + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + +regenerate-unicode-properties@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" + integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +regenerator-runtime@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" + integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== + +regenerator-transform@^0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== + dependencies: + "@babel/runtime" "^7.8.4" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +rescript@^10.1.4: + version "10.1.4" + resolved "https://registry.yarnpkg.com/rescript/-/rescript-10.1.4.tgz#0f37710d371f32a704f17b4e804f66ce3c79a305" + integrity sha512-FFKlS9AG/XrLepWsyw7B+A9DtQBPWEPDPDKghV831Y2KGbie+eeFBOS0xtRHp0xbt7S0N2Dm6hhX+kTZQ/3Ybg== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== + +resolve.exports@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" + integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== + +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + +resolve@^1.14.2, resolve@^1.20.0: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rimraf@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + +semver@^5.5.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.2, semver@^7.5.3, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@^0.5.6: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +throat@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" + integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +tough-cookie@^4.0.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tr46@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== + dependencies: + punycode "^2.1.1" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +update-browserslist-db@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +v8-to-istanbul@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" + integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +v8-to-istanbul@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7, walker@^1.0.8, walker@~1.0.5: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^8.0.0, whatwg-url@^8.5.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" + integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== + dependencies: + lodash "^4.7.0" + tr46 "^2.1.0" + webidl-conversions "^6.1.0" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +ws@^7.4.6: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + 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" + +yargs@^17.3.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + 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" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/ruby/.buildpath b/ruby/.buildpath new file mode 100644 index 00000000..47f1b520 --- /dev/null +++ b/ruby/.buildpath @@ -0,0 +1,5 @@ + + + + + diff --git a/ruby/.gitignore b/ruby/.gitignore new file mode 100644 index 00000000..c38fa4e0 --- /dev/null +++ b/ruby/.gitignore @@ -0,0 +1,2 @@ +.idea +*.iml diff --git a/ruby/.loadpath b/ruby/.loadpath new file mode 100644 index 00000000..b7db479f --- /dev/null +++ b/ruby/.loadpath @@ -0,0 +1,5 @@ + + + + + diff --git a/ruby/.project b/ruby/.project new file mode 100644 index 00000000..fcc43146 --- /dev/null +++ b/ruby/.project @@ -0,0 +1,23 @@ + + + GildedRose.rb + + + + + + org.rubypeople.rdt.core.rubybuilder + + + + + org.eclipse.dltk.core.scriptbuilder + + + + + + org.rubypeople.rdt.core.rubynature + org.eclipse.dltk.ruby.core.nature + + diff --git a/ruby/.rspec b/ruby/.rspec new file mode 100644 index 00000000..7438fbe5 --- /dev/null +++ b/ruby/.rspec @@ -0,0 +1,2 @@ +--colour +--format documentation diff --git a/ruby/README.md b/ruby/README.md new file mode 100644 index 00000000..ffbd4914 --- /dev/null +++ b/ruby/README.md @@ -0,0 +1,31 @@ +# Gilded Rose starting position in Ruby + +## Installation + + +## Run the unit tests from the Command-Line + +Ensure you have RSpec installed + + gem install rspec + +``` +rspec gilded_rose_spec.rb +``` + +## Run the TextTest fixture from the Command-Line + +For e.g. 10 days: + +``` +ruby texttest_fixture.rb 10 +``` + +You should make sure the command shown above works when you execute it in a terminal before trying to use TextTest (see below). + +## Run the TextTest approval test that comes with this project + +There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. You will need to specify the Ruby executable and interpreter in [config.gr](../texttests/config.gr). Uncomment these lines: + + executable:${TEXTTEST_HOME}/ruby/texttest_fixture.rb + interpreter:ruby diff --git a/ruby/gilded_rose.rb b/ruby/gilded_rose.rb new file mode 100644 index 00000000..e177a497 --- /dev/null +++ b/ruby/gilded_rose.rb @@ -0,0 +1,68 @@ +class GildedRose + + def initialize(items) + @items = items + end + + def update_quality() + @items.each do |item| + if item.name != "Aged Brie" and item.name != "Backstage passes to a TAFKAL80ETC concert" + if item.quality > 0 + if item.name != "Sulfuras, Hand of Ragnaros" + item.quality = item.quality - 1 + end + end + else + if item.quality < 50 + item.quality = item.quality + 1 + if item.name == "Backstage passes to a TAFKAL80ETC concert" + if item.sell_in < 11 + if item.quality < 50 + item.quality = item.quality + 1 + end + end + if item.sell_in < 6 + if item.quality < 50 + item.quality = item.quality + 1 + end + end + end + end + end + if item.name != "Sulfuras, Hand of Ragnaros" + item.sell_in = item.sell_in - 1 + end + if item.sell_in < 0 + if item.name != "Aged Brie" + if item.name != "Backstage passes to a TAFKAL80ETC concert" + if item.quality > 0 + if item.name != "Sulfuras, Hand of Ragnaros" + item.quality = item.quality - 1 + end + end + else + item.quality = item.quality - item.quality + end + else + if item.quality < 50 + item.quality = item.quality + 1 + end + end + end + end + end +end + +class Item + attr_accessor :name, :sell_in, :quality + + def initialize(name, sell_in, quality) + @name = name + @sell_in = sell_in + @quality = quality + end + + def to_s() + "#{@name}, #{@sell_in}, #{@quality}" + end +end diff --git a/ruby/gilded_rose_spec.rb b/ruby/gilded_rose_spec.rb new file mode 100644 index 00000000..015a759f --- /dev/null +++ b/ruby/gilded_rose_spec.rb @@ -0,0 +1,11 @@ +require 'rspec' + +require File.join(File.dirname(__FILE__), 'gilded_rose') + +describe GildedRose do + it "does not change the name" do + items = [Item.new("foo", 0, 0)] + GildedRose.new(items).update_quality() + expect(items[0].name).to eq "fixme" + end +end diff --git a/ruby/gilded_rose_tests.rb b/ruby/gilded_rose_tests.rb new file mode 100644 index 00000000..4e2ec30d --- /dev/null +++ b/ruby/gilded_rose_tests.rb @@ -0,0 +1,10 @@ +require 'minitest/autorun' +require_relative 'gilded_rose' + +class TestGildedRose < Minitest::Test + def test_foo + items = [Item.new("foo", 0, 0)] + GildedRose.new(items).update_quality() + assert_equal "fixme", items[0].name + end +end diff --git a/ruby/texttest_fixture.rb b/ruby/texttest_fixture.rb new file mode 100644 index 00000000..ad76aacf --- /dev/null +++ b/ruby/texttest_fixture.rb @@ -0,0 +1,33 @@ +#!/usr/bin/ruby -w + +require File.join(File.dirname(__FILE__), 'gilded_rose') + +puts "OMGHAI!" +items = [ + Item.new(name="+5 Dexterity Vest", sell_in=10, quality=20), + Item.new(name="Aged Brie", sell_in=2, quality=0), + Item.new(name="Elixir of the Mongoose", sell_in=5, quality=7), + Item.new(name="Sulfuras, Hand of Ragnaros", sell_in=0, quality=80), + Item.new(name="Sulfuras, Hand of Ragnaros", sell_in=-1, quality=80), + Item.new(name="Backstage passes to a TAFKAL80ETC concert", sell_in=15, quality=20), + Item.new(name="Backstage passes to a TAFKAL80ETC concert", sell_in=10, quality=49), + Item.new(name="Backstage passes to a TAFKAL80ETC concert", sell_in=5, quality=49), + # This Conjured item does not work properly yet + Item.new(name="Conjured Mana Cake", sell_in=3, quality=6), # <-- :O +] + +days = 2 +if ARGV.size > 0 + days = ARGV[0].to_i + 1 +end + +gilded_rose = GildedRose.new items +(0...days).each do |day| + puts "-------- day #{day} --------" + puts "name, sellIn, quality" + items.each do |item| + puts item + end + puts "" + gilded_rose.update_quality +end diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 00000000..2f7896d1 --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 00000000..e9e570d7 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "rust" +version = "0.2.0" + diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 00000000..875b0d44 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "rust" +version = "0.2.0" +authors = ["Michael Gerhaeuser ", "rrokkam "] +edition = "2018" diff --git a/rust/src/gildedrose.rs b/rust/src/gildedrose.rs new file mode 100644 index 00000000..e65fa085 --- /dev/null +++ b/rust/src/gildedrose.rs @@ -0,0 +1,99 @@ +use std::fmt::{self, Display}; +pub struct Item { + pub name: String, + pub sell_in: i32, + pub quality: i32, +} + +impl Item { + pub fn new(name: impl Into, sell_in: i32, quality: i32) -> Item { + Item { + name: name.into(), + sell_in, + quality, + } + } +} + +impl Display for Item { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}, {}, {}", self.name, self.sell_in, self.quality) + } +} + +pub struct GildedRose { + pub items: Vec, +} + +impl GildedRose { + pub fn new(items: Vec) -> GildedRose { + GildedRose { items } + } + + pub fn update_quality(&mut self) { + for i in 0..self.items.len() { + if self.items[i].name != "Aged Brie" && self.items[i].name != "Backstage passes to a TAFKAL80ETC concert" + { + if self.items[i].quality > 0 { + if self.items[i].name != "Sulfuras, Hand of Ragnaros" { + self.items[i].quality = self.items[i].quality - 1; + } + } + } else { + if self.items[i].quality < 50 { + self.items[i].quality = self.items[i].quality + 1; + + if self.items[i].name == "Backstage passes to a TAFKAL80ETC concert" { + if self.items[i].sell_in < 11 { + if self.items[i].quality < 50 { + self.items[i].quality = self.items[i].quality + 1; + } + } + + if self.items[i].sell_in < 6 { + if self.items[i].quality < 50 { + self.items[i].quality = self.items[i].quality + 1; + } + } + } + } + } + + if self.items[i].name != "Sulfuras, Hand of Ragnaros" { + self.items[i].sell_in = self.items[i].sell_in - 1; + } + + if self.items[i].sell_in < 0 { + if self.items[i].name != "Aged Brie" { + if self.items[i].name != "Backstage passes to a TAFKAL80ETC concert" { + if self.items[i].quality > 0 { + if self.items[i].name != "Sulfuras, Hand of Ragnaros" { + self.items[i].quality = self.items[i].quality - 1; + } + } + } else { + self.items[i].quality = self.items[i].quality - self.items[i].quality; + } + } else { + if self.items[i].quality < 50 { + self.items[i].quality = self.items[i].quality + 1; + } + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::{GildedRose, Item}; + + #[test] + pub fn foo() { + let items = vec![Item::new("foo", 0, 0)]; + let mut rose = GildedRose::new(items); + rose.update_quality(); + + assert_eq!("fixme", rose.items[0].name); + } +} diff --git a/rust/src/main.rs b/rust/src/main.rs new file mode 100644 index 00000000..3a552a81 --- /dev/null +++ b/rust/src/main.rs @@ -0,0 +1,30 @@ +mod gildedrose; + +use gildedrose::{GildedRose, Item}; + +fn main() { + let items = vec![ + Item::new("+5 Dexterity Vest", 10, 20), + Item::new("Aged Brie", 2, 0), + Item::new("Elixir of the Mongoose", 5, 7), + Item::new("Sulfuras, Hand of Ragnaros", 0, 80), + Item::new("Sulfuras, Hand of Ragnaros", -1, 80), + Item::new("Backstage passes to a TAFKAL80ETC concert", 15, 20), + Item::new("Backstage passes to a TAFKAL80ETC concert", 10, 49), + Item::new("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this conjured item does not work properly yet + Item::new("Conjured Mana Cake", 3, 6), + ]; + let mut rose = GildedRose::new(items); + + println!("OMGHAI!"); + for i in 0..=30 { + println!("-------- day {} --------", i); + println!("name, sellIn, quality"); + for item in &rose.items { + println!("{}", item); + } + println!(); + rose.update_quality(); + } +} diff --git a/scala/.gitignore b/scala/.gitignore new file mode 100644 index 00000000..5534fac1 --- /dev/null +++ b/scala/.gitignore @@ -0,0 +1,19 @@ +/bin/ + +# OSX Finder +.DS_Store + +# IntelliJ +.idea +*.iml + +# Eclipse +.worksheet +.settings +.cache +.cache-main +.cache-tests +.project +.classpath + +target/ diff --git a/scala/README.md b/scala/README.md new file mode 100644 index 00000000..6f36544e --- /dev/null +++ b/scala/README.md @@ -0,0 +1,3 @@ +# Gilded Rose starting position in Scala + +TODO: explain how to run this code diff --git a/scala/build.sbt b/scala/build.sbt new file mode 100644 index 00000000..d628c9f4 --- /dev/null +++ b/scala/build.sbt @@ -0,0 +1,8 @@ +name := "GildedRose" + +version := "1.0" + +scalaVersion := "2.13.1" + +resolvers += DefaultMavenRepository +libraryDependencies += "org.scalatest" %% "scalatest" % "3.1.1" % "test" diff --git a/scala/project/.gitignore b/scala/project/.gitignore new file mode 100644 index 00000000..6ba19f7f --- /dev/null +++ b/scala/project/.gitignore @@ -0,0 +1,2 @@ +project/ +target/ diff --git a/scala/project/build.properties b/scala/project/build.properties new file mode 100644 index 00000000..06703e34 --- /dev/null +++ b/scala/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.3.9 diff --git a/scala/src/main/scala/com/gildedrose/GildedRose.scala b/scala/src/main/scala/com/gildedrose/GildedRose.scala new file mode 100644 index 00000000..9f9fdd2b --- /dev/null +++ b/scala/src/main/scala/com/gildedrose/GildedRose.scala @@ -0,0 +1,58 @@ +package com.gildedrose + +class GildedRose(val items: Array[Item]) { + + + def updateQuality() { + for (i <- 0 until items.length) { + if (!items(i).name.equals("Aged Brie") + && !items(i).name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items(i).quality > 0) { + if (!items(i).name.equals("Sulfuras, Hand of Ragnaros")) { + items(i).quality = items(i).quality - 1 + } + } + } else { + if (items(i).quality < 50) { + items(i).quality = items(i).quality + 1 + + if (items(i).name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items(i).sellIn < 11) { + if (items(i).quality < 50) { + items(i).quality = items(i).quality + 1 + } + } + + if (items(i).sellIn < 6) { + if (items(i).quality < 50) { + items(i).quality = items(i).quality + 1 + } + } + } + } + } + + if (!items(i).name.equals("Sulfuras, Hand of Ragnaros")) { + items(i).sellIn = items(i).sellIn - 1 + } + + if (items(i).sellIn < 0) { + if (!items(i).name.equals("Aged Brie")) { + if (!items(i).name.equals("Backstage passes to a TAFKAL80ETC concert")) { + if (items(i).quality > 0) { + if (!items(i).name.equals("Sulfuras, Hand of Ragnaros")) { + items(i).quality = items(i).quality - 1 + } + } + } else { + items(i).quality = items(i).quality - items(i).quality + } + } else { + if (items(i).quality < 50) { + items(i).quality = items(i).quality + 1 + } + } + } + } + } +} \ No newline at end of file diff --git a/scala/src/main/scala/com/gildedrose/Item.scala b/scala/src/main/scala/com/gildedrose/Item.scala new file mode 100644 index 00000000..8515ced7 --- /dev/null +++ b/scala/src/main/scala/com/gildedrose/Item.scala @@ -0,0 +1,5 @@ +package com.gildedrose + +class Item(val name: String, var sellIn: Int, var quality: Int) { + +} \ No newline at end of file diff --git a/scala/src/test/scala/com/gildedrose/GildedRoseTest.scala b/scala/src/test/scala/com/gildedrose/GildedRoseTest.scala new file mode 100644 index 00000000..c0ce6b23 --- /dev/null +++ b/scala/src/test/scala/com/gildedrose/GildedRoseTest.scala @@ -0,0 +1,13 @@ +package com.gildedrose + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class GildedRoseTest extends AnyFlatSpec with Matchers { + it should "foo" in { + val items = Array[Item](new Item("foo", 0, 0)) + val app = new GildedRose(items) + app.updateQuality() + app.items(0).name should equal ("fixme") + } +} \ No newline at end of file diff --git a/scala/src/test/scala/com/gildedrose/TexttestFixture.scala b/scala/src/test/scala/com/gildedrose/TexttestFixture.scala new file mode 100644 index 00000000..1d00808c --- /dev/null +++ b/scala/src/test/scala/com/gildedrose/TexttestFixture.scala @@ -0,0 +1,29 @@ +package com.gildedrose + +object TexttestFixture { + def main(args: Array[String]): Unit = { + val items = Array[Item]( + new Item("+5 Dexterity Vest", 10, 20), + new Item("Aged Brie", 2, 0), + new Item("Elixir of the Mongoose", 5, 7), + new Item("Sulfuras, Hand of Ragnaros", 0, 80), + new Item("Sulfuras, Hand of Ragnaros", -1, 80), + new Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this conjured item does not work properly yet + new Item("Conjured Mana Cake", 3, 6) + ) + val app = new GildedRose(items) + val days = if (args.length > 0) args(0).toInt + 1 else 2 + for (i <- 0 until days) { + System.out.println("-------- day " + i + " --------") + System.out.println("name, sellIn, quality") + for (item <- items) { + System.out.println(item.name + ", " + item.sellIn + ", " + item.quality) + } + System.out.println() + app.updateQuality() + } + } +} diff --git a/scheme/README.md b/scheme/README.md new file mode 100644 index 00000000..0318ef54 --- /dev/null +++ b/scheme/README.md @@ -0,0 +1,18 @@ +# Scheme port of the Gilded-Rose Kata + +This is a (Gambit) R5RS Scheme port of the *Gilded-Rose-Kata*. + +## Building and Running + +```shell +gsi texttest-fixture.scm +``` + +## Unit Test + +`assert.scm` is a minimalist implementation of xUnit in Scheme style. +There are two assertions available, e.g. `(assert=)` and `(assert-string=)`. + +```shell +gsi gilded-rose-test.scm +``` diff --git a/scheme/assert.scm b/scheme/assert.scm new file mode 100644 index 00000000..99828669 --- /dev/null +++ b/scheme/assert.scm @@ -0,0 +1,58 @@ +;;; +;;; Unit test framework for Scheme +;;; Copyright (c) 2018, Peter Kofler, http://www.code-cop.org/ +;;; BSD licensed. +;;; +;;; Non S5RS used functions: +;;; * (error) from R6RS +;;; + +;; SchemeUnit from http://c2.com/cgi/wiki?SchemeUnit + +(define (fail msg) + (error (string-append "AssertionError" ": " msg))) + +(define (check msg condition) + (if (not condition) + (fail msg) + #t)) + +(define (assert msg condition) + (lambda () (check msg condition))) + +;; extensions + +;; private +(define (make-string-message prefix to-string expected actual) + (make-message prefix + (to-string expected) + (to-string actual))) + +;; private +(define (make-message prefix expected actual) + (string-append prefix "expected:<" expected "> but was:<" actual ">")) + +(define (assert-equal to-string eq-op expected actual) + (assert (make-string-message "" to-string expected actual) + (eq-op expected actual))) + +(define (assert= expected actual) + (assert-equal number->string = expected actual)) + +(define (assert-string= expected actual) + (assert-equal values string=? expected actual)) + +;; private +(define (test-case-name name) + (display name) + (display " ... ")) + +;; private +(define (test-case-success) + (display "OK") + (newline)) + +(define (test-case name . assertions) + (test-case-name name) + (for-each (lambda (a) (a)) assertions) + (test-case-success)) diff --git a/scheme/gilded-rose-test.scm b/scheme/gilded-rose-test.scm new file mode 100644 index 00000000..b0d67e09 --- /dev/null +++ b/scheme/gilded-rose-test.scm @@ -0,0 +1,7 @@ +(include "assert.scm") +(include "gilded-rose.scm") + +(test-case "foo" + (let ((items (list (make-item "foo" 0 0)))) + (update-quality items) + (assert-string= "fixme" (item-name (car items))))) diff --git a/scheme/gilded-rose.scm b/scheme/gilded-rose.scm new file mode 100644 index 00000000..dac984fa --- /dev/null +++ b/scheme/gilded-rose.scm @@ -0,0 +1,46 @@ +;;; Class ITEM + +(define-record-type item (make-item name sell-in quality) item? name sell-in quality) +;; define-record-type ... SRFI-9 +;; creates make-item, item?, item-name, item-sell-in, item-quality, item-name-set!, item-sell-in-set!, item-quality-set! + +(define (item-to-string item) + (string-append (item-name item) + ", " + (number->string (item-sell-in item)) + ", " + (number->string (item-quality item)))) + +;;; GILDED-ROSE + +(define (update-quality items) + (for-each + (lambda (item) + (if (and (not (string=? (item-name item) "Aged Brie")) + (not (string=? (item-name item) "Backstage passes to a TAFKAL80ETC concert"))) + (if (> (item-quality item) 0) + (if (not (string=? (item-name item) "Sulfuras, Hand of Ragnaros")) + (item-quality-set! item (- (item-quality item) 1)))) + (cond ((< (item-quality item) 50) + (item-quality-set! item (+ (item-quality item) 1)) + (if (string=? (item-name item) "Backstage passes to a TAFKAL80ETC concert") + (if (< (item-sell-in item) 11) + (cond ((< (item-quality item) 50) + (item-quality-set! item (+ (item-quality item) 1)) + (if (< (item-sell-in item) 6) + (if (< (item-quality item) 50) + (item-quality-set! item (+ (item-quality item) 1))))))))))) + + (if (not (string=? (item-name item) "Sulfuras, Hand of Ragnaros")) + (item-sell-in-set! item (- (item-sell-in item) 1))) + + (if (< (item-sell-in item) 0) + (if (not (string=? (item-name item) "Aged Brie")) + (if (not (string=? (item-name item) "Backstage passes to a TAFKAL80ETC concert")) + (if (> (item-quality item) 0) + (if (not (string=? (item-name item) "Sulfuras, Hand of Ragnaros")) + (item-quality-set! item (- (item-quality item) 1)))) + (item-quality-set! item (- (item-quality item) (item-quality item)))) + (if (< (item-quality item) 50) + (item-quality-set! item (+ (item-quality item) 1)))))) + items)) diff --git a/scheme/texttest-fixture.scm b/scheme/texttest-fixture.scm new file mode 100644 index 00000000..ee15981e --- /dev/null +++ b/scheme/texttest-fixture.scm @@ -0,0 +1,32 @@ +(include "gilded-rose.scm") + +(display "OMGHAI!") +(newline) + +(let ((items (list (make-item "+5 Dexterity Vest" 10 20) + (make-item "Aged Brie" 2 0) + (make-item "Elixir of the Mongoose" 5 7) + (make-item "Sulfuras, Hand of Ragnaros" 0 80) + (make-item "Sulfuras, Hand of Ragnaros" -1 80) + (make-item "Backstage passes to a TAFKAL80ETC concert" 15 20) + (make-item "Backstage passes to a TAFKAL80ETC concert" 10 49) + (make-item "Backstage passes to a TAFKAL80ETC concert" 5 49) + ;; this conjured item does not work properly yet + (make-item "Conjured Mana Cake" 3 6))) + (days 2)) + + (define (loop day) + (cond ((< day days) + (display (string-append "-------- day " (number->string day) " --------")) + (newline) + (display "name, sell-in, quality") + (newline) + (for-each + (lambda (item) + (display (item-to-string item)) + (newline)) + items) + (newline) + (update-quality items) + (loop (+ day 1))))) + (loop 0)) diff --git a/sql/README.md b/sql/README.md new file mode 100644 index 00000000..b667e475 --- /dev/null +++ b/sql/README.md @@ -0,0 +1,12 @@ +# Introduction +This code aims to be based on ISO-compliant, therefore database-agnostic. +However, Data Definition Language (DDL) usually involves vendor variants. + +# Setup +Create database structure: see ./structure//create.sql +Load test data: see ./test/data/load.sql + +# Execution +Execute SQL script: see /code/update_quality.sql +TODO: Introduce test framework - vendor specific + diff --git a/sql/code/update_quality.sql b/sql/code/update_quality.sql new file mode 100644 index 00000000..9da22bd5 --- /dev/null +++ b/sql/code/update_quality.sql @@ -0,0 +1,69 @@ +UPDATE item +SET + quality = quality - 1 +WHERE 1=1 + AND ( name <> 'Aged Brie' AND name <> 'Backstage passes to a TAFKAL80ETC concert') + AND quality > 0 + AND name <> 'Sulfuras, Hand of Ragnaros' +; + +UPDATE item +SET + quality = quality + 1 +WHERE 1=1 + AND NOT ( name <> 'Aged Brie' AND name <> 'Backstage passes to a TAFKAL80ETC concert') + AND quality < 50 + AND name = 'Backstage passes to a TAFKAL80ETC concert' + AND sellIn < 11 + AND quality < 50 +; + +UPDATE item +SET + quality = quality + 1 +WHERE 1=1 + AND NOT ( name <> 'Aged Brie' AND name <> 'Backstage passes to a TAFKAL80ETC concert') + AND quality < 50 + AND name = 'Backstage passes to a TAFKAL80ETC concert' + AND sellIn < 6 + AND quality < 50 +; + +UPDATE item +SET + sellIn = sellIn - 1 +WHERE 1=1 + AND name <> 'Sulfuras, Hand of Ragnaros' +; + +UPDATE item +SET + quality = quality - 1 +WHERE 1=1 + AND sellIn < 0 + AND name <> 'Aged Brie' + AND name <> 'Backstage passes to a TAFKAL80ETC concert' + AND quality > 0 + AND name <> 'Sulfuras, Hand of Ragnaros' +; + +UPDATE item +SET + quality = quality - quality +WHERE 1=1 + AND sellIn < 0 + AND name <> 'Aged Brie' + AND NOT (name <> 'Backstage passes to a TAFKAL80ETC concert') +; + +UPDATE item +SET + quality = quality + 1 +WHERE 1=1 + AND sellIn < 0 + AND NOT (name <> 'Aged Brie') + AND quality < 50 + AND name <> 'Sulfuras, Hand of Ragnaros' +; + +COMMIT; \ No newline at end of file diff --git a/sql/structure/postgreSQL/create.sql b/sql/structure/postgreSQL/create.sql new file mode 100644 index 00000000..5ce4d245 --- /dev/null +++ b/sql/structure/postgreSQL/create.sql @@ -0,0 +1,9 @@ +CREATE DATABASE gilded_rose; + +\connect gilded_rose; + +CREATE TABLE item ( + name CHARACTER VARYING(100) NOT NULL, + sellIn INTEGER, + quality INTEGER NOT NULL +); diff --git a/sql/test/data/load.sql b/sql/test/data/load.sql new file mode 100644 index 00000000..7a142212 --- /dev/null +++ b/sql/test/data/load.sql @@ -0,0 +1,15 @@ +DELETE FROM item; + +INSERT INTO item (name, sellIn, quality) VALUES ('+5 Dexterity Vest', 10, 20); +INSERT INTO item (name, sellIn, quality) VALUES ('Aged Brie', 2, 0); +INSERT INTO item (name, sellIn, quality) VALUES ('Elixir of the Mongoose', 5, 7); +INSERT INTO item (name, sellIn, quality) VALUES ('Sulfuras, Hand of Ragnaros', 0, 80); +INSERT INTO item (name, sellIn, quality) VALUES ('Sulfuras, Hand of Ragnaros', -1, 80); +INSERT INTO item (name, sellIn, quality) VALUES ('Backstage passes to a TAFKAL80ETC concert', 15, 20); +INSERT INTO item (name, sellIn, quality) VALUES ('Backstage passes to a TAFKAL80ETC concert', 10, 49); +INSERT INTO item (name, sellIn, quality) VALUES ('Backstage passes to a TAFKAL80ETC concert', 5, 49); + +-- this conjured item does not work properly yet +INSERT INTO item (name, sellIn, quality) VALUES ('Conjured Mana Cake', 3, 6); + +COMMIT; diff --git a/start_texttest.bat b/start_texttest.bat new file mode 100644 index 00000000..32f2d974 --- /dev/null +++ b/start_texttest.bat @@ -0,0 +1,5 @@ +set TEXTTEST_HOME=%~dp0 +cd %TEXTTEST_HOME% + +texttestc -con + diff --git a/start_texttest.sh b/start_texttest.sh new file mode 100755 index 00000000..805922b1 --- /dev/null +++ b/start_texttest.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +if [ ! -d "venv" ]; then + python -m venv venv +fi +venv/bin/pip install texttest +venv/bin/texttest -d . -con "$@" diff --git a/start_texttest_from_python.bat b/start_texttest_from_python.bat new file mode 100644 index 00000000..d13f4432 --- /dev/null +++ b/start_texttest_from_python.bat @@ -0,0 +1,14 @@ +set TEXTTEST_HOME=%~dp0 +cd %TEXTTEST_HOME% + +if not exist "venv" ( + py -m venv venv +) + +venv\Scripts\pip install texttest + +if %ERRORLEVEL% GEQ 1 ( + pause +) else ( + venv\Scripts\texttestc.py -con %* +) diff --git a/swift/.gitignore b/swift/.gitignore new file mode 100644 index 00000000..6ea2fdde --- /dev/null +++ b/swift/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata \ No newline at end of file diff --git a/swift/Package.swift b/swift/Package.swift new file mode 100644 index 00000000..3a86a40d --- /dev/null +++ b/swift/Package.swift @@ -0,0 +1,27 @@ +// swift-tools-version:5.5 + +import PackageDescription + +let package = Package( + name: "GildedRose", + products: [ + .library( + name: "GildedRose", + targets: ["GildedRose"] + ), + ], + targets: [ + .target( + name: "GildedRose", + dependencies: [] + ), + .executableTarget( + name: "GildedRoseApp", + dependencies: ["GildedRose"] + ), + .testTarget( + name: "GildedRoseTests", + dependencies: ["GildedRose"] + ), + ] +) diff --git a/swift/README.md b/swift/README.md new file mode 100644 index 00000000..60168d5b --- /dev/null +++ b/swift/README.md @@ -0,0 +1,12 @@ +## Build and test using any of the following + +Command line: +- `swift test` + +Xcode: +- Open this "swift" folder to open package +- In the Xcode menu, select Product > Test to run tests + +AppCode: +- Open this "swift" folder to open package +- Select "GildedRoseTests" configuration and run diff --git a/swift/Sources/GildedRose/GildedRose.swift b/swift/Sources/GildedRose/GildedRose.swift new file mode 100644 index 00000000..b3ff81a2 --- /dev/null +++ b/swift/Sources/GildedRose/GildedRose.swift @@ -0,0 +1,59 @@ +public class GildedRose { + var items: [Item] + + public init(items: [Item]) { + self.items = items + } + + public func updateQuality() { + for i in 0 ..< items.count { + if items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert" { + if items[i].quality > 0 { + if items[i].name != "Sulfuras, Hand of Ragnaros" { + items[i].quality = items[i].quality - 1 + } + } + } else { + if items[i].quality < 50 { + items[i].quality = items[i].quality + 1 + + if items[i].name == "Backstage passes to a TAFKAL80ETC concert" { + if items[i].sellIn < 11 { + if items[i].quality < 50 { + items[i].quality = items[i].quality + 1 + } + } + + if items[i].sellIn < 6 { + if items[i].quality < 50 { + items[i].quality = items[i].quality + 1 + } + } + } + } + } + + if items[i].name != "Sulfuras, Hand of Ragnaros" { + items[i].sellIn = items[i].sellIn - 1 + } + + if items[i].sellIn < 0 { + if items[i].name != "Aged Brie" { + if items[i].name != "Backstage passes to a TAFKAL80ETC concert" { + if items[i].quality > 0 { + if items[i].name != "Sulfuras, Hand of Ragnaros" { + items[i].quality = items[i].quality - 1 + } + } + } else { + items[i].quality = items[i].quality - items[i].quality + } + } else { + if items[i].quality < 50 { + items[i].quality = items[i].quality + 1 + } + } + } + } + } +} diff --git a/swift/Sources/GildedRose/Item.swift b/swift/Sources/GildedRose/Item.swift new file mode 100644 index 00000000..eb717bbb --- /dev/null +++ b/swift/Sources/GildedRose/Item.swift @@ -0,0 +1,17 @@ +public class Item { + public var name: String + public var sellIn: Int + public var quality: Int + + public init(name: String, sellIn: Int, quality: Int) { + self.name = name + self.sellIn = sellIn + self.quality = quality + } +} + +extension Item: CustomStringConvertible { + public var description: String { + name + ", " + String(sellIn) + ", " + String(quality) + } +} diff --git a/swift/Sources/GildedRoseApp/main.swift b/swift/Sources/GildedRoseApp/main.swift new file mode 100644 index 00000000..edd7dc13 --- /dev/null +++ b/swift/Sources/GildedRoseApp/main.swift @@ -0,0 +1,31 @@ +import GildedRose + +let items = [ + Item(name: "+5 Dexterity Vest", sellIn: 10, quality: 20), + Item(name: "Aged Brie", sellIn: 2, quality: 0), + Item(name: "Elixir of the Mongoose", sellIn: 5, quality: 7), + Item(name: "Sulfuras, Hand of Ragnaros", sellIn: 0, quality: 80), + Item(name: "Sulfuras, Hand of Ragnaros", sellIn: -1, quality: 80), + Item(name: "Backstage passes to a TAFKAL80ETC concert", sellIn: 15, quality: 20), + Item(name: "Backstage passes to a TAFKAL80ETC concert", sellIn: 10, quality: 49), + Item(name: "Backstage passes to a TAFKAL80ETC concert", sellIn: 5, quality: 49), + // this conjured item does not work properly yet + Item(name: "Conjured Mana Cake", sellIn: 3, quality: 6), +] + +let app = GildedRose(items: items) + +var days = 2 +if CommandLine.argc > 1 { + days = Int(CommandLine.arguments[1])! + 1 +} + +for i in 0 ..< days { + print("-------- day \(i) --------") + print("name, sellIn, quality") + for item in items { + print(item) + } + print("") + app.updateQuality() +} diff --git a/swift/Tests/GildedRoseTests/GildedRoseTests.swift b/swift/Tests/GildedRoseTests/GildedRoseTests.swift new file mode 100644 index 00000000..2241d23a --- /dev/null +++ b/swift/Tests/GildedRoseTests/GildedRoseTests.swift @@ -0,0 +1,11 @@ +@testable import GildedRose +import XCTest + +class GildedRoseTests: XCTestCase { + func testFoo() throws { + let items = [Item(name: "foo", sellIn: 0, quality: 0)] + let app = GildedRose(items: items) + app.updateQuality() + XCTAssertEqual(app.items[0].name, "fixme") + } +} diff --git a/texttests/.gitignore b/texttests/.gitignore new file mode 100644 index 00000000..5ceb3864 --- /dev/null +++ b/texttests/.gitignore @@ -0,0 +1 @@ +venv diff --git a/texttests/README.md b/texttests/README.md new file mode 100644 index 00000000..6b9c52d4 --- /dev/null +++ b/texttests/README.md @@ -0,0 +1,83 @@ +# TextTest regression tests + +This folder contains Text-Based Approval tests for the GildedRose Refactoring Kata designed by Emily Bache. They are fairly comprehensive and well worth using if you'd prefer to go straight to the refactoring without writing your own tests first. + +These tests are designed to be used with the open source testing tool "TextTest", available from [http://texttest.org](http://texttest.org). + + +## Configure the language version you want to test + +Before you can run the tests you need to tell texttest which language version of GildedRose you plan to refactor. Open the file 'config.gr' and edit it. Several languages are supported. All lines starting with '#' are comments in this file. Find the lines referring to the language you want, and uncomment them. + +While you're here, change the settings for editor and diff program to match your preferences. By default it uses 'subl' and 'meld'. It will accept any editors or diff programs that you can run from the command line. + +## Running TextTest + +The instructions are slightly different depending on your platform. + +### Running TextTest on Linux or MacOS + +There is a convenience script 'start_texttest.sh' in the root folder of this repo. This script assumes you already have Python installed. This script will first create a virtual python environment and install texttest if you haven't done that before, then run the tests. + +### Running TextTest on Windows + +Download the installer as explained on [TextTest.org](http://www.texttest.org/getting_started/install_windows.html). Make sure the texttest executable is on your PATH. Then use the convenience script 'start_texttest.bat' in the root folder of this repo to run the tests on the console. + +Windows may warn you that it doesn't trust this installer and be reluctant to download it. If you prefer not to continue with this, an alternative is to run TextTest via Python. First install Python then use the convenience script 'start_texttest_from_python.bat'. + +## Interpreting Test Results + +You should see output like this if the test passes: + + Using local queues for Application Gilded Rose Refactoring Kata + Q: Submitting Gilded Rose Refactoring Kata test-case ThirtyDays to default local queue + S: Gilded Rose Refactoring Kata test-case ThirtyDays succeeded on Emilys-MBP + +If the test fails it might look like this: + + Using local queues for Application Gilded Rose Refactoring Kata + Q: Submitting Gilded Rose Refactoring Kata test-case ThirtyDays to default local queue + S: Gilded Rose Refactoring Kata test-case ThirtyDays FAILED on Emilys-MBP : differences in stdout + View details(v), Approve(a) or continue(any other key)? + + +If you press 'v' it will try to open the diff tool you specified in 'config.gr' to show you the difference in output. If you press 'a' it will update the approved file - you will not want to do this if you are refactoring. Any other key will return you to the terminal prompt. + +## TextTest user interface + +TextTest has a graphical user interface you can use to manage your test cases. With only one test case it may not be worth it, but if you want to add other tests and/or examine test failures more closely it can be helpful. Be sure to set TEXTTEST_HOME to the root folder of this repository before starting the GUI. + +There are instructions for installing TextTest development tools on [texttest.org](https://texttest.org/) + +## Running the test without TextTest + +This should be perfectly possible, but is probably less convenient than using TextTest. + +Write a script that will execute the system under test (see "config.gr" for details of the executables), giving the commandline options listed in "options.gr". Collect the output from standard output in a file, and diff that against the golden copy "stdout.gr". Any diff is a test failure. + +## Explaining TextTest test cases + +Under the 'texttests' folder each test case has its own subdirectory. The name of the directory is the name of the test - in this case "ThirtyDays". The approved version of the output for that test case is kept in that directory. In this case we have three files: + +- __stderr.gr__ - the expected output to Standard Error (stderr) +- __stdout.gr__ - the expected output to Standard Output (stdout) +- __options.gr__ - the options to give on the command line when you run the System Under Test (SUT) + +In the directory above, there are configuration files for TextTest: + +- __config.gr__ - this tells TextTest where to find the SUT executable, and sets up options for how it runs the SUT and interprets the output. +- __environment.gr__ - this file lists environment variables that will be set before TextTest runs the SUT. This is especially important for Java applications, that need to set the CLASSPATH environment variable in order to run properly. +- __testsuite.gr__ - lists the constituent test cases of this suite. Change the order of the entries here to change the order they appear in the TextTest GUI. + +To run a test, click on it in the GUI and select "Run". TextTest will run it in a temporary (sandbox) directory and report the results. If the test fails, you can double click on a file to see the diff against the Golden Copy. + +If you run into difficulties with TextTest, there is documentation available on [texttest.org](http://texttest.org), or you can ask a question on the [mailing list](https://lists.sourceforge.net/lists/listinfo/texttest-users). + +## Introduction to Text-Based Approval Testing + +This is a testing approach which is very useful when refactoring legacy code. Before you change the code, you run it, and gather the output of the code as a plain text file. You review the text, and if it correctly describes the behaviour as you understand it, you can "approve" it, and save it as a "Golden Master". Then after you change the code, you run it again, and compare the new output against the Golden Master. Any differences, and the test fails. + +It's basically the same idea as "assertEquals(expected, actual)" in a unit test, except the text you are comparing is typically much longer, and the "expected" value is saved from actual output, rather than being defined in advance. + +Typically a piece of legacy code may not produce suitable textual output from the start, so you may need to modify it before you can write your first text-based approval test. One way to do that is to write a "main" method that executes the code and prints out what the result is afterwards. Each language version has implemented a texttest 'fixture' that does this. It runs the GildedRose 'update_quality' method once each day for 30 days, printing the item state each day. + diff --git a/texttests/ThirtyDays/options.gr b/texttests/ThirtyDays/options.gr new file mode 100755 index 00000000..64bb6b74 --- /dev/null +++ b/texttests/ThirtyDays/options.gr @@ -0,0 +1 @@ +30 diff --git a/texttests/ThirtyDays/stderr.gr b/texttests/ThirtyDays/stderr.gr new file mode 100755 index 00000000..e69de29b diff --git a/texttests/ThirtyDays/stdout.gr b/texttests/ThirtyDays/stdout.gr new file mode 100644 index 00000000..a04e48ac --- /dev/null +++ b/texttests/ThirtyDays/stdout.gr @@ -0,0 +1,373 @@ +OMGHAI! +-------- day 0 -------- +name, sellIn, quality ++5 Dexterity Vest, 10, 20 +Aged Brie, 2, 0 +Elixir of the Mongoose, 5, 7 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 15, 20 +Backstage passes to a TAFKAL80ETC concert, 10, 49 +Backstage passes to a TAFKAL80ETC concert, 5, 49 +Conjured Mana Cake, 3, 6 + +-------- day 1 -------- +name, sellIn, quality ++5 Dexterity Vest, 9, 19 +Aged Brie, 1, 1 +Elixir of the Mongoose, 4, 6 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 14, 21 +Backstage passes to a TAFKAL80ETC concert, 9, 50 +Backstage passes to a TAFKAL80ETC concert, 4, 50 +Conjured Mana Cake, 2, 5 + +-------- day 2 -------- +name, sellIn, quality ++5 Dexterity Vest, 8, 18 +Aged Brie, 0, 2 +Elixir of the Mongoose, 3, 5 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 13, 22 +Backstage passes to a TAFKAL80ETC concert, 8, 50 +Backstage passes to a TAFKAL80ETC concert, 3, 50 +Conjured Mana Cake, 1, 4 + +-------- day 3 -------- +name, sellIn, quality ++5 Dexterity Vest, 7, 17 +Aged Brie, -1, 4 +Elixir of the Mongoose, 2, 4 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 12, 23 +Backstage passes to a TAFKAL80ETC concert, 7, 50 +Backstage passes to a TAFKAL80ETC concert, 2, 50 +Conjured Mana Cake, 0, 3 + +-------- day 4 -------- +name, sellIn, quality ++5 Dexterity Vest, 6, 16 +Aged Brie, -2, 6 +Elixir of the Mongoose, 1, 3 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 11, 24 +Backstage passes to a TAFKAL80ETC concert, 6, 50 +Backstage passes to a TAFKAL80ETC concert, 1, 50 +Conjured Mana Cake, -1, 1 + +-------- day 5 -------- +name, sellIn, quality ++5 Dexterity Vest, 5, 15 +Aged Brie, -3, 8 +Elixir of the Mongoose, 0, 2 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 10, 25 +Backstage passes to a TAFKAL80ETC concert, 5, 50 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Conjured Mana Cake, -2, 0 + +-------- day 6 -------- +name, sellIn, quality ++5 Dexterity Vest, 4, 14 +Aged Brie, -4, 10 +Elixir of the Mongoose, -1, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 9, 27 +Backstage passes to a TAFKAL80ETC concert, 4, 50 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Conjured Mana Cake, -3, 0 + +-------- day 7 -------- +name, sellIn, quality ++5 Dexterity Vest, 3, 13 +Aged Brie, -5, 12 +Elixir of the Mongoose, -2, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 8, 29 +Backstage passes to a TAFKAL80ETC concert, 3, 50 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Conjured Mana Cake, -4, 0 + +-------- day 8 -------- +name, sellIn, quality ++5 Dexterity Vest, 2, 12 +Aged Brie, -6, 14 +Elixir of the Mongoose, -3, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 7, 31 +Backstage passes to a TAFKAL80ETC concert, 2, 50 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Conjured Mana Cake, -5, 0 + +-------- day 9 -------- +name, sellIn, quality ++5 Dexterity Vest, 1, 11 +Aged Brie, -7, 16 +Elixir of the Mongoose, -4, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 6, 33 +Backstage passes to a TAFKAL80ETC concert, 1, 50 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Conjured Mana Cake, -6, 0 + +-------- day 10 -------- +name, sellIn, quality ++5 Dexterity Vest, 0, 10 +Aged Brie, -8, 18 +Elixir of the Mongoose, -5, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 5, 35 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Conjured Mana Cake, -7, 0 + +-------- day 11 -------- +name, sellIn, quality ++5 Dexterity Vest, -1, 8 +Aged Brie, -9, 20 +Elixir of the Mongoose, -6, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 4, 38 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Conjured Mana Cake, -8, 0 + +-------- day 12 -------- +name, sellIn, quality ++5 Dexterity Vest, -2, 6 +Aged Brie, -10, 22 +Elixir of the Mongoose, -7, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 3, 41 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Conjured Mana Cake, -9, 0 + +-------- day 13 -------- +name, sellIn, quality ++5 Dexterity Vest, -3, 4 +Aged Brie, -11, 24 +Elixir of the Mongoose, -8, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 2, 44 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Conjured Mana Cake, -10, 0 + +-------- day 14 -------- +name, sellIn, quality ++5 Dexterity Vest, -4, 2 +Aged Brie, -12, 26 +Elixir of the Mongoose, -9, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 1, 47 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Conjured Mana Cake, -11, 0 + +-------- day 15 -------- +name, sellIn, quality ++5 Dexterity Vest, -5, 0 +Aged Brie, -13, 28 +Elixir of the Mongoose, -10, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, 0, 50 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Conjured Mana Cake, -12, 0 + +-------- day 16 -------- +name, sellIn, quality ++5 Dexterity Vest, -6, 0 +Aged Brie, -14, 30 +Elixir of the Mongoose, -11, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -1, 0 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Conjured Mana Cake, -13, 0 + +-------- day 17 -------- +name, sellIn, quality ++5 Dexterity Vest, -7, 0 +Aged Brie, -15, 32 +Elixir of the Mongoose, -12, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -2, 0 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Conjured Mana Cake, -14, 0 + +-------- day 18 -------- +name, sellIn, quality ++5 Dexterity Vest, -8, 0 +Aged Brie, -16, 34 +Elixir of the Mongoose, -13, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -3, 0 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Conjured Mana Cake, -15, 0 + +-------- day 19 -------- +name, sellIn, quality ++5 Dexterity Vest, -9, 0 +Aged Brie, -17, 36 +Elixir of the Mongoose, -14, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -4, 0 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Conjured Mana Cake, -16, 0 + +-------- day 20 -------- +name, sellIn, quality ++5 Dexterity Vest, -10, 0 +Aged Brie, -18, 38 +Elixir of the Mongoose, -15, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -5, 0 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Conjured Mana Cake, -17, 0 + +-------- day 21 -------- +name, sellIn, quality ++5 Dexterity Vest, -11, 0 +Aged Brie, -19, 40 +Elixir of the Mongoose, -16, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -6, 0 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Backstage passes to a TAFKAL80ETC concert, -16, 0 +Conjured Mana Cake, -18, 0 + +-------- day 22 -------- +name, sellIn, quality ++5 Dexterity Vest, -12, 0 +Aged Brie, -20, 42 +Elixir of the Mongoose, -17, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -7, 0 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Backstage passes to a TAFKAL80ETC concert, -17, 0 +Conjured Mana Cake, -19, 0 + +-------- day 23 -------- +name, sellIn, quality ++5 Dexterity Vest, -13, 0 +Aged Brie, -21, 44 +Elixir of the Mongoose, -18, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -8, 0 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Backstage passes to a TAFKAL80ETC concert, -18, 0 +Conjured Mana Cake, -20, 0 + +-------- day 24 -------- +name, sellIn, quality ++5 Dexterity Vest, -14, 0 +Aged Brie, -22, 46 +Elixir of the Mongoose, -19, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -9, 0 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Backstage passes to a TAFKAL80ETC concert, -19, 0 +Conjured Mana Cake, -21, 0 + +-------- day 25 -------- +name, sellIn, quality ++5 Dexterity Vest, -15, 0 +Aged Brie, -23, 48 +Elixir of the Mongoose, -20, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -10, 0 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Backstage passes to a TAFKAL80ETC concert, -20, 0 +Conjured Mana Cake, -22, 0 + +-------- day 26 -------- +name, sellIn, quality ++5 Dexterity Vest, -16, 0 +Aged Brie, -24, 50 +Elixir of the Mongoose, -21, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -11, 0 +Backstage passes to a TAFKAL80ETC concert, -16, 0 +Backstage passes to a TAFKAL80ETC concert, -21, 0 +Conjured Mana Cake, -23, 0 + +-------- day 27 -------- +name, sellIn, quality ++5 Dexterity Vest, -17, 0 +Aged Brie, -25, 50 +Elixir of the Mongoose, -22, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -12, 0 +Backstage passes to a TAFKAL80ETC concert, -17, 0 +Backstage passes to a TAFKAL80ETC concert, -22, 0 +Conjured Mana Cake, -24, 0 + +-------- day 28 -------- +name, sellIn, quality ++5 Dexterity Vest, -18, 0 +Aged Brie, -26, 50 +Elixir of the Mongoose, -23, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -13, 0 +Backstage passes to a TAFKAL80ETC concert, -18, 0 +Backstage passes to a TAFKAL80ETC concert, -23, 0 +Conjured Mana Cake, -25, 0 + +-------- day 29 -------- +name, sellIn, quality ++5 Dexterity Vest, -19, 0 +Aged Brie, -27, 50 +Elixir of the Mongoose, -24, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -14, 0 +Backstage passes to a TAFKAL80ETC concert, -19, 0 +Backstage passes to a TAFKAL80ETC concert, -24, 0 +Conjured Mana Cake, -26, 0 + +-------- day 30 -------- +name, sellIn, quality ++5 Dexterity Vest, -20, 0 +Aged Brie, -28, 50 +Elixir of the Mongoose, -25, 0 +Sulfuras, Hand of Ragnaros, 0, 80 +Sulfuras, Hand of Ragnaros, -1, 80 +Backstage passes to a TAFKAL80ETC concert, -15, 0 +Backstage passes to a TAFKAL80ETC concert, -20, 0 +Backstage passes to a TAFKAL80ETC concert, -25, 0 +Conjured Mana Cake, -27, 0 + diff --git a/texttests/config.gr b/texttests/config.gr new file mode 100755 index 00000000..b7166cd0 --- /dev/null +++ b/texttests/config.gr @@ -0,0 +1,67 @@ +full_name:Gilded Rose Refactoring Kata + +# set your preferred editor and diff tool. +view_program:subl +diff_program:meld + +# Settings for the Python version +#executable:${TEXTTEST_HOME}/python/texttest_fixture.py +#interpreter:python + +# Settings for the cpp version +#executable:${TEXTTEST_HOME}/cpp/cmake-build-debug/test/cpp_texttest/GildedRoseTextTests + +# Settings for the c (cmocka) version +#executable:${TEXTTEST_HOME}/c_cmocka/cmake-build-debug/main + +# Settings for the zig version +#executable:${TEXTTEST_HOME}/zig/zig-out/bin/zig + +# Settings for the Java version using Gradle wrapped in a python script +#executable:${TEXTTEST_HOME}/Java/texttest_rig.py +#interpreter:python + +# Settings for the Java version using the classpath +#executable:com.gildedrose.TexttestFixture +#interpreter:java +# note you'll also need to update the file environment.gr with your classpath if you keep your classfiles somewhere unusual + +# Settings for the Kotlin version using Gradle wrapped in a python script +#executable:${TEXTTEST_HOME}/Kotlin/texttest_rig.py +#interpreter:python + +# Settings for the Ruby version +executable:${TEXTTEST_HOME}/ruby/texttest_fixture.rb +interpreter:ruby + +# Settings for the C# Core version +#executable:${TEXTTEST_HOME}/csharpcore/GildedRoseTests/bin/Debug/net8.0/GildedRoseTests + +# Settings for the Perl version +#executable:${TEXTTEST_HOME}/perl/texttest_fixture.pl +#interpreter:perl + +# Settings for the TypeScript version +#executable:${TEXTTEST_HOME}/TypeScript/texttest_rig.py +#interpreter:python + +# Settings for the Javascript Jest version +#executable:${TEXTTEST_HOME}/js-jest/test/texttest_fixture.js +#interpreter:node + +# Settings for the Javascript Mocha version +#executable:${TEXTTEST_HOME}/js-mocha/test/texttest_fixture.js +#interpreter:node + +# Settings for the Julia version +#executable:${TEXTTEST_HOME}/julia/texttest_fixture.jl +#interpreter:julia + +# Settings for the PHP version +#executable:${TEXTTEST_HOME}/php/fixtures/texttest_fixture.php +#interpreter:php + +# Settings for the OCaml version +# executable:${TEXTTEST_HOME}/ocaml/_build/default/bin/main.exe + +filename_convention_scheme:standard diff --git a/texttests/environment.gr b/texttests/environment.gr new file mode 100755 index 00000000..11be7a5a --- /dev/null +++ b/texttests/environment.gr @@ -0,0 +1,3 @@ +# If your .class files are somewhere else, add the path to the list +CLASSPATH:${TEXTTEST_HOME}/Java/build/classes/java/test;${TEXTTEST_HOME}/Java/build/classes/java/main;${TEXTTEST_HOME}/Java/target/classes;${TEXTTEST_HOME}/Java/target/test-classes +PERL5OPT:-I${TEXTTEST_HOME}/perl \ No newline at end of file diff --git a/texttests/testsuite.gr b/texttests/testsuite.gr new file mode 100755 index 00000000..9dccc658 --- /dev/null +++ b/texttests/testsuite.gr @@ -0,0 +1,2 @@ +# With one example of each special item, run for 30 days updating sellIn and quality. +ThirtyDays \ No newline at end of file diff --git a/vbnet/.gitignore b/vbnet/.gitignore new file mode 100644 index 00000000..8cfd7874 --- /dev/null +++ b/vbnet/.gitignore @@ -0,0 +1,402 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +riderModule.iml +/_ReSharper.Caches/ +.idea/ \ No newline at end of file diff --git a/vbnet/GildedRose-Refactoring-Kata.sln b/vbnet/GildedRose-Refactoring-Kata.sln new file mode 100644 index 00000000..ddc4e9b0 --- /dev/null +++ b/vbnet/GildedRose-Refactoring-Kata.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "GildedRoseKata", "GildedRoseKata\GildedRoseKata.vbproj", "{FFC7A053-D8C3-4359-A440-08BED0B015F1}" +EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "GildedRoseKata.Tests", "GildedRoseKata.Tests\GildedRoseKata.Tests.vbproj", "{95A14964-D7C9-413A-95F6-FF3A29851246}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FFC7A053-D8C3-4359-A440-08BED0B015F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FFC7A053-D8C3-4359-A440-08BED0B015F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FFC7A053-D8C3-4359-A440-08BED0B015F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FFC7A053-D8C3-4359-A440-08BED0B015F1}.Release|Any CPU.Build.0 = Release|Any CPU + {95A14964-D7C9-413A-95F6-FF3A29851246}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {95A14964-D7C9-413A-95F6-FF3A29851246}.Debug|Any CPU.Build.0 = Debug|Any CPU + {95A14964-D7C9-413A-95F6-FF3A29851246}.Release|Any CPU.ActiveCfg = Release|Any CPU + {95A14964-D7C9-413A-95F6-FF3A29851246}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/vbnet/GildedRoseKata.Tests/GildedRoseKata.Tests.vbproj b/vbnet/GildedRoseKata.Tests/GildedRoseKata.Tests.vbproj new file mode 100644 index 00000000..b32765ee --- /dev/null +++ b/vbnet/GildedRoseKata.Tests/GildedRoseKata.Tests.vbproj @@ -0,0 +1,20 @@ + + + + net6.0 + + false + + + + + + + + + + + + + + diff --git a/vbnet/GildedRoseKata.Tests/GildedRoseTests.vb b/vbnet/GildedRoseKata.Tests/GildedRoseTests.vb new file mode 100644 index 00000000..7dceda87 --- /dev/null +++ b/vbnet/GildedRoseKata.Tests/GildedRoseTests.vb @@ -0,0 +1,19 @@ +Imports NUnit.Framework + +Namespace GildedRoseKata.Tests + Public Class GildedRoseTests + + Public Sub foo() + Dim Items As IList(Of Item) = New List(Of Item) From { + New Item With { + .Name = "foo", + .SellIn = 0, + .Quality = 0 + } + } + Dim app = New GildedRose(Items) + app.UpdateQuality() + Assert.AreEqual("fixme", Items(0).Name) + End Sub + End Class +End Namespace \ No newline at end of file diff --git a/vbnet/GildedRoseKata/GildedRose.vb b/vbnet/GildedRoseKata/GildedRose.vb new file mode 100644 index 00000000..df9c24a4 --- /dev/null +++ b/vbnet/GildedRoseKata/GildedRose.vb @@ -0,0 +1,73 @@ +Imports System.Collections.Generic + +Public Class GildedRose + Private Items As IList(Of Item) + + Public Sub New(items As IList(Of Item)) + Me.Items = items + End Sub + + Public Sub UpdateQuality() + For i = 0 To Items.Count - 1 + + If Items(i).Name <> "Aged Brie" AndAlso Items(i).Name <> "Backstage passes to a TAFKAL80ETC concert" Then + + If Items(i).Quality > 0 Then + + If Items(i).Name <> "Sulfuras, Hand of Ragnaros" Then + Items(i).Quality = Items(i).Quality - 1 + End If + End If + Else + + If Items(i).Quality < 50 Then + Items(i).Quality = Items(i).Quality + 1 + + If Items(i).Name = "Backstage passes to a TAFKAL80ETC concert" Then + + If Items(i).SellIn < 11 Then + + If Items(i).Quality < 50 Then + Items(i).Quality = Items(i).Quality + 1 + End If + End If + + If Items(i).SellIn < 6 Then + + If Items(i).Quality < 50 Then + Items(i).Quality = Items(i).Quality + 1 + End If + End If + End If + End If + End If + + If Items(i).Name <> "Sulfuras, Hand of Ragnaros" Then + Items(i).SellIn = Items(i).SellIn - 1 + End If + + If Items(i).SellIn < 0 Then + + If Items(i).Name <> "Aged Brie" Then + + If Items(i).Name <> "Backstage passes to a TAFKAL80ETC concert" Then + + If Items(i).Quality > 0 Then + + If Items(i).Name <> "Sulfuras, Hand of Ragnaros" Then + Items(i).Quality = Items(i).Quality - 1 + End If + End If + Else + Items(i).Quality = Items(i).Quality - Items(i).Quality + End If + Else + + If Items(i).Quality < 50 Then + Items(i).Quality = Items(i).Quality + 1 + End If + End If + End If + Next + End Sub +End Class \ No newline at end of file diff --git a/vbnet/GildedRoseKata/GildedRoseKata.vbproj b/vbnet/GildedRoseKata/GildedRoseKata.vbproj new file mode 100644 index 00000000..dbcac0ea --- /dev/null +++ b/vbnet/GildedRoseKata/GildedRoseKata.vbproj @@ -0,0 +1,9 @@ + + + + Exe + GildedRoseKata + net6.0 + + + diff --git a/vbnet/GildedRoseKata/Item.vb b/vbnet/GildedRoseKata/Item.vb new file mode 100644 index 00000000..22dfa2b7 --- /dev/null +++ b/vbnet/GildedRoseKata/Item.vb @@ -0,0 +1,9 @@ +Public Class Item + Public Property Name As String + Public Property SellIn As Integer + Public Property Quality As Integer + + Public Overrides Function ToString() As String + Return Me.Name & ", " & Me.SellIn & ", " & Me.Quality + End Function +End Class diff --git a/vbnet/GildedRoseKata/Program.vb b/vbnet/GildedRoseKata/Program.vb new file mode 100644 index 00000000..3bad359b --- /dev/null +++ b/vbnet/GildedRoseKata/Program.vb @@ -0,0 +1,67 @@ +Imports System + +Module Program + Sub Main(args As String()) + Console.WriteLine("OMGHAI!") + Dim Items As IList(Of Item) = New List(Of Item) From { + New Item With { + .Name = "+5 Dexterity Vest", + .SellIn = 10, + .Quality = 20 + }, + New Item With { + .Name = "Aged Brie", + .SellIn = 2, + .Quality = 0 + }, + New Item With { + .Name = "Elixir of the Mongoose", + .SellIn = 5, + .Quality = 7 + }, + New Item With { + .Name = "Sulfuras, Hand of Ragnaros", + .SellIn = 0, + .Quality = 80 + }, + New Item With { + .Name = "Sulfuras, Hand of Ragnaros", + .SellIn = - 1, + .Quality = 80 + }, + New Item With { + .Name = "Backstage passes to a TAFKAL80ETC concert", + .SellIn = 15, + .Quality = 20 + }, + New Item With { + .Name = "Backstage passes to a TAFKAL80ETC concert", + .SellIn = 10, + .Quality = 49 + }, + New Item With { + .Name = "Backstage passes to a TAFKAL80ETC concert", + .SellIn = 5, + .Quality = 49 + }, + New Item With { + .Name = "Conjured Mana Cake", + .SellIn = 3, + .Quality = 6 + } + } + Dim app = New GildedRose(Items) + + For i = 0 To 31 - 1 + Console.WriteLine("-------- day " & i & " --------") + Console.WriteLine("name, sellIn, quality") + + For j = 0 To Items.Count - 1 + System.Console.WriteLine(Items(j)) + Next + + Console.WriteLine("") + app.UpdateQuality() + Next + End Sub +End Module diff --git a/xslt/.gitignore b/xslt/.gitignore new file mode 100644 index 00000000..d1a79349 --- /dev/null +++ b/xslt/.gitignore @@ -0,0 +1,3 @@ +*.html +*.test_result.xml +*.next_day.xml diff --git a/xslt/README.md b/xslt/README.md new file mode 100644 index 00000000..d06ce2ce --- /dev/null +++ b/xslt/README.md @@ -0,0 +1,19 @@ +# XSLT port of the Gilded-Rose Kata + +This is a XSLT 1.0 port of the *Gilded-Rose-Kata*. + +## Building and Running + +* [Apache Ant's XSLT task](https://ant.apache.org/manual/Tasks/style.html) is used to run the transformations. +* `update_quality.xsl` contains the Gilded Rose logic. +* Run Ant in the current folder to transform all files. +* `texttest_fixture.xml` is transformed into `texttest_fixture.next_day.xml` with updated values. + +## Unit Test + +[xsltunit](http://xsltunit.org/) is a implementation of xUnit in XSLT. + +* `tst_update_quality.xsl`is the the test (stylesheet). +* Run Ant in the current folder to run the tests. +* `update_quality.test_result.xml` contains the results and + `update_quality.test_result.html` is a readable report. diff --git a/xslt/build.xml b/xslt/build.xml new file mode 100644 index 00000000..98eaa2ff --- /dev/null +++ b/xslt/build.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xslt/gilded_rose.dtd b/xslt/gilded_rose.dtd new file mode 100644 index 00000000..2967f30d --- /dev/null +++ b/xslt/gilded_rose.dtd @@ -0,0 +1,8 @@ + + + + + + diff --git a/xslt/texttest_fixture.xml b/xslt/texttest_fixture.xml new file mode 100644 index 00000000..ae15fcf1 --- /dev/null +++ b/xslt/texttest_fixture.xml @@ -0,0 +1,18 @@ + + + + OMGHAI! + 0 + + + + + + + + + + + + + diff --git a/xslt/tst_update_quality.xsl b/xslt/tst_update_quality.xsl new file mode 100644 index 00000000..99834ade --- /dev/null +++ b/xslt/tst_update_quality.xsl @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + OMGHAI! + + + + + + OMGHAI! + + + + + + + + + + OMGHAI! + + + + + + 12 + + + + + + + + + + 13 + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + fixme + + + + + + diff --git a/xslt/update_quality.xsl b/xslt/update_quality.xsl new file mode 100644 index 00000000..f98a5baf --- /dev/null +++ b/xslt/update_quality.xsl @@ -0,0 +1,141 @@ + + + + + + +<!DOCTYPE gildedrose SYSTEM "gilded_rose.dtd"> + + + + + OMGHAI! + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + - + + + + + + -0+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xslt/xsltunit-0.2/xsltunit.xsl b/xslt/xsltunit-0.2/xsltunit.xsl new file mode 100644 index 00000000..447b2f92 --- /dev/null +++ b/xslt/xsltunit-0.2/xsltunit.xsl @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Should have been different! + + + + + + + + + + passed + + + failed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xslt/xsltunit-0.2/xsltunit_report.xsl b/xslt/xsltunit-0.2/xsltunit_report.xsl new file mode 100644 index 00000000..fe504d42 --- /dev/null +++ b/xslt/xsltunit-0.2/xsltunit_report.xsl @@ -0,0 +1,55 @@ + + + + + + + + + All Tests + + +

    Tests Stylesheet

    + + + +
    + + +

    Test

    +
      + +
    +
    + + +
  • + Assert ... + + + Passed + + + Failed + + + +
  • + + + + + + + + + + + + + + +
    Expected:
    Actual:
    +
    + + diff --git a/zig/.gitignore b/zig/.gitignore new file mode 100644 index 00000000..7dc0ebe5 --- /dev/null +++ b/zig/.gitignore @@ -0,0 +1,13 @@ +# Created by https://www.toptal.com/developers/gitignore/api/zig +# Edit at https://www.toptal.com/developers/gitignore?templates=zig + +### zig ### +# Zig programming language + +zig-cache/ +zig-out/ +build/ +build-*/ +docgen_tmp/ + +# End of https://www.toptal.com/developers/gitignore/api/zig diff --git a/zig/README.md b/zig/README.md new file mode 100644 index 00000000..f0e22897 --- /dev/null +++ b/zig/README.md @@ -0,0 +1,34 @@ +# Gilded Rose starting position in Zig + +I assume you have [installed](https://ziglang.org/learn/getting-started/#installing-zig) `zig` on your system. + +## Run unit tests from the command line + +```sh +$ zig build test +``` + +## Run the TextTest fixture on the command line + +Build and install the executable + +```sh +$ zig build +``` + +Execute it on the command line with an argument for the number of days: + +```sh +$ ./zig/zig-out/bin/zig 10 +``` + +## Run the TextTest approval test that comes with this project + +There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. +You will need to specify the executable in [config.gr](../texttests/config.gr). +Uncomment this line to use it: + +``` +#executable:${TEXTTEST_HOME}/zig/zig-out/bin/zig +``` + diff --git a/zig/build.zig b/zig/build.zig new file mode 100644 index 00000000..21ecdf84 --- /dev/null +++ b/zig/build.zig @@ -0,0 +1,68 @@ +const std = @import("std"); + +// Although this function looks imperative, note that its job is to +// declaratively construct a build graph that will be executed by an external +// runner. +pub fn build(b: *std.Build) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "zig", + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + // This declares intent for the executable to be installed into the + // standard location when the user invokes the "install" step (the default + // step when running `zig build`). + b.installArtifact(exe); + + // This *creates* a Run step in the build graph, to be executed when another + // step is evaluated that depends on it. The next line below will establish + // such a dependency. + const run_cmd = b.addRunArtifact(exe); + + // By making the run step depend on the install step, it will be run from the + // installation directory rather than directly from within the cache directory. + // This is not necessary, however, if the application depends on other installed + // files, this ensures they will be present and in the expected location. + run_cmd.step.dependOn(b.getInstallStep()); + + // This allows the user to pass arguments to the application in the build + // command itself, like this: `zig build run -- arg1 arg2 etc` + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // This creates a build step. It will be visible in the `zig build --help` menu, + // and can be selected like this: `zig build run` + // This will evaluate the `run` step rather than the default, which is "install". + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + // Creates a step for unit testing. This only builds the test executable + // but does not run it. + const lib_unit_tests = b.addTest(.{ + .root_source_file = b.path("src/gilded_rose.zig"), + .target = target, + .optimize = optimize, + }); + + const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); + + // Similar to creating the run step earlier, this exposes a `test` step to + // the `zig build --help` menu, providing a way for the user to request + // running the unit tests. + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_lib_unit_tests.step); +} diff --git a/zig/src/gilded_rose.zig b/zig/src/gilded_rose.zig new file mode 100644 index 00000000..297aeab6 --- /dev/null +++ b/zig/src/gilded_rose.zig @@ -0,0 +1,79 @@ +const std = @import("std"); + +pub const Item = struct { + name: []const u8, + sell_in: i32, + quality: i32, + + pub fn init(name: []const u8, sell_in: i32, quality: i32) Item { + return Item{ + .name = name, + .sell_in = sell_in, + .quality = quality, + }; + } +}; + +pub const GildedRose = struct { + items: []Item, + + pub fn init(items: []Item) GildedRose { + return GildedRose{ .items = items }; + } + + pub fn updateQuality(self: *GildedRose) []Item { + for (0..self.items.len) |i| { + if (!std.mem.eql(u8, self.items[i].name, "Aged Brie") and !std.mem.eql(u8, self.items[i].name, "Backstage passes to a TAFKAL80ETC concert")) { + if (self.items[i].quality > 0) { + if (!std.mem.eql(u8, self.items[i].name, "Sulfuras, Hand of Ragnaros")) { + self.items[i].quality = self.items[i].quality - 1; + } + } + } else { + if (self.items[i].quality < 50) { + self.items[i].quality = self.items[i].quality + 1; + if (std.mem.eql(u8, self.items[i].name, "Backstage passes to a TAFKAL80ETC concert")) { + if (self.items[i].sell_in < 11) { + if (self.items[i].quality < 50) { + self.items[i].quality = self.items[i].quality + 1; + } + } + if (self.items[i].sell_in < 6) { + if (self.items[i].quality < 50) { + self.items[i].quality = self.items[i].quality + 1; + } + } + } + } + } + if (!std.mem.eql(u8, self.items[i].name, "Sulfuras, Hand of Ragnaros")) { + self.items[i].sell_in = self.items[i].sell_in - 1; + } + if (self.items[i].sell_in < 0) { + if (!std.mem.eql(u8, self.items[i].name, "Aged Brie")) { + if (!std.mem.eql(u8, self.items[i].name, "Backstage passes to a TAFKAL80ETC concert")) { + if (self.items[i].quality > 0) { + if (!std.mem.eql(u8, self.items[i].name, "Sulfuras, Hand of Ragnaros")) { + self.items[i].quality = self.items[i].quality - 1; + } + } + } else { + self.items[i].quality = self.items[i].quality - self.items[i].quality; + } + } else { + if (self.items[i].quality < 50) { + self.items[i].quality = self.items[i].quality + 1; + } + } + } + } + return self.items; + } +}; + +test "updateQuality" { + var items = [_]Item{Item.init("foo", 0, 0)}; + var app = GildedRose.init(&items); + _ = app.updateQuality(); + try std.testing.expectEqualStrings("fixme", items[0].name); +} diff --git a/zig/src/main.zig b/zig/src/main.zig new file mode 100644 index 00000000..4d99ea22 --- /dev/null +++ b/zig/src/main.zig @@ -0,0 +1,43 @@ +const std = @import("std"); +const gilded_rose = @import("gilded_rose.zig"); +const Item = gilded_rose.Item; +const GildedRose = gilded_rose.GildedRose; + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + const args = try std.process.argsAlloc(allocator); + var days: u8 = 2; + if (args.len >= 2) { + days = try std.fmt.parseInt(u8, args[1], 10); + } + + var items = [_]Item{ + Item.init("+5 Dexterity Vest", 10, 20), + Item.init("Aged Brie", 2, 0), + Item.init("Elixir of the Mongoose", 5, 7), + Item.init("Sulfuras, Hand of Ragnaros", 0, 80), + Item.init("Sulfuras, Hand of Ragnaros", -1, 80), + Item.init("Backstage passes to a TAFKAL80ETC concert", 15, 20), + Item.init("Backstage passes to a TAFKAL80ETC concert", 10, 49), + Item.init("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this Conjured item doesn't yet work properly + Item.init("Conjured Mana Cake", 3, 6), + }; + var app = GildedRose.init(&items); + + const stdout = std.io.getStdOut().writer(); + try stdout.print("OMGHAI!\n", .{}); + + for (0..days + 1) |day| { + try stdout.print("-------- day {d} --------\n", .{day}); + try stdout.print("name, sellIn, quality\n", .{}); + for (items) |item| { + try stdout.print("{s}, {d}, {d}\n", .{ item.name, item.sell_in, item.quality }); + } + try stdout.print("\n", .{}); + _ = app.updateQuality(); + } +}