mirror of
https://github.com/emilybache/GildedRose-Refactoring-Kata.git
synced 2026-02-15 22:41:30 +00:00
Implemented ConcertPass, AgedCheese, and LegendaryItem, along with better test output
This commit is contained in:
parent
278e67ae75
commit
0abd370fd1
13
js-jest/package-lock.json
generated
13
js-jest/package-lock.json
generated
@ -3917,6 +3917,14 @@
|
|||||||
"restore-cursor": "^3.1.0"
|
"restore-cursor": "^3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cli-table": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz",
|
||||||
|
"integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=",
|
||||||
|
"requires": {
|
||||||
|
"colors": "1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"cli-width": {
|
"cli-width": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
|
||||||
@ -3965,6 +3973,11 @@
|
|||||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"colors": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs="
|
||||||
|
},
|
||||||
"combined-stream": {
|
"combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
|||||||
@ -49,5 +49,8 @@
|
|||||||
"ignore": [
|
"ignore": [
|
||||||
"node_modules"
|
"node_modules"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cli-table": "^0.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
|
const AGED_CHEESE = ['Aged Brie']
|
||||||
|
const CONCERT_PASS = ['Backstage passes to a TAFKAL80ETC concert']
|
||||||
|
const LEGENDARY_ITEMS = ['Sulfuras, Hand of Ragnaros']
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do not alter the Item class or Items property as those belong to the goblin in the corner who
|
* "(...) do not alter the Item class or Items property as those belong to the goblin in the corner
|
||||||
* will insta-rage and one-shot you as he doesn't believe in shared code ownership
|
* who will insta-rage and one-shot you as he doesn't believe in shared code ownership"
|
||||||
*/
|
*/
|
||||||
export class Item {
|
export class Item {
|
||||||
constructor (name, sellIn, quality) {
|
constructor (name, sellIn, quality) {
|
||||||
@ -19,32 +22,55 @@ export class RegularItem extends Item {
|
|||||||
this.depreciationRate = 1
|
this.depreciationRate = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isNameValid (name) {
|
||||||
|
return typeof name === 'string' && name.length
|
||||||
|
}
|
||||||
|
|
||||||
|
isSellInValid (sellIn) {
|
||||||
|
return typeof sellIn === 'number'
|
||||||
|
}
|
||||||
|
|
||||||
|
isQualityValid (quality) {
|
||||||
|
// "The Quality of an item is never negative"
|
||||||
|
// "The Quality of an item is never more than 50"
|
||||||
|
return typeof quality === 'number' && quality >= 0 && quality <= 50
|
||||||
|
}
|
||||||
|
|
||||||
validateItemProps ({ name, sellIn, quality }) {
|
validateItemProps ({ name, sellIn, quality }) {
|
||||||
const errors = []
|
const errors = []
|
||||||
|
|
||||||
const isNameValid = typeof name === 'string' && name.length
|
!this.isNameValid(name) && errors.push('"name" must be a valid, non-empty string')
|
||||||
const isSellInValid = typeof sellIn === 'number'
|
!this.isSellInValid(sellIn) && errors.push('"sellIn" must be an integer')
|
||||||
// "The Quality of an item is never negative"
|
!this.isQualityValid(quality) && errors.push('"quality" must be an integer, between 0 and 50')
|
||||||
// "The Quality of an item is never more than 50"
|
|
||||||
const isQualityValid = typeof quality === 'number' && quality >= 0 && quality <= 50
|
|
||||||
|
|
||||||
!isNameValid && errors.push('"name" must be a valid, non-empty string')
|
|
||||||
!isSellInValid && errors.push('"sellIn" must be an integer')
|
|
||||||
!isQualityValid && errors.push('"qualityValid" must be an integer, between 0 and 50')
|
|
||||||
|
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
throw new Error(`[RegularItem.validateItemProps] Invalid itemProps passed to the constructor: ${errors.join(', ')}`)
|
throw new Error(`[RegularItem.validateItemProps] Invalid itemProps passed to the constructor: ${errors.join(', ')}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateQuality () {
|
/**
|
||||||
// "Once the sell by date has passed, Quality degrades twice as fast"
|
* "Once the sell by date has passed, Quality degrades twice as fast"
|
||||||
const adjustedDepreciationRate = this.sellIn < 0
|
*/
|
||||||
? this.depreciationRate * 2
|
getDepreciationRate () {
|
||||||
: this.depreciationRate
|
return this.sellIn < 0 ? this.depreciationRate * 2 : this.depreciationRate
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "The Quality of an item is never more than 50"
|
||||||
|
*
|
||||||
|
* @param {Number} value
|
||||||
|
*/
|
||||||
|
setQuality (value) {
|
||||||
|
if (value > 50) {
|
||||||
|
this.quality = 50
|
||||||
|
} else {
|
||||||
|
this.quality = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateQuality () {
|
||||||
// "The Quality of an item is never negative"
|
// "The Quality of an item is never negative"
|
||||||
this.quality = Math.max(0, this.quality - adjustedDepreciationRate)
|
this.setQuality(Math.max(0, this.quality - this.getDepreciationRate()))
|
||||||
|
|
||||||
// Assuming updateQuality is called only once a day...
|
// Assuming updateQuality is called only once a day...
|
||||||
// "At the end of each day our system lowers both values [quality and sellIn] (...)"
|
// "At the end of each day our system lowers both values [quality and sellIn] (...)"
|
||||||
@ -52,8 +78,76 @@ export class RegularItem extends Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ConcertPass extends RegularItem {
|
||||||
|
constructor (itemProps) {
|
||||||
|
super(itemProps)
|
||||||
|
|
||||||
|
// "Backstage passes", like aged brie, increases in Quality as its SellIn value approaches
|
||||||
|
this.depreciationRate = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
getDepreciationRate () {
|
||||||
|
// "Quality drops to 0 after the concert"
|
||||||
|
if (this.sellIn < 0) {
|
||||||
|
return this.quality
|
||||||
|
}
|
||||||
|
|
||||||
|
// [Quality increases] by 3 when there are 5 days or less"
|
||||||
|
if (this.sellIn <= 5) {
|
||||||
|
return this.depreciationRate * 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// "[and] by 2 when there are 10 days or less"
|
||||||
|
if (this.sellIn <= 10) {
|
||||||
|
return this.depreciationRate * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.depreciationRate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AgedCheese extends RegularItem {
|
||||||
|
constructor (itemProps) {
|
||||||
|
super(itemProps)
|
||||||
|
|
||||||
|
// "Aged Brie" actually increases in Quality the older it gets
|
||||||
|
this.depreciationRate = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
getDepreciationRate () {
|
||||||
|
return this.sellIn <= 0 ? this.depreciationRate * 2 : this.depreciationRate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LegendaryItem extends RegularItem {
|
||||||
|
constructor (itemProps) {
|
||||||
|
super(itemProps)
|
||||||
|
this.depreciationRate = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "(...) an item can never have its Quality increase above 50, however (...) a legendary
|
||||||
|
* item['s quality] is 80 and it never alters."
|
||||||
|
*
|
||||||
|
* @param {Number} quality
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
isQualityValid (quality) {
|
||||||
|
return typeof quality === 'number' && quality === 80
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Sulfuras", being a legendary item, never has to be sold or decreases in Quality
|
||||||
|
*/
|
||||||
|
updateQuality () {}
|
||||||
|
}
|
||||||
|
|
||||||
export class Shop {
|
export class Shop {
|
||||||
constructor (items = []) {
|
constructor (items = []) {
|
||||||
|
/*
|
||||||
|
* "(...)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"
|
||||||
|
*/
|
||||||
this.items = items
|
this.items = items
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +164,7 @@ export class Shop {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.items[i].quality < 50) {
|
if (this.items[i].quality < 50) {
|
||||||
// Increment quality by 1 for all non RegularItems
|
// Increment quality by 1 for all non RegularItems (cheese and concert pass)
|
||||||
this.items[i].quality = this.items[i].quality + 1
|
this.items[i].quality = this.items[i].quality + 1
|
||||||
|
|
||||||
if (this.items[i].name === 'Backstage passes to a TAFKAL80ETC concert') {
|
if (this.items[i].name === 'Backstage passes to a TAFKAL80ETC concert') {
|
||||||
@ -120,3 +214,31 @@ export class Shop {
|
|||||||
return this.items
|
return this.items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ShopV2 extends Shop {
|
||||||
|
constructor (items = []) {
|
||||||
|
// Copying the items fulfills the constraint of not mutating the Items list, and adds new
|
||||||
|
// functionality to each item according to the provided requirements.
|
||||||
|
const itemsV2 = items.map(({ name, sellIn, quality }) => {
|
||||||
|
let ItemClass = RegularItem
|
||||||
|
|
||||||
|
// Special Items
|
||||||
|
if (LEGENDARY_ITEMS.indexOf(name) !== -1) {
|
||||||
|
ItemClass = LegendaryItem
|
||||||
|
} else if (AGED_CHEESE.indexOf(name) !== -1) {
|
||||||
|
ItemClass = AgedCheese
|
||||||
|
} else if (CONCERT_PASS.indexOf(name) !== -1) {
|
||||||
|
ItemClass = ConcertPass
|
||||||
|
}
|
||||||
|
return new ItemClass({ name, sellIn, quality })
|
||||||
|
})
|
||||||
|
|
||||||
|
super(itemsV2)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateQuality () {
|
||||||
|
this.items.forEach((item) => {
|
||||||
|
item.updateQuality()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
|
|
||||||
import { Shop, Item } from '../src/gilded_rose'
|
import Table from 'cli-table'
|
||||||
|
import { Shop, ShopV2, Item } from '../src/gilded_rose'
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "(...)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"
|
||||||
|
*/
|
||||||
const items = [
|
const items = [
|
||||||
new Item('+5 Dexterity Vest', 10, 20),
|
new Item('+5 Dexterity Vest', 10, 20),
|
||||||
new Item('Aged Brie', 2, 0),
|
new Item('Aged Brie', 2, 0),
|
||||||
@ -17,11 +22,24 @@ const items = [
|
|||||||
|
|
||||||
const days = Number(process.argv[2]) || 2
|
const days = Number(process.argv[2]) || 2
|
||||||
const gildedRose = new Shop(items)
|
const gildedRose = new Shop(items)
|
||||||
|
const gildedRoseV2 = new ShopV2(items)
|
||||||
|
|
||||||
console.log('OMGHAI!')
|
|
||||||
for (let day = 0; day < days; day++) {
|
for (let day = 0; day < days; day++) {
|
||||||
console.log(`\n-------- day ${day} --------`)
|
const shopTable = new Table({
|
||||||
console.log('name, sellIn, quality')
|
head: ['Name', 'Sell In (v1)', 'Quality (v1)', 'Sell In (v2)', 'Quality (v2)'],
|
||||||
items.forEach(item => console.log(`${item.name}, ${item.sellIn}, ${item.quality}`))
|
colWidths: [50, 15, 15, 15, 15]
|
||||||
|
})
|
||||||
|
|
||||||
|
shopTable.push(...items.map(({ name, sellIn, quality }, index) => {
|
||||||
|
const {
|
||||||
|
sellIn: sellInV2,
|
||||||
|
quality: qualityV2
|
||||||
|
} = gildedRoseV2.items[index]
|
||||||
|
return [name, sellIn, quality, sellInV2, qualityV2]
|
||||||
|
}))
|
||||||
|
|
||||||
|
console.log(`\n-------- Day ${day} --------`)
|
||||||
|
console.log(shopTable.toString())
|
||||||
gildedRose.updateQuality()
|
gildedRose.updateQuality()
|
||||||
|
gildedRoseV2.updateQuality()
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user