Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DCS] Symbols Update #10596

Merged
merged 2 commits into from
Jun 23, 2024

add back dropped glyphs

1112aac
Select commit
Loading
Failed to load commit list.
Sign in for the full log view
Merged

[DCS] Symbols Update #10596

add back dropped glyphs
1112aac
Select commit
Loading
Failed to load commit list.
GitHub Actions / JEST Tests succeeded Jun 23, 2024 in 0s

491 passed, 0 failed and 0 skipped

Tests passed successfully

✅ junit.xml

491 tests were completed in 20s with 491 passed, 0 failed and 0 skipped.

Test suite Passed Failed Skipped Time
src/app/accounts/destiny-account.test.ts 3✅ 756ms
src/app/armory/wishlist-collapser.test.ts 1✅ 68ms
src/app/bungie-api/http-client.test.ts 7✅ 75ms
src/app/destiny2/d2-definitions.test.ts 1✅ 2s
src/app/dim-api/reducer.test.ts 15✅ 5s
src/app/hotkeys/hotkeys.test.ts 2✅ 73ms
src/app/inventory/d2-stores.test.ts 9✅ 3s
src/app/inventory/notes-hashtags.test.ts 26✅ 67ms
src/app/item-popup/item-popup-actions.test.ts 1✅ 3s
src/app/item-triage/triage-utils.test.ts 2✅ 1s
src/app/loadout-analyzer/analysis.test.ts 7✅ 4s
src/app/loadout-builder/auto-stat-mod-utils.test.ts 7✅ 3s
src/app/loadout-builder/item-filter.test.ts 11✅ 4s
src/app/loadout-builder/process-utils.test.ts 38✅ 5s
src/app/loadout-builder/process-worker/stats-set.test.ts 1✅ 55ms
src/app/loadout-builder/process/mappers.test.ts 3✅ 3s
src/app/loadout-builder/utils.test.ts 1✅ 55ms
src/app/loadout-drawer/loadout-drawer-reducer.test.ts 36✅ 6s
src/app/loadout/loadout-share/loadout-import.test.ts 10✅ 278ms
src/app/loadout/mod-assignment-utils.test.ts 5✅ 3s
src/app/register-service-worker.test.ts 3✅ 167ms
src/app/search/autocomplete.test.ts 38✅ 578ms
src/app/search/loadouts/loadout-search-filter.test.ts 2✅ 394ms
src/app/search/query-parser.test.ts 75✅ 226ms
src/app/search/search-config.test.ts 57✅ 398ms
src/app/search/search-filter.test.ts 34✅ 79ms
src/app/settings/vault-grouping.test.ts 3✅ 3s
src/app/store-stats/AccountCurrencies.test.tsx 2✅ 244ms
src/app/utils/collections.test.ts 9✅ 60ms
src/app/utils/intl.test.ts 33✅ 154ms
src/app/utils/memoize.test.ts 1✅ 55ms
src/app/utils/promises.test.ts 1✅ 46ms
src/app/utils/time.test.ts 36✅ 481ms
src/app/utils/undo-redo-history.test.ts 1✅ 424ms
src/app/vendors/d2-vendors.test.ts 1✅ 3s
src/app/wishlists/wishlist-file.test.ts 1✅ 82ms
src/browsercheck.test.ts 7✅ 116ms
src/testing/precache-manifest.test.ts 1✅ 2s

✅ src/app/accounts/destiny-account.test.ts

generatePlatforms
  ✅ gets one D2 account and one D1 account
  ✅ handles when D2 accounts are in profilesWithErrors and error code DestinyUnexpectedError
  ✅ does not return D2 account when they are in profilesWithErrors and error code DestinyAccountNotFound

✅ src/app/armory/wishlist-collapser.test.ts

perkConsolidator
  ✅ does its thing

✅ src/app/bungie-api/http-client.test.ts

✅ check Request builder for [Function getItem]
✅ check Request builder for [Function getProfile]
✅ check Request builder for [Function getLinkedProfiles]
✅ check Request builder for [Function getVendor]
✅ check Request builder for [Function pullFromPostmaster]
✅ should throw an error if there is no room in the destination
✅ should throw an error if API is down for maintenance

✅ src/app/destiny2/d2-definitions.test.ts

✅ something

✅ src/app/dim-api/reducer.test.ts

setSetting
  ✅ changes settings
setItemTag
  ✅ sets tags if there were none before
  ✅ clears set tags
setItemHashTag
  ✅ sets tags if there were none before
  ✅ clears set tags
prepareToFlushUpdates
  ✅ can coalesce settings
  ✅ can handle multiple profile updates
  ✅ can handle multiple profile updates with settings last
  ✅ can handle loadouts
  ✅ can handle tag stuff
finishedUpdates
  ✅ can mark success
saveSearch
  ✅ can save valid queries
  ✅ can unsave valid queries
  ✅ does not save invalid queries
  ✅ can unsave invalid queries

✅ src/app/hotkeys/hotkeys.test.ts

✅ stacks hotkeys
✅ allows Escape hotkey when an input is focused

✅ src/app/inventory/d2-stores.test.ts

process stores
  ✅ can process stores without errors
  ✅ all sockets' plugged is present in the list of plugOptions
  ✅ intrinsic sockets should have only one plugOption
  ✅ sparrows should have perks
  ✅ items with stats should have at least one nonzero stat
  ✅ item perks can be marked as cannotCurrentlyRoll
  ✅ generates a correct  Weapons CSV export
  ✅ generates a correct  Armor CSV export
  ✅ generates a correct  Ghost CSV export

✅ src/app/inventory/notes-hashtags.test.ts

