Merge branch 'develop' into KristianTashkov-nvm-in-vagrant-develop

This commit is contained in:
Blade Barringer 2016-01-18 06:47:17 -06:00
commit 5b0d3be444
157 changed files with 6982 additions and 6151 deletions

1
.gitignore vendored
View file

@ -27,5 +27,6 @@ website/public/docs
coverage
coverage.html
common/dist/scripts/*
common/dist/sprites/habitrpg-shared.css
test/spec/mocks/translations.js

View file

@ -8,7 +8,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provider "virtualbox" do |v|
v.memory = 768
v.cpus = 1
v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/vagrant", "1"]
end
config.vm.box = "thepeopleseason/habitrpg"
config.ssh.forward_agent = true

File diff suppressed because one or more lines are too long

View file

@ -18,7 +18,7 @@
}
.2014_Fall_Warrior_PROMO {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -452px -417px;
background-position: -306px -402px;
width: 90px;
height: 90px;
}
@ -54,7 +54,7 @@
}
.promo_dilatoryDistress {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -91px -1085px;
background-position: -364px -1085px;
width: 90px;
height: 90px;
}
@ -78,7 +78,7 @@
}
.promo_enchanted_armoire_201509 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -364px -1085px;
background-position: -91px -1085px;
width: 90px;
height: 90px;
}
@ -90,7 +90,7 @@
}
.promo_enchanted_armoire_201601 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -306px -402px;
background-position: -452px -417px;
width: 90px;
height: 90px;
}
@ -318,13 +318,13 @@
}
.promo_springclasses2014 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -648px -724px;
background-position: -1119px 0px;
width: 288px;
height: 90px;
}
.promo_springclasses2015 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -1119px 0px;
background-position: -648px -724px;
width: 288px;
height: 90px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 213 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

After

Width:  |  Height:  |  Size: 274 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 61 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 152 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 98 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 142 KiB

View file

@ -858,13 +858,13 @@
}
.headAccessory_mystery_301405 {
background-image: url(spritesmith-main-4.png);
background-position: -910px -1061px;
background-position: -1001px -1061px;
width: 90px;
height: 90px;
}
.head_mystery_301405 {
background-image: url(spritesmith-main-4.png);
background-position: -1001px -1061px;
background-position: -910px -1061px;
width: 90px;
height: 90px;
}
@ -882,13 +882,13 @@
}
.shop_headAccessory_mystery_301405 {
background-image: url(spritesmith-main-4.png);
background-position: -1066px -1516px;
background-position: -1025px -1516px;
width: 40px;
height: 40px;
}
.shop_head_mystery_301405 {
background-image: url(spritesmith-main-4.png);
background-position: -1025px -1516px;
background-position: -1066px -1516px;
width: 40px;
height: 40px;
}
@ -948,97 +948,97 @@
}
.headAccessory_special_spring2015Healer {
background-image: url(spritesmith-main-4.png);
background-position: 0px -1152px;
background-position: -728px -1152px;
width: 90px;
height: 90px;
}
.headAccessory_special_spring2015Mage {
background-image: url(spritesmith-main-4.png);
background-position: -91px -1152px;
background-position: -819px -1152px;
width: 90px;
height: 90px;
}
.headAccessory_special_spring2015Rogue {
background-image: url(spritesmith-main-4.png);
background-position: -182px -1152px;
background-position: -910px -1152px;
width: 90px;
height: 90px;
}
.headAccessory_special_spring2015Warrior {
background-image: url(spritesmith-main-4.png);
background-position: -273px -1152px;
background-position: -1001px -1152px;
width: 90px;
height: 90px;
}
.headAccessory_special_springHealer {
background-image: url(spritesmith-main-4.png);
background-position: -364px -1152px;
background-position: -1092px -1152px;
width: 90px;
height: 90px;
}
.headAccessory_special_springMage {
background-image: url(spritesmith-main-4.png);
background-position: -455px -1152px;
background-position: -1183px -1152px;
width: 90px;
height: 90px;
}
.headAccessory_special_springRogue {
background-image: url(spritesmith-main-4.png);
background-position: -546px -1152px;
background-position: -1300px 0px;
width: 90px;
height: 90px;
}
.headAccessory_special_springWarrior {
background-image: url(spritesmith-main-4.png);
background-position: -637px -1152px;
background-position: -1300px -91px;
width: 90px;
height: 90px;
}
.head_special_spring2015Healer {
background-image: url(spritesmith-main-4.png);
background-position: -728px -1152px;
background-position: 0px -1152px;
width: 90px;
height: 90px;
}
.head_special_spring2015Mage {
background-image: url(spritesmith-main-4.png);
background-position: -819px -1152px;
background-position: -91px -1152px;
width: 90px;
height: 90px;
}
.head_special_spring2015Rogue {
background-image: url(spritesmith-main-4.png);
background-position: -910px -1152px;
background-position: -182px -1152px;
width: 90px;
height: 90px;
}
.head_special_spring2015Warrior {
background-image: url(spritesmith-main-4.png);
background-position: -1001px -1152px;
background-position: -273px -1152px;
width: 90px;
height: 90px;
}
.head_special_springHealer {
background-image: url(spritesmith-main-4.png);
background-position: -1092px -1152px;
background-position: -364px -1152px;
width: 90px;
height: 90px;
}
.head_special_springMage {
background-image: url(spritesmith-main-4.png);
background-position: -1183px -1152px;
background-position: -455px -1152px;
width: 90px;
height: 90px;
}
.head_special_springRogue {
background-image: url(spritesmith-main-4.png);
background-position: -1300px 0px;
background-position: -546px -1152px;
width: 90px;
height: 90px;
}
.head_special_springWarrior {
background-image: url(spritesmith-main-4.png);
background-position: -1300px -91px;
background-position: -637px -1152px;
width: 90px;
height: 90px;
}
@ -1128,97 +1128,97 @@
}
.shop_headAccessory_special_spring2015Healer {
background-image: url(spritesmith-main-4.png);
background-position: -615px -1516px;
background-position: -287px -1516px;
width: 40px;
height: 40px;
}
.shop_headAccessory_special_spring2015Mage {
background-image: url(spritesmith-main-4.png);
background-position: -574px -1516px;
background-position: -246px -1516px;
width: 40px;
height: 40px;
}
.shop_headAccessory_special_spring2015Rogue {
background-image: url(spritesmith-main-4.png);
background-position: -533px -1516px;
background-position: -205px -1516px;
width: 40px;
height: 40px;
}
.shop_headAccessory_special_spring2015Warrior {
background-image: url(spritesmith-main-4.png);
background-position: -492px -1516px;
background-position: -164px -1516px;
width: 40px;
height: 40px;
}
.shop_headAccessory_special_springHealer {
background-image: url(spritesmith-main-4.png);
background-position: -451px -1516px;
background-position: -123px -1516px;
width: 40px;
height: 40px;
}
.shop_headAccessory_special_springMage {
background-image: url(spritesmith-main-4.png);
background-position: -410px -1516px;
background-position: -82px -1516px;
width: 40px;
height: 40px;
}
.shop_headAccessory_special_springRogue {
background-image: url(spritesmith-main-4.png);
background-position: -369px -1516px;
background-position: -41px -1516px;
width: 40px;
height: 40px;
}
.shop_headAccessory_special_springWarrior {
background-image: url(spritesmith-main-4.png);
background-position: -328px -1516px;
background-position: 0px -1516px;
width: 40px;
height: 40px;
}
.shop_head_special_spring2015Healer {
background-image: url(spritesmith-main-4.png);
background-position: -287px -1516px;
background-position: -615px -1516px;
width: 40px;
height: 40px;
}
.shop_head_special_spring2015Mage {
background-image: url(spritesmith-main-4.png);
background-position: -246px -1516px;
background-position: -574px -1516px;
width: 40px;
height: 40px;
}
.shop_head_special_spring2015Rogue {
background-image: url(spritesmith-main-4.png);
background-position: -205px -1516px;
background-position: -533px -1516px;
width: 40px;
height: 40px;
}
.shop_head_special_spring2015Warrior {
background-image: url(spritesmith-main-4.png);
background-position: -164px -1516px;
background-position: -492px -1516px;
width: 40px;
height: 40px;
}
.shop_head_special_springHealer {
background-image: url(spritesmith-main-4.png);
background-position: -123px -1516px;
background-position: -451px -1516px;
width: 40px;
height: 40px;
}
.shop_head_special_springMage {
background-image: url(spritesmith-main-4.png);
background-position: -82px -1516px;
background-position: -410px -1516px;
width: 40px;
height: 40px;
}
.shop_head_special_springRogue {
background-image: url(spritesmith-main-4.png);
background-position: -41px -1516px;
background-position: -369px -1516px;
width: 40px;
height: 40px;
}
.shop_head_special_springWarrior {
background-image: url(spritesmith-main-4.png);
background-position: 0px -1516px;
background-position: -328px -1516px;
width: 40px;
height: 40px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 129 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 KiB

After

Width:  |  Height:  |  Size: 370 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 175 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 134 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 145 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -125,8 +125,9 @@
"mageText": "Mages learn swiftly, gaining Experience and Levels faster than other classes. They also get a great deal of Mana for using special abilities. Play a Mage if you enjoy the tactical game aspects of Habit, or if you are strongly motivated by leveling up and unlocking advanced features!",
"rogueText": "Rogues love to accumulate wealth, gaining more Gold than anyone else, and are adept at finding random items. Their iconic Stealth ability lets them duck the consequences of missed Dailies. Play a Rogue if you find strong motivation from Rewards and Achievements, striving for loot and badges!",
"healerText": "Healers stand impervious against harm, and extend that protection to others. Missed Dailies and bad Habits don't faze them much, and they have ways to recover Health from failure. Play a Healer if you enjoy assisting others in your Party, or if the idea of cheating Death through hard work inspires you!",
"optOut": "Opt Out",
"optOutText": "Can't be bothered with classes? Want to choose later? Opt out - you'll be a warrior with no special abilities. You can read about the class system later on the wiki and enable classes at any time under User -> Stats.",
"optOutOfClasses": "Opt Out",
"optOutOfPMs": "Opt Out",
"optOutOfClassesText": "Can't be bothered with classes? Want to choose later? Opt out - you'll be a warrior with no special abilities. You can read about the class system later on the wiki and enable classes at any time under User -> Stats.",
"select": "Select",
"stealth": "Stealth",
"stealthNewDay": "When a new day begins, you will avoid damage from this many missed Dailies.",

View file

@ -147,6 +147,10 @@
"questEggUnicornMountText": "Winged Unicorn",
"questEggUnicornAdjective": "a magical",
"questEggSabretoothText": "Sabretooth Tiger",
"questEggSabretoothMountText": "Sabretooth Tiger",
"questEggSabretoothAdjective": "a ferocious",
"eggNotes": "Find a hatching potion to pour on this egg, and it will hatch into <%= eggAdjective(locale) %> <%= eggText(locale) %>.",
"hatchingPotionBase": "Base",

View file

@ -0,0 +1,13 @@
{
"merch" : "Merchandise",
"merch-teespring-summary" : "Teespring is a platform that makes it easy for anyone to create and sell high-quality products people love, with no cost or risk.",
"merch-teespring-goto" : "Get a Habitica T-shirt",
"merch-teespring-eu-summary" : "EUROPEAN VERSION : Teespring is a platform that makes it easy for anyone to create and sell high-quality products people love, with no cost or risk.",
"merch-teespring-eu-goto" : "Get a Habitica T-shirt (EU)",
"merch-stickermule-summary" : "Stick proud Melior wherever you (or someone else) need a reminder of both present and future accomplishments!",
"merch-stickermule-goto" : "Get Habitica stickers"
}

View file

@ -16,11 +16,12 @@
"displayPotionForGold": "Do you want to sell a <strong><%= itemType %> Potion</strong>?",
"sellForGold": "Sell it for <%= gold %> Gold",
"buyGems": "Buy Gems",
"purchaseGems": "Purchase Gems",
"justin": "Justin",
"ian": "Ian",
"ianText": "Welcome to the Quest Shop! Here you can use Quest Scrolls to battle monsters with your friends. Be sure to check out our fine array of Quest Scrolls for purchase on the right!",
"ianBrokenText": "Welcome to the Quest Shop... Here you can use Quest Scrolls to battle monsters with your friends... Be sure to check out our fine array of Quest Scrolls for purchase on the right...",
"USD": "USD",
"USD": "(USD)",
"newStuff": "New Stuff",
"cool": "Tell Me Later",
"dismissAlert": "Dismiss This Alert",
@ -30,9 +31,9 @@
"donationDesc": "20 Gems, Donation to Habitica",
"payWithCard": "Pay with Card",
"payNote": "Note: PayPal sometimes takes a long time to clear. We recommend paying with card.",
"card": "Card",
"card": "Credit Card (using Stripe)",
"amazonInstructions": "Click the button to pay using Amazon Payments",
"paymentMethods": "Payment Methods:",
"paymentMethods": "Purchase using",
"classGear": "Class Gear",
"classGearText": "First: don't panic! Your old gear is in your inventory, and you're now wearing your apprentice <strong><%= klass %></strong> equipment. Wearing your class's gear grants you a 50% bonus to stats. However, feel free to switch back to your old gear.",

View file

@ -1,13 +1,13 @@
{
"questEvilSantaText": "Trapper Santa",
"questEvilSantaNotes": "You hear moaning roars deep in the icefields. You follow the roars and growls - punctuated by another voice's cackling - to a clearing in the woods where you see a fully-grown polar bear. She's caged and shackled, roaring for life. Dancing atop the cage is a malicious little imp wearing castaway Christmas costumes. Vanquish Trapper Santa, and save the beast!",
"questEvilSantaCompletion": "Trapper Santa squeals in anger, and bounces off into the night. A grateful she-bear, through roars and growls, tries to tell you something. You take her back to the stables, where Matt Boch the whisperer listens to her tale with a gasp of horror. She has a cub! He ran off into the icefields when mama bear was captured. Help her find her baby!",
"questEvilSantaNotes": "You hear agonized roars deep in the icefields. You follow the growls - punctuated by the sound of cackling - to a clearing in the woods, where you see a fully-grown polar bear. She's caged and shackled, fighting for her life. Dancing atop the cage is a malicious little imp wearing a castaway costume. Vanquish Trapper Santa, and save the beast!",
"questEvilSantaCompletion": "Trapper Santa squeals in anger, and bounces off into the night. The grateful she-bear, through roars and growls, tries to tell you something. You take her back to the stables, where Matt Boch the Beast Master listens to her tale with a gasp of horror. She has a cub! He ran off into the icefields when mama bear was captured.",
"questEvilSantaBoss": "Trapper Santa",
"questEvilSantaDropBearCubPolarMount": "Polar Bear (Mount)",
"questEvilSanta2Text": "Find The Cub",
"questEvilSanta2Notes": "Mama bear's cub had run off into the icefields when she was captured by the trapper. At the edge of the woods, she sniffs the air. You hear twig-snaps and snow crunch through the crystaline sound of the forest. Paw prints! You both start racing to follow the trail. Find all the prints and broken twigs, and retrieve her cub!",
"questEvilSanta2Completion": "You've found the cub! Mama and baby bear couldn't be more grateful. As a token, they've decided to keep you company till the end of days.",
"questEvilSanta2Notes": "When Trapper Santa captured the polar bear mount, her cub ran off into the icefields. You hear twig-snaps and snow crunch through the crystaline sound of the forest. Paw prints! You start racing to follow the trail. Find all the prints and broken twigs, and retrieve the cub!",
"questEvilSanta2Completion": "You've found the cub! It will keep you company forever.",
"questEvilSanta2CollectTracks": "Tracks",
"questEvilSanta2CollectBranches": "Broken Twigs",
"questEvilSanta2DropBearCubPolarPet": "Polar Bear (Pet)",
@ -325,5 +325,12 @@
"questUnicornCompletion": "Impressed by your diligence and strength, the Unicorn Queen at last agrees that your cause is worthy. She allows you to ride on her back as she soars to the source of Conquest Creek. As she lowers her golden horn to the befouled waters, a brilliant blue light rises from the waters surface. It is so blinding that you are forced to close your eyes. When you open them a moment later, the unicorn is gone. However, @rosiesully lets out a cry of delight: the water is now clear, and three shining eggs rest at the creeks edge.",
"questUnicornBoss": "The Unicorn Queen",
"questUnicornDropUnicornEgg": "Unicorn (Egg)",
"questUnicornUnlockText": "Unlocks purchasable Unicorn eggs in the Market"
"questUnicornUnlockText": "Unlocks purchasable Unicorn eggs in the Market",
"questSabretoothText": "The Sabre Cat",
"questSabretoothNotes": "A roaring monster is terrorizing Habitica! The creature stalks through the wilds and woods, then bursts forth to attack before vanishing again. It's been hunting innocent pandas and frightening the flying pigs into fleeing their pens to roost in the trees. @Inventrix and @icefelis explain that the Zombie Sabre Cat was set free while they were excavating in the ancient, untouched ice-fields of the Stoikalm Steppes. \"It was perfectly friendly at first I don't know what happened. Please, you have to help us recapture it! Only a champion of Habitica can subdue this prehistoric beast!\"",
"questSabretoothCompletion": "After a long and tiring battle, you wrestle the Zombie Sabre Cat to the ground. As you are finally able to approach, you notice a nasty cavity in one of its sabre teeth. Realising the true cause of the cat's wrath, you're able to get the cavity filled by @Fandekasp, and advise everyone to avoid feeding their friend sweets in future. The Sabre Cat flourishes, and in gratitude, its tamers send you a generous reward a clutch of sabretooth eggs!",
"questSabretoothBoss": "Zombie Sabre Cat",
"questSabretoothDropSabretoothEgg": "Sabretooth (Egg)",
"questSabretoothUnlockText": "Unlocks purchasable Sabretooth eggs in the Market"
}

View file

@ -10,6 +10,7 @@
"newTaskEditPop": "With this option set, new tasks will immediately open for you to add details like notes and tags.",
"dailyDueDefaultView": "Set Dailies default to 'due' tab",
"dailyDueDefaultViewPop": "With this option set, the Dailies tasks will default to 'due' instead of 'all'",
"reverseChatOrder": "Show chat messages in reverse order",
"startCollapsed": "Tag list in tasks starts collapsed",
"startCollapsedPop": "With this option set, the list of task tags will be hidden when you first open a task for editing.",
"startAdvCollapsed": "Advanced Options in tasks start collapsed",
@ -106,6 +107,7 @@
"invitedQuest": "Invited to Quest",
"kickedGroup": "Kicked from group",
"remindersToLogin": "Reminders to check in to Habitica",
"subscribeUsing": "Subscribe using",
"unsubscribedSuccessfully": "Unsubscribed successfully!",
"unsubscribedTextUsers": "You have successfully unsubscribed from all Habitica emails. You can enable only the emails you want to receive from <a href=\"/#/options/settings/notifications\">the settings</a> (requires login).",
"unsubscribedTextOthers": "You won't receive any other email from Habitica.",
@ -113,6 +115,7 @@
"unsubscribeAllEmailsText": "By checking this box, I certify that I understand that by unsubscribing from all emails, Habitica will never be able to notify me via email about important changes to the site or my account.",
"correctlyUnsubscribedEmailType": "Correctly unsubscribed from \"<%= emailType %>\" emails.",
"subscriptionRateText": "Recurring $<%= price %> USD every <%= months %> months",
"recurringText": "recurring",
"benefits": "Benefits",
"coupon": "Coupon",
"couponPlaceholder": "Enter Coupon Code",

View file

@ -8,7 +8,7 @@
"retainHistoryText": "Makes your full history available in graphs and export. Non-subscriber histories get consolidated for database optimization.",
"doubleDrops": "Daily drop-caps doubled",
"doubleDropsText": "Complete your stable faster!",
"mysteryItem": "Unique Monthly Subscription Items",
"mysteryItem": "Exclusive Monthly Items",
"mysteryItemText": "Each month you will receive a unique cosmetic item for your avatar! Plus, for every three months of consecutive subscription, the Mysterious Time Travelers will grant you access to historic (and futuristic!) cosmetic items.",
"supportDevs": "Supports the developers",
"supportDevsText": "Your subscription helps keep Habitica thriving and helps fund the development of new features. Thank you for your generosity!",
@ -28,6 +28,7 @@
"subscribed": "Subscribed",
"manageSub": "Click to manage subscription",
"cancelSub": "Cancel Subscription",
"canceledSubscription": "Canceled Subscription",
"adminSub": "Administrator Subscriptions",
"morePlans": "More Plans<br/>Coming Soon",
"organizationSub": "Private Organization",
@ -54,8 +55,16 @@
"checkout": "Checkout",
"sureCancelSub": "Are you sure you want to cancel your subscription?",
"subCanceled": "Subscription will become inactive on",
"buyGemsGoldTitle": "To Buy Gems with Gold",
"becomeSubscriber": "Become a Subscriber",
"subGemPop": "Because you subscribe to Habitica, you can purchase a number of Gems each month using Gold. You can see how many Gems are available to buy at the corner of the Gem icon.",
"subGemName": "Subscriber Gems",
"freeGemsTitle": "Obtain Gems for Free",
"maxBuyGems": "You have bought all the gems you can this month. Thanks for subscribing!",
"buyGemsAllow1": "You can buy",
"buyGemsAllow2": "more gems this month",
"purchaseGemsSeparately": "Purchase Additional Gems",
"subFreeGemsHow": "Habitica players can earn gems for free by winning <a href=\"/#/options/groups/challenges\">challenges</a> that award gems as a prize, or as a <a href=\"http://habitica.wikia.com/wiki/Contributing_to_Habitica\">contributor reward by helping the development of Habitica.</a>",
"timeTravelers": "Time Travelers",
"timeTravelersTitleNoSub": "<%= linkStartTyler %>Tyler<%= linkEnd %> and <%= linkStartVicky %>Vicky<%= linkEnd %>",
"timeTravelersTitle": "Mysterious Time Travelers",

View file

@ -932,6 +932,13 @@ api.questEggs = {
return u.achievements.quests && (u.achievements.quests.unicorn != null) > 0;
})
},
Sabretooth: {
text: t('questEggSabretoothText'),
adjective: t('questEggSabretoothAdjective'),
canBuy: (function(u) {
return u.achievements.quests && (u.achievements.quests.sabretooth != null) > 0;
})
},
};
_.each(api.questEggs, function(egg, key) {
@ -1623,7 +1630,6 @@ api.quests = {
notes: t('questEvilSanta2Notes'),
completion: t('questEvilSanta2Completion'),
value: 4,
previous: 'evilsanta',
category: 'pet',
collect: {
tracks: {
@ -2982,6 +2988,38 @@ api.quests = {
unlock: t('questUnicornUnlockText')
}
},
sabretooth: {
text: t('questSabretoothText'),
notes: t('questSabretoothNotes'),
completion: t('questSabretoothCompletion'),
value: 4,
category: 'pet',
boss: {
name: t('questSabretoothBoss'),
hp: 1000,
str: 2
},
drop: {
items: [
{
type: 'eggs',
key: 'Sabretooth',
text: t('questSabretoothDropSabretoothEgg')
}, {
type: 'eggs',
key: 'Sabretooth',
text: t('questSabretoothDropSabretoothEgg')
}, {
type: 'eggs',
key: 'Sabretooth',
text: t('questSabretoothDropSabretoothEgg')
}
],
gp: 67,
exp: 650,
unlock: t('questSabretoothUnlockText')
}
},
};
_.each(api.quests, function(v, key) {

View file

@ -7,10 +7,9 @@ import {
MAX_LEVEL,
MAX_STAT_POINTS,
} from './constants';
import { preenHistory, preenUserHistory } from './preenUserHistory';
import * as statHelpers from './statHelpers';
var $w, _, api, content, i18n, moment, sortOrder,
var $w, _, api, content, i18n, preenHistory, moment, sortOrder,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
moment = require('moment');
@ -32,8 +31,6 @@ api.maxHealth = MAX_HEALTH;
api.tnl = statHelpers.toNextLevel;
api.diminishingReturns = statHelpers.diminishingReturns;
api.preenHistory = preenHistory;
$w = api.$w = function(s) {
return s.split(' ');
};
@ -82,6 +79,47 @@ api.planGemLimits = {
convCap: 25
};
/*
Preen history for users with > 7 history entries
This takes an infinite array of single day entries [day day day day day...], and turns it into a condensed array
of averages, condensing more the further back in time we go. Eg, 7 entries each for last 7 days; 1 entry each week
of this month; 1 entry for each month of this year; 1 entry per previous year: [day*7 week*4 month*12 year*infinite]
*/
preenHistory = function(history) {
var newHistory, preen, thisMonth;
history = _.filter(history, function(h) {
return !!h;
});
newHistory = [];
preen = function(amount, groupBy) {
var groups;
groups = _.chain(history).groupBy(function(h) {
return moment(h.date).format(groupBy);
}).sortBy(function(h, k) {
return k;
}).value();
groups = groups.slice(-amount);
groups.pop();
return _.each(groups, function(group) {
newHistory.push({
date: moment(group[0].date).toDate(),
value: _.reduce(group, (function(m, obj) {
return m + obj.value;
}), 0) / group.length
});
return true;
});
};
preen(50, "YYYY");
preen(moment().format('MM'), "YYYYMM");
thisMonth = moment().format('YYYYMM');
newHistory = newHistory.concat(_.filter(history, function(h) {
return moment(h.date).format('YYYYMM') === thisMonth;
}));
return newHistory;
};
/*
Preen 3-day past-completed To-Dos from Angular & mobile app
*/
@ -2547,7 +2585,15 @@ api.wrap = function(user, main) {
date: now,
value: expTally
});
preenUserHistory(user);
if (!((ref1 = user.purchased) != null ? (ref2 = ref1.plan) != null ? ref2.customerId : void 0 : void 0)) {
user.fns.preenUserHistory();
if (typeof user.markModified === "function") {
user.markModified('history');
}
if (typeof user.markModified === "function") {
user.markModified('dailys');
}
}
user.stats.buffs = perfect ? ((base3 = user.achievements).perfect != null ? base3.perfect : base3.perfect = 0, user.achievements.perfect++, lvlDiv2 = Math.ceil(api.capByLevel(user.stats.lvl) / 2), {
str: lvlDiv2,
int: lvlDiv2,
@ -2592,6 +2638,28 @@ api.wrap = function(user, main) {
}
return _progress;
},
preenUserHistory: function(minHistLen) {
if (minHistLen == null) {
minHistLen = 7;
}
_.each(user.habits.concat(user.dailys), function(task) {
var ref;
if (((ref = task.history) != null ? ref.length : void 0) > minHistLen) {
task.history = preenHistory(task.history);
}
return true;
});
_.defaults(user.history, {
todos: [],
exp: []
});
if (user.history.exp.length > minHistLen) {
user.history.exp = preenHistory(user.history.exp);
}
if (user.history.todos.length > minHistLen) {
return user.history.todos = preenHistory(user.history.todos);
}
},
ultimateGear: function() {
var base, owned;
owned = typeof window !== "undefined" && window !== null ? user.items.gear.owned : user.items.gear.owned.toObject();

View file

@ -1,88 +0,0 @@
import _ from 'lodash';
import moment from 'moment';
// Aggregate entries
function _aggregate (history, aggregateBy) {
return _.chain(history)
.groupBy(entry => { // group entries by aggregateBy
return moment(entry.date).format(aggregateBy);
})
.sortBy((entry, key) => key) // sort by date
.map(entries => {
return {
date: Number(entries[0].date),
value: _.reduce(entries, (previousValue, entry) => {
return previousValue + entry.value;
}, 0) / entries.length,
};
})
.value();
}
/* Preen an array of history entries
Free users:
- 1 value for each day of the past 60 days (no compression)
- 1 value each month for the previous 10 months
- 1 value each year for the previous years
Subscribers and challenges:
- 1 value for each day of the past 365 days (no compression)
- 1 value each month for the previous 12 months
- 1 value each year for the previous years
*/
export function preenHistory (history, isSubscribed, timezoneOffset) {
history = _.filter(history, historyEntry => Boolean(historyEntry)); // Filter missing entries
let now = timezoneOffset ? moment().zone(timezoneOffset) : moment();
// Date after which to begin compressing data
let cutOff = now.subtract(isSubscribed ? 365 : 60, 'days').startOf('day');
// Keep uncompressed entries (modifies history)
let newHistory = _.remove(history, entry => {
let date = moment(entry.date);
return date.isSame(cutOff) || date.isAfter(cutOff);
});
// Date after which to begin compressing data by year
let monthsCutOff = cutOff.subtract(isSubscribed ? 12 : 10, 'months').startOf('day');
let aggregateByMonth = _.remove(history, entry => {
let date = moment(entry.date);
return date.isSame(monthsCutOff) || date.isAfter(monthsCutOff);
});
// Aggregate remaining entries by month and year
if (aggregateByMonth.length > 0) newHistory.unshift(..._aggregate(aggregateByMonth, 'YYYYMM'));
if (history.length > 0) newHistory.unshift(..._aggregate(history, 'YYYY'));
return newHistory;
}
// Preen history for users and tasks. This code runs only on the server.
export function preenUserHistory (user) {
let isSubscribed = user.purchased && user.purchased.plan && user.purchased.plan.customerId;
let minHistoryLength = isSubscribed ? 365 : 60;
function _processTask (task, index) {
if (task.history && task.history.length > minHistoryLength) {
task.history = preenHistory(task.history, isSubscribed, user.preferences.timezoneOffset);
if (user.markModified) user.markModified(`${task.type}s.${index}.history`);
}
}
_.each(user.habits, _processTask);
_.each(user.dailys, _processTask);
_.defaults(user.history, {
todos: [],
exp: [],
});
if (user.history.exp.length > minHistoryLength) {
user.history.exp = preenHistory(user.history.exp, isSubscribed, user.preferences.timezoneOffset);
user.markModified('history.exp');
}
if (user.history.todos.length > minHistoryLength) {
user.history.todos = preenHistory(user.history.todos, isSubscribed, user.preferences.timezoneOffset);
user.markModified('history.todos');
}
}

View file

@ -30,7 +30,6 @@ let holidayQuests = {
text: t('questEvilSanta2Text'),
notes: t('questEvilSanta2Notes'),
completion: t('questEvilSanta2Completion'),
previous: 'evilsanta',
collect: {
tracks: {
text: t('questEvilSanta2CollectTracks'),

View file

@ -0,0 +1,106 @@
var migrationName = '20160111_challenges_condense_same_day_history_entries.js';
/*
* Compress challenges tasks history entries so that only one entry per day is kept
*/
var dbserver = '';
var dbname = '';
var mongo = require('mongoskin');
var _ = require('lodash');
var moment = require('moment');
var dbChallenges = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('challenges');
// Find all challenges
var query = {
};
// we only want habits and dailies (rewards and todos don't have history)
var fields = {
'habits': 1,
'dailys': 1,
};
function compressEntries (history) {
return _.chain(history)
.filter(function(entry) {
return !!entry;
})
.groupBy(function(entry) { // group by day
return moment(entry.date).format('YYYYMMDD');
})
.sortBy(function(entry, key) { // sort by date and transform back to array of array of entries
return key;
})
.map(function(entries) { // aggregate the value
return {
date: Number(entries[0].date),
value: _.reduce(entries, function (previousValue, entry) {
return previousValue + entry.value;
}, 0) / entries.length,
};
})
.value();
};
console.warn('Updating challenges...');
var progressCount = 100;
var count = 0;
dbChallenges.findEach(query, fields, {batchSize: 250}, function(err, challenge) {
if (err) { return exiting(1, 'ERROR! ' + err); }
if (!challenge) {
console.warn('All appropriate challenges found.');
return displayData();
}
count++;
// specify challenge data to change:
var set = {};
if (challenge.habits && challenge.habits.length > 0) {
challenge.habits.forEach(function(habit, index) {
if (habit.history && habit.history.length > 1) {
var originalL = habit.history.length;
habit.history = compressEntries(habit.history);
if (originalL > 1000) console.log(originalL, habit.history.length);
set['habits.' + index + '.history'] = habit.history;
}
});
}
if (challenge.dailys && challenge.dailys.length > 0) {
challenge.dailys.forEach(function(daily, index) {
if (daily.history && daily.history.length > 1) {
var originalL = daily.history.length;
daily.history = compressEntries(daily.history);
if (originalL > 1000) console.log(originalL, daily.history.length);
set['dailys.' + index + '.history'] = daily.history;
}
});
}
dbChallenges.update({_id: challenge._id}, {$set: set}, function(err) {
if(err) throw err;
console.log('updated a challenge');
});
if (count%progressCount == 0) console.warn(count + ' ' + challenge._id);
});
function displayData() {
console.warn('\n' + count + ' challenges processed\n');
return exiting(0);
}
function exiting(code, msg) {
code = code || 0; // 0 = success
if (code && !msg) { msg = 'ERROR!'; }
if (msg) {
if (code) { console.error(msg); }
else { console.log( msg); }
}
process.exit(code);
}

View file

@ -0,0 +1,34 @@
import {
createAndPopulateGroup,
generateChallenge,
} from '../../../helpers/api-integration/v2';
describe('GET /challenges/:id', () => {
context('Member of a challenge', () => {
let leader, party, challenge;
before(async () => {
let {
group,
groupLeader,
} = await createAndPopulateGroup();
party = group;
leader = groupLeader;
challenge = await generateChallenge(leader, party, {
name: 'a created challenge',
shortName: 'aCreatedChallenge',
description: 'a description for the challenge',
});
});
it('returns the challenge object', async () => {
let fetchedChallenge = await leader.get(`/challenges/${challenge._id}`);
expect(fetchedChallenge.name).to.eql(challenge.name);
expect(fetchedChallenge.shortName).to.eql(challenge.shortName);
expect(fetchedChallenge.description).to.eql(challenge.description);
expect(fetchedChallenge.members).to.have.a.lengthOf(1);
});
});
});

View file

@ -2,7 +2,7 @@ import {
generateGroup,
generateUser,
resetHabiticaDB,
} from '../../../helpers/api-integration.helper';
} from '../../../helpers/api-integration/v2';
describe('GET /groups', () => {
const NUMBER_OF_PUBLIC_GUILDS = 3;
@ -22,6 +22,7 @@ describe('GET /groups', () => {
name: 'public guild - is member',
type: 'guild',
privacy: 'public',
}, {
members: [leader._id, user._id],
});
@ -35,6 +36,7 @@ describe('GET /groups', () => {
name: 'private guild - is member',
type: 'guild',
privacy: 'private',
}, {
members: [leader._id, user._id],
});

View file

@ -2,7 +2,7 @@ import {
createAndPopulateGroup,
generateUser,
translate as t,
} from '../../../helpers/api-integration.helper';
} from '../../../helpers/api-integration/v2';
import {
find,
each,
@ -24,7 +24,7 @@ describe('GET /groups/:id', () => {
groupDetails,
});
leader = groupData.leader;
leader = groupData.groupLeader;
member = groupData.members[0];
createdGroup = groupData.group;
});

View file

@ -2,7 +2,7 @@ import {
generateGroup,
generateUser,
translate as t,
} from '../../../helpers/api-integration.helper';
} from '../../../helpers/api-integration/v2';
describe('POST /groups', () => {
context('All groups', () => {

View file

@ -2,7 +2,7 @@ import {
generateGroup,
generateUser,
translate as t,
} from '../../../helpers/api-integration.helper';
} from '../../../helpers/api-integration/v2';
describe('POST /groups/:id', () => {
context('user is not the leader of the group', () => {
@ -58,10 +58,10 @@ describe('POST /groups/:id', () => {
description: 'New group description',
});
let group = await user.get(`/groups/${usersGroup._id}`);
await usersGroup.sync();
expect(group.name).to.eql('New Group Title');
expect(group.description).to.eql('New group description');
expect(usersGroup.name).to.eql('New Group Title');
expect(usersGroup.description).to.eql('New group description');
});
});
});

View file

@ -2,8 +2,8 @@ import {
createAndPopulateGroup,
generateUser,
translate as t,
} from '../../../helpers/api-integration.helper';
import { each, find } from 'lodash';
} from '../../../helpers/api-integration/v2';
import { each } from 'lodash';
describe('POST /groups/:id/join', () => {
context('user is already a member of the group', () => {
@ -30,10 +30,9 @@ describe('POST /groups/:id/join', () => {
it(`allows user to join a ${groupType}`, async () => {
await invitee.post(`/groups/${group._id}/join`);
let members = (await invitee.get(`/groups/${group._id}`)).members;
let userInGroup = find(members, '_id', invitee._id);
await group.sync();
expect(userInGroup).to.exist;
expect(group.members).to.include(invitee._id);
});
});
});
@ -79,10 +78,9 @@ describe('POST /groups/:id/join', () => {
it('allows user to join a public guild', async () => {
await user.post(`/groups/${group._id}/join`);
let members = (await user.get(`/groups/${group._id}`)).members;
let userInGroup = find(members, '_id', user._id);
await group.sync();
expect(userInGroup).to.exist;
expect(group.members).to.include(user._id);
});
});
@ -98,14 +96,16 @@ describe('POST /groups/:id/join', () => {
},
});
group = groupData.group;
await groupData.leader.post(`/groups/${group._id}/leave`);
await groupData.groupLeader.post(`/groups/${group._id}/leave`);
user = await generateUser();
});
it('makes the joining user the leader', async () => {
await user.post(`/groups/${group._id}/join`);
await expect(user.get(`/groups/${group._id}`)).to.eventually.have.deep.property('leader._id', user._id);
await group.sync();
await expect(group.leader).to.eql(user._id);
});
});
});