✅ getHashtagsFromNote: #foo #bar
✅ getHashtagsFromNote: #foo, #bar
✅ getHashtagsFromNote: This note has #foo tag and also#bar
✅ getHashtagsFromNote: #foo#bar
✅ getHashtagsFromNote: #foo,#bar
✅ getHashtagsFromNote: #foo-#bar
✅ getHashtagsFromNote: Emoji #🤯 tags
✅ collectNotesHashtags should get a unique set of hashtags from multiple notes
✅ appendedToNote: undefined + A note => A note
✅ appendedToNote: A note + #fancy => A note #fancy
✅ appendedToNote: A #fancy note + #fancy => A #fancy note
✅ appendedToNote: #pve #s20 + #pve #stasis => #pve #s20 #stasis
✅ appendedToNote: #pve #s20 + #void #pve #stasis => #pve #s20 #void #stasis
✅ appendedToNote: My favorite! #pve #s20 + #pve #stasis => My favorite! #pve #s20 #stasis
✅ appendedToNote: My favorite!
#pve #s20 + #pve #stasis => My favorite!
#pve #s20 #stasis
✅ appendedToNote: My favorite!
#pve #s20 + #pve
#stasis => My favorite!
#pve #s20
#stasis
✅ appendedToNote: #pve, #s20 + #pve #stasis => #pve, #s20 #stasis
✅ appendedToNote: #void + #void #voidwalker #other => #void #voidwalker #other
✅ removedFromNote: A note - A note =>
✅ removedFromNote: A note #fancy - #fancy => A note
✅ removedFromNote: A #fancy note - #fancy => A note
✅ removedFromNote: #voidwalker #void - #void => #voidwalker
✅ removedFromNote: #voidwalker #void - void => #voidwalker #void
✅ removedFromNote: #voidwalker My #void gun - My #void gun => #voidwalker
✅ removedFromNote: #pve, #s20, #stasis - #s20 => #pve, , #stasis
✅ removedFromNote: Void void void arc - void => Void arc

✅ src/app/item-popup/item-popup-actions.test.ts

✅ handles an equipped item

✅ src/app/item-triage/triage-utils.test.ts

triage armor comparison works
  ✅ works for non-artifice armor
  ✅ works for artifice armor

✅ src/app/loadout-analyzer/analysis.test.ts

basic loadout analysis finding tests
  ✅ finds MissingItems
  ✅ finds InvalidMods
  ✅ finds EmptyFragmentSlots/TooManyFragments
  ✅ finds InvalidSearchQuery
  ✅ finds UsesSeasonalMods/ModsDontFit
  ✅ finds DoesNotRespectExotic
  ✅ finds DoesNotSatisfyStatConstraints

✅ src/app/loadout-builder/auto-stat-mod-utils.test.ts

process-utils auto mod structure
  ✅ snapshot of mod defs when assuming general for auto mods
  ✅ snapshot of mod defs when assuming cheapgeneral for auto mods
  ✅ different ways of hitting target stats with 5 remaining general mods (using artifice mods: false)
  ✅ different ways of hitting target stats with 3 remaining general mods (using artifice mods: false)
  ✅ different ways of hitting target stats with 1 remaining general mods (using artifice mods: true)
  ✅ different ways of hitting target stats with 0 remaining general mods (using artifice mods: true)
  ✅ different ways of hitting target stats with 5 remaining general mods (using artifice mods: true)

✅ src/app/loadout-builder/item-filter.test.ts

loadout-builder item-filter
  ✅ filters nothing out when no filters specified
  ✅ filters out items with insufficient energy capacity
  ✅ filters nothing out when any exotic
  ✅ removes exotics when no exotic
  ✅ filters nothing out when infeasible filter
  ✅ ignores filter for certain slots
  ✅ filter applies to some slots
  ✅ specific exotic filtered
  ✅ any exotic does not ignore filters if they can be satisfied
  ✅ any exotic ignores filters if they cannot be satisfied
  ✅ mod assignment may cause exotic slot to not have options

✅ src/app/loadout-builder/process-utils.test.ts

process-utils mod assignment
  ✅ generates the correct number of permutations for 1 unique mods
  ✅ generates the correct number of permutations for 2 unique mods
  ✅ generates the correct number of permutations for 3 unique mods
  ✅ generates the correct number of permutations for 4 unique mods
  ✅ generates the correct number of permutations for 5 unique mods
  ✅ can fit all mods when there are no mods
  ✅ can fit five general mods
  ✅ it can fit a general mod into a single item at index 0
  ✅ it can fit a general mod into a single item at index 1
  ✅ it can fit a general mod into a single item at index 2
  ✅ it can fit a general mod into a single item at index 3
  ✅ it can fit a general mod into a single item at index 4
  ✅ it can fit five activity mods
  ✅ it can't fit five activity mods
  ✅ it can fit a activity mod into a single item at index 0
  ✅ it can fit a activity mod into a single item at index 1
  ✅ it can fit a activity mod into a single item at index 2
  ✅ it can fit a activity mod into a single item at index 3
  ✅ it can fit a activity mod into a single item at index 4
  ✅ can fit general, activity, and combat mods if there is enough energy
  ✅ can't fit general, activity, and combat mods if there isn't enough energy
  ✅ can't fit mods if general mods have too much energy
  ✅ can't fit mods if combat mods have too much energy
  ✅ can't fit mods if activity mods have too much energy
process-utils auto mods
  ✅ the problem is solvable
  ✅ higher stats means we cannot find a viable set of picks
  ✅ we need all artifice mod slots
  ✅ we need all the energy capacity in all general mod slots
  ✅ activity mod cannot go into the other item if we want to hit stats
process-utils optimal mods
  ✅ set with stats [80, 70, 80, 40, 30, 30], energies [0, 3, 0, 3, 0], numArtifice 0 yields tiers [8, 7, 8, 6, 3, 3]
  ✅ set with stats [63, 70, 59, 35, 30, 30], energies [2, 0, 0, 0, 0], numArtifice 3 yields tiers [7, 7, 6, 3, 3, 3]
  ✅ set with stats [80, 65, 80, 35, 30, 30], energies [1, 0, 0, 0, 0], numArtifice 2 yields tiers [8, 7, 8, 4, 3, 3]
  ✅ set with stats [68, 66, 30, 30, 30, 30], energies [0, 0, 0, 0, 0], numArtifice 4 yields tiers [8, 6, 3, 3, 3, 3]
  ✅ set with stats [68, 66, 30, 30, 11, 30], energies [2, 2, 0, 0, 0], numArtifice 4 yields tiers [7, 6, 3, 3, 3, 3]
  ✅ set with stats [30, 61, 30, 30, 30, -14], energies [5, 5, 5, 5, 5], numArtifice 5 yields tiers [5, 6, 3, 3, 3, 3]
  ✅ set with stats [18, 80, 80, 26, 80, 30], energies [0, 0, 0, 3, 0], numArtifice 4 produces exact stats [30, 80, 80, 31, 80, 30]
  ✅ set with stats [26, 80, 80, 18, 80, 30], energies [0, 0, 0, 3, 0], numArtifice 4 produces exact stats [32, 80, 80, 31, 80, 30]
✅ process-utils activity mods

✅ src/app/loadout-builder/process-worker/stats-set.test.ts

✅ insertAll 1

✅ src/app/loadout-builder/process/mappers.test.ts

lo process mappers
  ✅ mapped energy capacity is 10 when assumed masterwork is used
  ✅ mapped energy capacity is the items when assumed masterwork is not used
  ✅ mapped energy capacity defaults to MIN_LO_ITEM_ENERGY when assumed masterwork is not used and the item energy is low

✅ src/app/loadout-builder/utils.test.ts

statTierWithHalf
  ✅ checks proper visual tier formatting

✅ src/app/loadout-drawer/loadout-drawer-reducer.test.ts

addItem
  ✅ can add an item to an empty loadout
  ✅ saves the crafted date for crafted items
  ✅ can add an item to a loadout that is already there as a noop
  ✅ can add an unequipped item to a loadout
  ✅ defaults equip when unset
  ✅ can replace the currently equipped item
  ✅ can switch an equipped item to unequipped if added again with the equip flag set
  ✅ can switch an unequipped item to equipped if added again with the equip flag set
  ✅ handles duplicates even if the new version is a reshaped version of the one saved in the loadout
  ✅ fills in socket overrides when adding a subclass
  ✅ replaces the existing subclass when adding a new one
  ✅ does nothing if the item cannot be in a loadout
  ✅ does nothing if the item is for the wrong class
  ✅ does nothing when adding class-specific item to any-class loadout
  ✅ removes class-specific items when saving as "any class"
  ✅ does nothing if the bucket is already at capacity
  ✅ de-equips an exotic in another bucket when equipping a new exotic
removeItem
  ✅ promotes an unequipped item to equipped when the equipped item is removed
  ✅ does nothing when asked to remove an item that is not in the loadout
toggleEquipped
  ✅ can toggle an equipped item to unequipped
  ✅ can toggle an unequipped item to equipped
  ✅ does nothing when applied to a subclass
  ✅ does not lose socket overrides
removeMod
  ✅ removes a mod by inventory item hash
clearSubclass
  ✅ removes the subclass
setLoadoutSubclassFromEquipped
  ✅ correctly populates the subclass and its overrides
fillLoadoutFromEquipped
  ✅ can fill in weapons
  ✅ can fill in armor
  ✅ can fill in everything
  ✅ will not overwrite mods if they are already there
  ✅ will not overwrite artifact unlocks if they are already there
fillLoadoutFromUnequipped
  ✅ fills in unequipped items but does not change an existing item
  ✅ fills in unequipped items for a single category
  ✅ fills in unequipped items for a single category without overflow
clearBucketCategory
  ✅ clears the weapons category
  ✅ clears the general category without clearing subclass

✅ src/app/loadout/loadout-share/loadout-import.test.ts

dim.gg loadout share links parsing
  ✅ valid dim.gg loadout link 4j5nz4q
  ✅ valid dim.gg loadout link 4j5nz4q/Heart-of-Inmost-Light-Arc
  ✅ valid dim.gg loadout link dim.gg/4j5nz4q
  ✅ valid dim.gg loadout link https://dim.gg/4j5nz4q
  ✅ valid dim.gg loadout link https://dim.gg/4j5nz4q/
  ✅ valid dim.gg loadout link http://dim.gg/4j5nz4q/
  ✅ valid dim.gg loadout link https://dim.gg/4j5nz4q/Heart-of-Inmost-Light-Arc
  ✅ valid direct loadout link https://beta.destinyitemmanager.com/loadouts?loadout=%7B%22id%22%3A%22jf7w53i%22%2C%22name%22%3A%22Simple+Loadout%22%2C%22classType%22%3A3%2C%22clearSpace%22%3Afalse%2C%22equipped%22%3A%5B%5D%2C%22unequipped%22%3A%5B%5D%2C%22createdAt%22%3A1668353218495%2C%22parameters%22%3A%7B%22mods%22%3A%5B2623485440%5D%2C%22lockArmorEnergyType%22%3A1%2C%22assumeArmorMasterwork%22%3A1%7D%7D
  ✅ valid direct loadout link https://app.destinyitemmanager.com/loadouts?loadout={%22name%22:%22robojumper%27s%20loadout%22,%22classType%22:0,%22equipped%22:[{%22id%22:%226917529830717539496%22,%22hash%22:2531963421},{%22id%22:%226917529836701241352%22,%22hash%22:1937552980},{%22id%22:%226917529682880957257%22,%22hash%22:1399243961},{%22id%22:%226917529643654891819%22,%22hash%22:1322544481},{%22id%22:%226917529208297337712%22,%22hash%22:613647804,%22socketOverrides%22:{%220%22:3260056808,%221%22:469281040,%222%22:1399217,%223%22:3866705246,%224%22:2031919264,%225%22:3469412971,%226%22:537774540,%227%22:3469412975,%228%22:2483898429,%229%22:2483898430}}],%22parameters%22:{%22mods%22:[667313240,3521781036,1499759470,2051366208,1740246051,1740246051,1740246051,2187989982,2187989982,1977242754,1977242752,445559589,445559589]}}
  ✅ valid direct loadout link https://beta.destinyitemmanager.com/4611686018483139177/d2/loadouts?loadout=%7B%22id%22%3A%22d2ap%22%2C%22name%22%3A%22D2ArmorPicker%20Loadout%22%2C%22classType%22%3A1%2C%22parameters%22%3A%7B%22statConstraints%22%3A%5B%7B%22statHash%22%3A2996146975%2C%22minTier%22%3A5%2C%22maxTier%22%3A10%7D%2C%7B%22statHash%22%3A392767087%2C%22minTier%22%3A10%2C%22maxTier%22%3A10%7D%2C%7B%22statHash%22%3A1943323491%2C%22minTier%22%3A0%2C%22maxTier%22%3A10%7D%2C%7B%22statHash%22%3A1735777505%2C%22minTier%22%3A0%2C%22maxTier%22%3A10%7D%2C%7B%22statHash%22%3A144602215%2C%22minTier%22%3A0%2C%22maxTier%22%3A10%7D%2C%7B%22statHash%22%3A4244567218%2C%22minTier%22%3A10%2C%22maxTier%22%3A10%7D%5D%2C%22mods%22%3A%5B1484685887%2C2979815167%2C2850583378%2C2850583378%5D%2C%22assumeArmorMasterwork%22%3A3%2C%22lockArmorEnergyType%22%3A1%2C%22exoticArmorHash%22%3A2773056939%7D%2C%22equipped%22%3A%5B%7B%22id%22%3A%226917529342535580813%22%2C%22hash%22%3A2773056939%7D%2C%7B%22id%22%3A%226917529801497037343%22%2C%22hash%22%3A1148597205%7D%2C%7B%22id%22%3A%226917529793250524983%22%2C%22hash%22%3A145651147%7D%2C%7B%22id%22%3A%226917529807139650343%22%2C%22hash%22%3A2724719415%7D%2C%7B%22id%22%3A%2212345%22%2C%22hash%22%3A2453351420%2C%22socketOverrides%22%3A%7B%227%22%3A2661180602%2C%228%22%3A2272984671%7D%7D%5D%2C%22unequipped%22%3A%5B%5D%2C%22clearSpace%22%3Afalse%7D