View file

@ -1,8 +1,7 @@
import {
checkExistence,
createAndPopulateGroup,
} from '../../../helpers/api-integration.helper';
import { find } from 'lodash';
} from '../../../helpers/api-integration/v2';
describe('POST /groups/:id/leave', () => {
context('user is not member of the group', () => {
@ -29,10 +28,9 @@ describe('POST /groups/:id/leave', () => {
it('leaves the group', async () => {
await user.post(`/groups/${group._id}/leave`);
let members = (await user.get(`/groups/${group._id}`)).members;
let userInGroup = find(members, '_id', user._id);
await group.sync();
expect(userInGroup).to.not.be.ok;
expect(group.members).to.not.include(user._id);
});
});
@ -48,7 +46,7 @@ describe('POST /groups/:id/leave', () => {
},
});
user = groupData.leader;
user = groupData.groupLeader;
group = groupData.group;
});
@ -71,7 +69,7 @@ describe('POST /groups/:id/leave', () => {
},
});
user = groupData.leader;
user = groupData.groupLeader;
group = groupData.group;
});
@ -95,7 +93,7 @@ describe('POST /groups/:id/leave', () => {
},
});
user = groupData.leader;
user = groupData.groupLeader;
group = groupData.group;
invitee1 = groupData.invitees[0];
invitee2 = groupData.invitees[1];
@ -122,7 +120,7 @@ describe('POST /groups/:id/leave', () => {
},
});
user = groupData.leader;
user = groupData.groupLeader;
group = groupData.group;
invitee1 = groupData.invitees[0];
invitee2 = groupData.invitees[1];

View file

@ -1,7 +1,7 @@
import {
createAndPopulateGroup,
translate as t,
} from '../../../helpers/api-integration.helper';
} from '../../../helpers/api-integration/v2';
describe('POST /groups/:id/removeMember', () => {
context('user is not member of the group', () => {
@ -24,7 +24,7 @@ describe('POST /groups/:id/removeMember', () => {
privacy: 'public',
},
}).then((res) => {
leader = res.leader;
leader = res.groupLeader;
member = res.members[0];
group = res.group;
});

View file

@ -1,7 +1,7 @@
import {
createAndPopulateGroup,
translate as t,
} from '../../../../helpers/api-integration.helper';
} from '../../../../helpers/api-integration/v2';
describe('DELETE /groups/:id/chat', () => {
let group, message, user;
@ -14,7 +14,7 @@ describe('DELETE /groups/:id/chat', () => {
},
}).then((res) => {
group = res.group;
user = res.leader;
user = res.groupLeader;
return user.post(`/groups/${group._id}/chat`, null, { message: 'Some message' });
}).then((res) => {

View file

@ -1,6 +1,6 @@
import {
createAndPopulateGroup,
} from '../../../../helpers/api-integration.helper';
} from '../../../../helpers/api-integration/v2';
describe('GET /groups/:id/chat', () => {
context('group with multiple messages', () => {
@ -16,7 +16,7 @@ describe('GET /groups/:id/chat', () => {
});
group = groupData.group;
user = groupData.leader;
user = groupData.groupLeader;
member = groupData.members[0];
await member.post(`/groups/${group._id}/chat`, null, { message: 'Group member message' });

View file

@ -1,7 +1,7 @@
import {
createAndPopulateGroup,
translate as t,
} from '../../../../helpers/api-integration.helper';
} from '../../../../helpers/api-integration/v2';
describe('POST /groups/:id/chat', () => {
let group, user;
@ -14,7 +14,7 @@ describe('POST /groups/:id/chat', () => {
},
}).then((res) => {
group = res.group;
user = res.leader;
user = res.groupLeader;
});
});

View file

@ -2,27 +2,26 @@ import {
createAndPopulateGroup,
generateUser,
translate as t,
} from '../../../../helpers/api-integration.helper';
} from '../../../../helpers/api-integration/v2';
describe('POST /groups/:id/chat/:id/clearflags', () => {
let group;
let guild;
beforeEach(async () => {
return createAndPopulateGroup({
let { group } = await createAndPopulateGroup({
groupDetails: {
type: 'guild',
privacy: 'public',
members: 1,
flagCount: 1,
chat: [{
id: 'message-to-clear',
flagCount: 1,
flags: { 'some-id': true },
}],
},
}).then((res) => {
group = res.group;
});
guild = group;
});
context('non admin', () => {
@ -33,7 +32,7 @@ describe('POST /groups/:id/chat/:id/clearflags', () => {
});
it('cannot clear flags', async () => {
return expect(nonadmin.post(`/groups/${group._id}/chat/message-to-clear/clearflags`))
return expect(nonadmin.post(`/groups/${guild._id}/chat/message-to-clear/clearflags`))
.to.eventually.be.rejected.and.eql({
code: 401,
text: t('messageGroupChatAdminClearFlagCount'),
@ -53,23 +52,23 @@ describe('POST /groups/:id/chat/:id/clearflags', () => {
});
it('clears flags', async () => {
return admin.post(`/groups/${group._id}/chat/message-to-clear/clearflags`).then(() => {
return admin.get(`/groups/${group._id}/chat`);
return admin.post(`/groups/${guild._id}/chat/message-to-clear/clearflags`).then(() => {
return admin.get(`/groups/${guild._id}/chat`);
}).then((messages) => {
expect(messages[0].flagCount).to.eql(0);
});
});
it('leaves old flags on the flag object', async () => {
return admin.post(`/groups/${group._id}/chat/message-to-clear/clearflags`).then(() => {
return admin.get(`/groups/${group._id}/chat`);
return admin.post(`/groups/${guild._id}/chat/message-to-clear/clearflags`).then(() => {
return admin.get(`/groups/${guild._id}/chat`);
}).then((messages) => {
expect(messages[0].flags).to.have.property('some-id', true);
});
});
it('returns error if message does not exist', async () => {
return expect(admin.post(`/groups/${group._id}/chat/non-existant-message/clearflags`))
return expect(admin.post(`/groups/${guild._id}/chat/non-existant-message/clearflags`))
.to.eventually.be.rejected.and.eql({
code: 404,
text: t('messageGroupChatNotFound'),

View file

@ -2,7 +2,7 @@ import {
createAndPopulateGroup,
generateUser,
translate as t,
} from '../../../../helpers/api-integration.helper';
} from '../../../../helpers/api-integration/v2';
describe('POST /groups/:id/chat/:id/flag', () => {
context('another member\'s message', () => {
@ -17,7 +17,7 @@ describe('POST /groups/:id/chat/:id/flag', () => {
members: 1,
}).then((res) => {
group = res.group;
user = res.leader;
user = res.groupLeader;
member = res.members[0];
return member.post(`/groups/${group._id}/chat`, null, { message: 'Group member message' });
@ -56,7 +56,7 @@ describe('POST /groups/:id/chat/:id/flag', () => {
},
}).then((res) => {
group = res.group;
user = res.leader;
user = res.groupLeader;
return user.post(`/groups/${group._id}/chat`, null, { message: 'User\'s own message' });
}).then((res) => {
@ -84,7 +84,7 @@ describe('POST /groups/:id/chat/:id/flag', () => {
},
}).then((res) => {
group = res.group;
user = res.leader;
user = res.groupLeader;
});
});
@ -121,7 +121,7 @@ describe('POST /groups/:id/chat/:id/flag', () => {
});
group = groupData.group;
user = groupData.leader;
user = groupData.groupLeader;
});
it('changes only the message that is flagged', async () => {
@ -166,7 +166,7 @@ describe('POST /groups/:id/chat/:id/flag', () => {
members: 1,
}).then((res) => {
group = res.group;
user = res.leader;
user = res.groupLeader;
member = res.members[0];
return member.post(`/groups/${group._id}/chat`, null, { message: 'Group member message' });

View file

@ -2,7 +2,7 @@ import {
createAndPopulateGroup,
generateUser,
translate as t,
} from '../../../../helpers/api-integration.helper';
} from '../../../../helpers/api-integration/v2';
describe('POST /groups/:id/chat/:id/like', () => {
context('another member\'s message', () => {
@ -17,7 +17,7 @@ describe('POST /groups/:id/chat/:id/like', () => {
members: 1,
}).then((res) => {
group = res.group;
user = res.leader;
user = res.groupLeader;
member = res.members[0];
return member.post(`/groups/${group._id}/chat`, null, { message: 'Group member message' });
@ -53,7 +53,7 @@ describe('POST /groups/:id/chat/:id/like', () => {
},
}).then((res) => {
group = res.group;
user = res.leader;
user = res.groupLeader;
return user.post(`/groups/${group._id}/chat`, null, { message: 'User\'s own message' });
}).then((res) => {
@ -94,7 +94,7 @@ describe('POST /groups/:id/chat/:id/like', () => {
});
group = groupData.group;
user = groupData.leader;
user = groupData.groupLeader;
});
it('changes only the message that is liked', async () => {
@ -134,7 +134,7 @@ describe('POST /groups/:id/chat/:id/like', () => {
},
}).then((res) => {
group = res.group;
user = res.leader;
user = res.groupLeader;
});
});

View file

@ -2,7 +2,7 @@ import {
generateUser,
requester,
translate as t,
} from '../../../helpers/api-integration.helper';
} from '../../../helpers/api-integration/v2';
import { v4 as generateRandomUserName } from 'uuid';
import { each } from 'lodash';

View file

@ -1,4 +1,4 @@
import {requester} from '../../../helpers/api-integration.helper';
import {requester} from '../../../helpers/api-integration/v2';
describe('Status', () => {
it('returns a status of up when server is up', async () => {

Some files were not shown because too many files have changed in this diff Show more