✅ src/app/loadout/mod-assignment-utils.test.ts

mod-assignment-utils plugging strategy
  ✅ reset assignments are present and required
  ✅ keeps existing mod in place if removal optional
  ✅ removes existing mod if needed
  ✅ prefers replacing mutual exclusion mod
  ✅ succeeds even if we choose not to replace in same slot

✅ src/app/register-service-worker.test.ts

isNewVersion
  ✅ should recognize two identical versions
  ✅ should newer versions
  ✅ should ignore older versions

✅ src/app/search/autocomplete.test.ts

autocompleteTermSuggestions
  ✅ autocomplete within query for plain string match {jotu} - {jötunn}
  ✅ autocomplete within query for {is:haspower is:b}
  ✅ autocomplete within query for {(is:blue ju|n)}
  ✅ autocomplete within query for {is:bow is:v|oid}
  ✅ autocomplete within query for {season:>outl}
  ✅ autocomplete within query for {not(}
  ✅ autocomplete within multi-word query for {arctic haz} should suggest {name:"arctic haze"}
  ✅ autocomplete within multi-word query for {is:weapon arctic haz| -is:exotic} should suggest {is:weapon name:"arctic haze" -is:exotic}
  ✅ autocomplete within multi-word query for {name:"arctic haz} should suggest {name:"arctic haze"}
  ✅ autocomplete within multi-word query for {name:'arctic haz} should suggest {name:"arctic haze"}
  ✅ autocomplete within multi-word query for {name:"foo" arctic haz} should suggest {name:"foo" name:"arctic haze"}
  ✅ autocomplete within multi-word query for {ager's sce} should suggest {name:"ager's scepter"}
  ✅ autocomplete within multi-word query for {the last word} should suggest {name:"the last word"}
  ✅ autocomplete within multi-word query for {acd/0 fee} should suggest {name:"acd/0 feedback fence"}
  ✅ autocomplete within multi-word query for {stat:rpm:200 first in, last} should suggest {stat:rpm:200 name:"first in, last out"}
  ✅ autocomplete within multi-word query for {two-tail} should suggest {name:"two-tailed fox"}
  ✅ autocomplete within multi-word query for {(is:a or is:b) and (is:c or multi w|)} should suggest {(is:a or is:b) and (is:c or name:"multi word")}
  ✅ autocomplete within multi-word query for {"rare curio" arctic haz} should suggest {"rare curio" name:"arctic haze"}
  ✅ autocomplete within multi-word query for {"rare curio" or arctic haz} should suggest {"rare curio" or name:"arctic haze"}
  ✅ autocomplete within multi-word query for {toil and trou} should suggest {name:"toil and trouble"}
  ✅ autocomplete within multi-word query for {perkname:"fate of} should suggest {perkname:"fate of all fools"}
  ✅ autocomplete within multi-word query for {perkname:fate of} should suggest {perkname:"fate of all fools"}
  ✅ autocomplete within multi-word query for {rare curio or arctic haz} should suggest {rare curio or name:"arctic haze"}
  ✅ autocomplete within multi-word query for {name:heritage arctic haze} should suggest {name:heritage name:"arctic haze"}
  ✅ autocomplete within multi-word query for {adept pali} should suggest {adept name:"the palindrome"}
filterSortRecentSearches
  ✅ filter/sort recent searches for query ||
  ✅ filter/sort recent searches for query |high|
  ✅ check saved search highlighting for query ||
  ✅ check saved search highlighting for query |craft|
  ✅ check saved search highlighting for query |craftable|
  ✅ check saved search highlighting for query |crafted|
filterComplete
  ✅ autocomplete terms for |is:b|
  ✅ autocomplete terms for |jun|
  ✅ autocomplete terms for |sni|
  ✅ autocomplete terms for |stat:mob|
  ✅ autocomplete terms for |stat|
  ✅ autocomplete terms for |stat:|
  ✅ autocomplete terms for |ote|

✅ src/app/search/loadouts/loadout-search-filter.test.ts

buildSearchConfig
  ✅ generates a reasonable filter map
  ✅ filter formats specify unambiguous formats

✅ src/app/search/query-parser.test.ts

✅ parse |is:blue is:haspower -is:maxpower|
✅ parse |is:blue is:haspower not:maxpower|
✅ parse |not:maxpower|
✅ parse |not -not:maxpower|
✅ parse |not not not:maxpower|
✅ parse |is:blue is:weapon or is:armor not:maxpower|
✅ parse |not not:maxpower|
✅ parse |-is:equipped is:haspower is:incurrentchar|
✅ parse |-source:garden -source:lastwish sunsetsafter:arrival|
✅ parse |-is:exotic -is:locked -is:maxpower -is:tagged stat:total:<55|
✅ parse |(is:weapon is:sniperrifle) or (is:armor modslot:arrival)|
✅ parse |(is:weapon and is:sniperrifle) or not (is:armor and modslot:arrival)|
✅ parse |is:weapon and is:sniperrifle or not is:armor and modslot:arrival|
✅ parse |is:weapon is:sniperrifle or not is:armor modslot:arrival|
✅ parse |is:weapon is:sniperrifle or is:armor and modslot:arrival|
✅ parse |is:weapon (is:sniperrifle or (is:armor and modslot:arrival))|
✅ parse |-(power:>1000 and -modslot:arrival)|
✅ parse |( power:>1000 and -modslot:arrival ) |
✅ parse |- is:exotic - (power:>1000)|
✅ parse |is:armor2.0|
✅ parse |not forgotten|
✅ parse |cluster tracking|
✅ parse |name:"Hard Light"|
✅ parse |name:'Hard Light'|
✅ parse |name:"Gahlran's Right Hand"|
✅ parse |-witherhoard|
✅ parse |perk:수집가|
✅ parse |perk:"수집가"|
✅ parse |"수집가"|
✅ parse |수집가|
✅ parse |is:rocketlauncher -"cluster" -"tracking module"|
✅ parse |is:rocketlauncher -"cluster" -'tracking module'|
✅ parse |is:rocketlauncher (perk:"cluster" or perk:"tracking module")|
✅ parse |(is:hunter power:>=540) or (is:warlock power:>=560)|
✅ parse |"grenade launcher reserves"|
✅ parse |“grenade launcher reserves”|
✅ parse |‘grenade launcher reserves’|
✅ parse |(("test" or "test") and "test")|
✅ parse |  (
    (
    (is:weapon -is:maxpower powerlimit:1060 or tag:junk or is:blue)
    or
    (
    (is:armor -is:exotic -is:classitem)
  
    -(is:titan (basestat:recovery:>=18 or basestat:total:>=63))
    -(is:hunter ((basestat:recovery:>=13 basestat:mobility:>=18) or basestat:recovery:>15 or basestat:total:>=63))
    -(is:warlock ((basestat:recovery:>=18 discipline:>=17) or basestat:total:>=63))
  
    -(
    ((basestat:mobility:>=18 basestat:resilience:>=13) or
    (basestat:mobility:>=18 basestat:recovery:>=13) or
    (basestat:mobility:>=18 basestat:discipline:>=13) or
    (basestat:mobility:>=18 basestat:intellect:>=13) or
    (basestat:mobility:>=18 basestat:strength:>=13) or
    (basestat:resilience:>=18 basestat:mobility:>=13) or
    (basestat:resilience:>=18 basestat:recovery:>=13) or
    (basestat:resilience:>=18 basestat:discipline:>=13) or
    (basestat:resilience:>=18 basestat:intellect:>=13) or
    (basestat:resilience:>=18 basestat:strength:>=13) or
    (basestat:recovery:>=18 basestat:mobility:>=13) or
    (basestat:recovery:>=18 basestat:resilience:>=13) or
    (basestat:recovery:>=18 basestat:discipline:>=13) or
    (basestat:recovery:>=18 basestat:intellect:>=13) or
    (basestat:recovery:>=18 basestat:strength:>=13) or
    (basestat:discipline:>=18 basestat:mobility:>=13) or
    (basestat:discipline:>=18 basestat:resilience:>=13) or
    (basestat:discipline:>=18 basestat:recovery:>=13) or
    (basestat:discipline:>=18 basestat:intellect:>=13) or
    (basestat:discipline:>=18 basestat:strength:>=13) or
    (basestat:intellect:>=18 basestat:mobility:>=13) or
    (basestat:intellect:>=18 basestat:resilience:>=13) or
    (basestat:intellect:>=18 basestat:recovery:>=13) or
    (basestat:intellect:>=18 basestat:discipline:>=13) or
    (basestat:intellect:>=18 basestat:strength:>=13) or
    (basestat:strength:>=18 basestat:mobility:>=13) or
    (basestat:strength:>=18 basestat:resilience:>=13) or
    (basestat:strength:>=18 basestat:recovery:>=13) or
    (basestat:strength:>=18 basestat:discipline:>=13) or
    (basestat:strength:>=18 basestat:intellect:>=13))
    )
  
    -(
    ((basestat:mobility:>=13 basestat:resilience:>=13 basestat:recovery:>=13) or
    (basestat:mobility:>=13 basestat:resilience:>=13 basestat:discipline:>=13) or
    (basestat:mobility:>=13 basestat:resilience:>=13 basestat:intellect:>=13) or
    (basestat:mobility:>=13 basestat:resilience:>=13 basestat:strength:>=13) or
    (basestat:mobility:>=13 basestat:recovery:>=13 basestat:discipline:>=13) or
    (basestat:mobility:>=13 basestat:recovery:>=13 basestat:intellect:>=13) or
    (basestat:mobility:>=13 basestat:recovery:>=13 basestat:strength:>=13) or
    (basestat:mobility:>=13 basestat:discipline:>=13 basestat:intellect:>=13) or
    (basestat:mobility:>=13 basestat:discipline:>=13 basestat:strength:>=13) or
    (basestat:mobility:>=13 basestat:intellect:>=13 basestat:strength:>=13) or
    (basestat:resilience:>=13 basestat:recovery:>=13 basestat:discipline:>=13) or
    (basestat:resilience:>=13 basestat:recovery:>=13 basestat:intellect:>=13) or
    (basestat:resilience:>=13 basestat:recovery:>=13 basestat:strength:>=13) or
    (basestat:resilience:>=13 basestat:discipline:>=13 basestat:intellect:>=13) or
    (basestat:resilience:>=13 basestat:discipline:>=13 basestat:strength:>=13) or
    (basestat:resilience:>=13 basestat:intellect:>=13 basestat:strength:>=13) or
    (basestat:recovery:>=13 basestat:discipline:>=13 basestat:intellect:>=13) or
    (basestat:recovery:>=13 basestat:discipline:>=13 basestat:strength:>=13) or
    (basestat:recovery:>=13 basestat:intellect:>=13 basestat:strength:>=13) or
    (basestat:discipline:>=13 basestat:intellect:>=13 basestat:strength:>=13))
    )
  
    -(basestat:mobility:>=8 basestat:resilience:>=8 basestat:recovery:>=8 basestat:discipline:>=8 basestat:intellect:>=8 basestat:strength:>=8)
    )
  
    or
    (is:classitem ((is:dupelower -is:modded) or (is:sunset))) 
    or
    (is:armor -powerlimit:>1060) 
    or
    (is:armor is:blue)
    )
    -tag:keep -tag:archive -tag:favorite -tag:infuse -is:maxpower -power:>=1260 -is:inloadout -is:masterwork
    )|
✅ parse |not forgotten|
✅ parse |not (forgotten)|
✅ parse |not "forgotten"|
✅ parse |gnawing hunger|
✅ parse |/* My cool search */ is:armor|
✅ parse |  /* My cool search */
 is:armor|
✅ parse |/* My cool search */ (/* armor */ is:armor and is:blue) or (/*weapons*/ is:weapon and perkname:"Kill Clip")|
✅ |is:blue is:weapon or is:armor not:maxpower| is equivalent to |is:blue and (is:weapon or is:armor) and -is:maxpower|
✅ |not forgotten| is equivalent to |-"forgotten"|
✅ |cluster tracking| is equivalent to |"cluster" and "tracking"|
✅ |is:weapon and is:sniperrifle or not is:armor and modslot:arrival| is equivalent to |(is:weapon and is:sniperrifle) or (-is:armor and modslot:arrival)|
✅ |is:weapon is:sniperrifle or not is:armor modslot:arrival| is equivalent to |is:weapon and (is:sniperrifle or -is:armor) and modslot:arrival|
✅ |is:weapon is:sniperrifle or is:armor and modslot:arrival| is equivalent to |is:weapon and (is:sniperrifle or (is:armor and modslot:arrival))|
✅ |is:rocketlauncher perk:"cluster" or perk:"tracking module"| is equivalent to |is:rocketlauncher (perk:"cluster" or perk:"tracking module")|
✅ |is:blue (is:rocketlauncher| is equivalent to |is:blue is:rocketlauncher|
✅ |  is:blue  | is equivalent to |is:blue|
✅ |is:blue is:haspower not:maxpower| is canonically |is:blue is:haspower -is:maxpower|
✅ |is:weapon and is:sniperrifle or not is:armor and modslot:arrival| is canonically |(is:weapon is:sniperrifle) or (-is:armor modslot:arrival)|
✅ |is:rocketlauncher perk:"cluster" or perk:'tracking module'| is canonically |is:rocketlauncher (perk:cluster or perk:"tracking module")|
✅ |( power:>1000 and -modslot:arrival ) | is canonically |power:>1000 -modslot:arrival|
✅ |food fight| is canonically |food and fight|
✅ |/* My cool search   */
 is:armor| is canonically |/* my cool search */ is:armor|
✅ |/* My cool search */ (/* armor */ is:armor and is:blue) or (/*weapons*/ is:weapon and perkname:"Kill Clip")| is canonically |/* my cool search */ (is:armor is:blue) or (is:weapon perkname:"kill clip")|
✅ |inloadout:"----<()>fast"| is canonically |inloadout:"----<()>fast"|
✅ |perkname:"foobar"| is canonically |perkname:foobar|
✅ |perkname:'foo bar'| is canonically |perkname:"foo bar"|
✅ |perkname:"foobar"| is canonically |perkname:foobar|
✅ |perkname:'foo"bar'| is canonically |perkname:'foo"bar'|
✅ |perkname:"foo\"bar"| is canonically |perkname:'foo"bar'|
✅ |perkname:'foo\"ba\'r'| is canonically |perkname:'foo"ba\'r'|
✅ |foobar| quoting roundtrip
✅ |Foo\bar| quoting roundtrip
✅ |My cool loadout| quoting roundtrip
✅ |My "cool" loadout| quoting roundtrip
✅ |My "cool" loadout's little brother| quoting roundtrip
✅ |My "cool" load\out's little brother| quoting roundtrip

✅ src/app/search/search-config.test.ts

buildSearchConfig
  ✅ generates a reasonable filter map
  ✅ filter formats specify unambiguous formats
validateQuery
  ✅ is: filter is:crafted - validity true
  ✅ is: filter not:crafted - validity true
  ✅ is: filter crafted:is - validity false
  ✅ query filter tag:favorite - validity true
  ✅ query filter tag:none - validity true
  ✅ query filter tag:any - validity false
  ✅ query filter is:tag - validity false
  ✅ query filter tag:<5 - validity false
  ✅ query filter tag:recovery:17 - validity false
  ✅ freeform filter notes:#hashtag - validity true
  ✅ freeform filter notes:verbatim:colon - validity true
  ✅ freeform filter notes"with spaces" - validity true
  ✅ freeform filter notes:<5 - validity true
  ✅ freeform filter is:notes - validity false
  ✅ mixed filter is:masterwork - validity true
  ✅ mixed filter masterwork:range - validity true
  ✅ mixed filter masterwork:<5 - validity true
  ✅ mixed filter masterwork:=5 - validity true
  ✅ mixed filter masterwork:5 - validity true
  ✅ mixed filter masterwork:rnage - validity false
  ✅ mixed filter masterwork:<range - validity false
  ✅ mixed filter masterwork:range:5 - validity false
  ✅ stat filter stat:recovery:5 - validity true
  ✅ stat filter stat:recovery:<=5 - validity true
  ✅ stat filter stat:recovery+discipline:>=5 - validity true
  ✅ stat filter stat:highest&secondhighest:>20 - validity true
  ✅ stat filter stat:highest&recovery+strength&strength&range:>20 - validity true
  ✅ stat filter basestat:range:>50 - validity true
  ✅ stat filter stat:badstat:>20 - validity false
  ✅ stat filter stat:badstat&badcombo:>20 - validity false
  ✅ stat filter stat:too&+many:>20 - validity false
  ✅ stat filter is:stat - validity false
  ✅ stat filter stat:recovery - validity false
  ✅ stat filter stat:=5 - validity false
  ✅ search string count:2 - validity true
  ✅ search string count:=2 - validity true
  ✅ search string count:<=2 - validity true
  ✅ search string count:<=2.5 - validity true
  ✅ search string is:count - validity false
  ✅ search string count:count - validity false
  ✅ search string count:recovery - validity false
  ✅ search string count:<=2:=2 - validity false
  ✅ search string count:<2> - validity false
  ✅ search string season:worthy - validity true
  ✅ search string season:<=worthy - validity true
  ✅ search string season:=worthy - validity true
  ✅ search string season:>=arrival - validity true
  ✅ search string season:222 - validity true
  ✅ search string season:=10 - validity true
  ✅ search string season:>2.5 - validity true
  ✅ search string is:season - validity false
  ✅ search string season:1redwar - validity false
  ✅ search string season:season - validity false
  ✅ search string season:arrivals - validity false
  ✅ search string season:arrivalworthy - validity false

✅ src/app/search/search-filter.test.ts

generateSuggestionsForFilter
  ✅ full suggestions for filter format 'undefined', keyword '[ 'a', 'b', 'c' ]' with suggestions undefined, overload undefined
  ✅ full suggestions for filter format 'query', keyword 'a' with suggestions [ 'b', 'c' ], overload undefined
  ✅ full suggestions for filter format 'stat', keyword 'a' with suggestions [ 'b', 'c' ], overload undefined
  ✅ full suggestions for filter format 'range', keyword 'a' with suggestions undefined, overload undefined
  ✅ full suggestions for filter format 'range', keyword 'a' with suggestions undefined, overload { worthy: 10, arrivals: 11 }
  ✅ full suggestions for filter format 'freeform', keyword 'a' with suggestions [ 'b', 'c' ], overload undefined
  ✅ full suggestions for filter format 'undefined', keyword '[ 'a' ]' with suggestions undefined, overload undefined
  ✅ full suggestions for filter format 'stat', keyword 'stat' with suggestions [
  'rpm',             'rof',
  'charge',          'impact',
  'handling',        'range',
  'stability',       'reload',
  'magazine',        'aimassist',
  'equipspeed',      'shieldduration',
  'velocity',        'blastradius',
  'recoildirection', 'drawtime',
  'zoom',            'airborne',
  'accuracy',        'swingspeed',
  'guardefficiency', 'guardresistance',
  'chargerate',      'guardendurance',
  'ammocapacity',    'mobility',
  'resilience',      'recovery',
  'discipline',      'intellect',
  'strength',        'total',
  'any'
], overload undefined
  ✅ full suggestions for filter format 'query', keyword 'maxstatvalue' with suggestions [
  'mobility',
  'resilience',
  'recovery',
  'discipline',
  'intellect',
  'strength',
  'total',
  'any'
], overload undefined
  ✅ full suggestions for filter format 'query', keyword 'maxstatvalue' with suggestions [
  'mobility',
  'resilience',
  'recovery',
  'discipline',
  'intellect',
  'strength',
  'total',
  'any'
], overload undefined
  ✅ full suggestions for filter format 'range', keyword 'energycapacity' with suggestions undefined, overload undefined
rangeStringToComparator
  ✅ rangeStringToComparator('<10')(8) === true
  ✅ rangeStringToComparator('<10')(10) === false
  ✅ rangeStringToComparator('>10')(10) === false
  ✅ rangeStringToComparator('>10')(15) === true
  ✅ rangeStringToComparator('<=10')(10) === true
  ✅ rangeStringToComparator('>=10')(10) === true
  ✅ rangeStringToComparator('<=10')(8) === true
  ✅ rangeStringToComparator('>=10')(15) === true
  ✅ rangeStringToComparator('<=10')(15) === false
  ✅ rangeStringToComparator('>=10')(8) === false
  ✅ rangeStringToComparator('=10')(10) === true
  ✅ rangeStringToComparator('=10')(15) === false
  ✅ rangeStringToComparator('10')(10) === true
rangeStringToComparatorWithOverloads
  ✅ rangeStringToComparatorWithOverloads('<worthy')(8) === true
  ✅ rangeStringToComparatorWithOverloads('<worthy')(10) === false
  ✅ rangeStringToComparatorWithOverloads('>10')(10) === false
  ✅ rangeStringToComparatorWithOverloads('>10')(15) === true
  ✅ rangeStringToComparatorWithOverloads('<=worthy')(10) === true
  ✅ rangeStringToComparatorWithOverloads('>=worthy')(10) === true
  ✅ rangeStringToComparatorWithOverloads('=worthy')(10) === true
  ✅ rangeStringToComparatorWithOverloads('=worthy')(15) === false
  ✅ rangeStringToComparatorWithOverloads('arrivals')(11) === true
  ✅ rangeStringToComparatorWithOverloads('arrivals')(10) === false

✅ src/app/settings/vault-grouping.test.ts

vaultWeaponGroupingSettingSelector
  ✅ returns the currently selected weapon grouping setting
vaultWeaponGroupingSelector
  ✅ returns the items ungrouped when no grouping is selected
  ✅ groups items by the currently selected weapon grouping

✅ src/app/store-stats/AccountCurrencies.test.tsx

✅ renders correctly with the basic currencies
✅ renders correctly with the basic currencies and synth stuff

✅ src/app/utils/collections.test.ts

count
  ✅ counts elements that match the predicate
objectifyArray
  ✅ keys objects by a property name that maps to an array
wrap
  ✅ negative index
  ✅ too large index
  ✅ too large index by a lot
  ✅ negative index by a lot
uniqBy
  ✅ identity
  ✅ object values
  ✅ complex func

✅ src/app/utils/intl.test.ts

✅ should include a sorting test case for every supported DIM language
✅ localizedSorter: en
✅ localizedSorter: de
✅ localizedSorter: ko
✅ localizedSorter: ja
✅ localizedSorter: es
✅ localizedSorter: es-mx
✅ localizedSorter: fr
✅ localizedSorter: it
✅ localizedSorter: pl
✅ localizedSorter: pt-br
✅ localizedSorter: ru
✅ localizedSorter: zh-chs
✅ localizedSorter: zh-cht
✅ should include an include test case for every supported DIM language
✅ localizedIncludes("en", "bar")("foobar") === true
✅ localizedIncludes("en", "😀")("Laughing 😀!!") === true
✅ localizedIncludes("en", "�")("Laughing 😀!!") === true
✅ localizedIncludes("en", "👨‍👩‍👧")("👨‍👩‍👧‍👦") === true
✅ localizedIncludes("en", "föo")("foobar") === true
✅ localizedIncludes("en", "Föo")("foobar") === true
✅ localizedIncludes("de", "föo")("foobar") === false
✅ localizedIncludes("ko", "가")("하가") === true
✅ localizedIncludes("ja", "かさ")("あかさ赤け") === true
✅ localizedIncludes("es", "ño")("niño") === true
✅ localizedIncludes("es-mx", "ño")("niño") === true
✅ localizedIncludes("fr", "bar")("foobar") === true
✅ localizedIncludes("it", "bar")("foobar") === true
✅ localizedIncludes("pl", "bar")("foobar") === true
✅ localizedIncludes("pt-br", "bar")("foobar") === true
✅ localizedIncludes("ru", "bar")("foobar") === true
✅ localizedIncludes("zh-chs", "bar")("foobar") === true
✅ localizedIncludes("zh-cht", "bar")("foobar") === true

✅ src/app/utils/memoize.test.ts

weakMemoize
  ✅ caches results of computation

✅ src/app/utils/promises.test.ts

dedupePromise
  ✅ caches inflight promises

✅ src/app/utils/time.test.ts

✅ timerDurationFromMs(1000) === "0:00:01"
✅ timerDurationFromMs(0) === "0:00:00"
✅ timerDurationFromMs(279241234) === "3:05:34:01"
✅ timerDurationFromMs(20041234) === "5:34:01"
✅ timerDurationFromMs(1000) === "0:01"
✅ timerDurationFromMs(0) === "0:00"
✅ timerDurationFromMs(279241234) === "3:05:34:01.234"
✅ timerDurationFromMs(20041234) === "5:34:01.234"
english localization
  ✅ i15dDurationFromMs(1000) === "0:00"
  ✅ i15dDurationFromMs(0) === "0:00"
  ✅ i15dDurationFromMs(86400000) === "1 Day 0:00"
  ✅ i15dDurationFromMs(279241234) === "3 Days 5:34"
  ✅ i15dDurationFromMs(20041234) === "5:34"
  ✅ i15dDurationFromMs(1000) === "0:00"
  ✅ i15dDurationFromMs(0) === "0:00"
  ✅ i15dDurationFromMs(86400000) === "1d 0:00"
  ✅ i15dDurationFromMs(279241234) === "3d 5:34"
  ✅ i15dDurationFromMs(20041234) === "5:34"
  ✅ i15dDurationFromMs(1000) === "0:00:01"
  ✅ i15dDurationFromMs(0) === "0:00:00"
  ✅ i15dDurationFromMs(279241234) === "3 Days 5:34:01"
  ✅ i15dDurationFromMs(20041234) === "5:34:01"
japanese localization
  ✅ i15dDurationFromMs(1000) === "0:00"
  ✅ i15dDurationFromMs(0) === "0:00"
  ✅ i15dDurationFromMs(86400000) === "1日 0:00"
  ✅ i15dDurationFromMs(279241234) === "3日間 5:34"
  ✅ i15dDurationFromMs(20041234) === "5:34"
  ✅ i15dDurationFromMs(1000) === "0:00"
  ✅ i15dDurationFromMs(0) === "0:00"
  ✅ i15dDurationFromMs(86400000) === "1日 0:00"
  ✅ i15dDurationFromMs(279241234) === "3日間 5:34"
  ✅ i15dDurationFromMs(20041234) === "5:34"
  ✅ i15dDurationFromMs(1000) === "0:00:01"
  ✅ i15dDurationFromMs(0) === "0:00:00"
  ✅ i15dDurationFromMs(279241234) === "3日間 5:34:01"
  ✅ i15dDurationFromMs(20041234) === "5:34:01"

✅ src/app/utils/undo-redo-history.test.ts

✅ allows you to undo and redo loadout edits

✅ src/app/vendors/d2-vendors.test.ts

process vendors
  ✅ can process vendors without errors

✅ src/app/wishlists/wishlist-file.test.ts

✅ parse wishlist line: dimwishlist:item=-69420&perks=2682205016,2402480669#notes:Enh Over, Enh FocuFury

✅ src/browsercheck.test.ts

✅ Firefox 72: User agent Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0, supported: true
✅ Chrome 79: User agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36, supported: true
✅ iOS 12: User agent Mozilla/5.0 (iPod; CPU iPhone OS 12_0 like macOS) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/12.0 Mobile/14A5335d Safari/602.1.50, supported: true
✅ Edge 18: User agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362, supported: true
✅ Vivaldi: User agent Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36 Vivaldi/2.10, supported: true
✅ Opera: User agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36 OPR/65.0.3467.78, supported: true
✅ Old Chrome: User agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36, supported: false

✅ src/testing/precache-manifest.test.ts

✅ precache manifest