From f84b0fcc7b3edea44ff8e207e2cf68134fe54a1d Mon Sep 17 00:00:00 2001 From: Max Strasinsky <98811342+mstrasinskis@users.noreply.github.com> Date: Mon, 23 Dec 2024 10:37:43 +0000 Subject: [PATCH] Nns missing rewards indicator (#6068) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Motivation When the user has Nns neurons that haven’t been active and are set to start losing rewards in 30 days or less, a red circle needs to be displayed next to the “Neuron Staking” sidebar item and on in the Neuron Staking page table to indicate that action is required. In this PR, we introduce the indicator component, which will be used in the navigation menu and the projects table. # Changes - New `NnsNeuronsMissingRewardsBadge` component. # Tests - Added. # Todos - [ ] Add entry to changelog (if necessary). Not necessary. --- .../NnsNeuronsMissingRewardsBadge.svelte | 32 ++++++++ frontend/src/lib/i18n/en.json | 1 + frontend/src/lib/types/i18n.d.ts | 1 + .../NnsNeuronsMissingRewardsBadge.spec.ts | 75 +++++++++++++++++++ ...sNeuronsMissingRewardsBadge.page-object.ts | 16 ++++ 5 files changed, 125 insertions(+) create mode 100644 frontend/src/lib/components/neurons/NnsNeuronsMissingRewardsBadge.svelte create mode 100644 frontend/src/tests/lib/components/neurons/NnsNeuronsMissingRewardsBadge.spec.ts create mode 100644 frontend/src/tests/page-objects/NnsNeuronsMissingRewardsBadge.page-object.ts diff --git a/frontend/src/lib/components/neurons/NnsNeuronsMissingRewardsBadge.svelte b/frontend/src/lib/components/neurons/NnsNeuronsMissingRewardsBadge.svelte new file mode 100644 index 0000000000..6f1ff4f03f --- /dev/null +++ b/frontend/src/lib/components/neurons/NnsNeuronsMissingRewardsBadge.svelte @@ -0,0 +1,32 @@ + + + + {#if $soonLosingRewardNeuronsStore.length > 0} + + {/if} + + + diff --git a/frontend/src/lib/i18n/en.json b/frontend/src/lib/i18n/en.json index 376ae424e7..18d43d6b05 100644 --- a/frontend/src/lib/i18n/en.json +++ b/frontend/src/lib/i18n/en.json @@ -394,6 +394,7 @@ "description": "ICP neurons that are inactive for $period start missing voting rewards. To avoid missing rewards, vote manually, edit, or confirm your following.", "confirming": "Confirming following. This may take a moment.", "confirm": "Confirm Following", + "badge_label": "Confirm following to avoid missing rewards", "hw_hotkey_warning": "You may have neurons that are not added to the NNS dapp. If you want to view your neurons and get alerts in case they start missing voting rewards, you can add them by clicking Show Neurons > Add to NNS Dapp." }, "losing_rewards_banner": { diff --git a/frontend/src/lib/types/i18n.d.ts b/frontend/src/lib/types/i18n.d.ts index d133c8809c..3c6be6f97f 100644 --- a/frontend/src/lib/types/i18n.d.ts +++ b/frontend/src/lib/types/i18n.d.ts @@ -408,6 +408,7 @@ interface I18nLosing_rewards { description: string; confirming: string; confirm: string; + badge_label: string; hw_hotkey_warning: string; } diff --git a/frontend/src/tests/lib/components/neurons/NnsNeuronsMissingRewardsBadge.spec.ts b/frontend/src/tests/lib/components/neurons/NnsNeuronsMissingRewardsBadge.spec.ts new file mode 100644 index 0000000000..d5bb2ea8c0 --- /dev/null +++ b/frontend/src/tests/lib/components/neurons/NnsNeuronsMissingRewardsBadge.spec.ts @@ -0,0 +1,75 @@ +import NnsNeuronsMissingRewardsBadge from "$lib/components/neurons/NnsNeuronsMissingRewardsBadge.svelte"; +import { SECONDS_IN_HALF_YEAR } from "$lib/constants/constants"; +import { neuronsStore } from "$lib/stores/neurons.store"; +import { nowInSeconds } from "$lib/utils/date.utils"; +import { mockIdentity } from "$tests/mocks/auth.store.mock"; +import { mockFullNeuron, mockNeuron } from "$tests/mocks/neurons.mock"; +import { NnsNeuronsMissingRewardsBadgePo } from "$tests/page-objects/NnsNeuronsMissingRewardsBadge.page-object"; +import { JestPageObjectElement } from "$tests/page-objects/jest.page-object"; +import { render } from "$tests/utils/svelte.test-utils"; + +describe("NnsNeuronsMissingRewardsBadge", () => { + const nowSeconds = nowInSeconds(); + const activeNeuron = { + ...mockNeuron, + neuronId: 0n, + fullNeuron: { + ...mockFullNeuron, + votingPowerRefreshedTimestampSeconds: BigInt(nowSeconds), + controller: mockIdentity.getPrincipal().toText(), + }, + }; + const losingRewardsNeuron = { + ...mockNeuron, + neuronId: 2n, + fullNeuron: { + ...mockFullNeuron, + votingPowerRefreshedTimestampSeconds: BigInt( + nowSeconds - SECONDS_IN_HALF_YEAR + ), + controller: mockIdentity.getPrincipal().toText(), + }, + }; + const renderComponent = async () => { + const { container } = render(NnsNeuronsMissingRewardsBadge); + return NnsNeuronsMissingRewardsBadgePo.under( + new JestPageObjectElement(container) + ); + }; + + beforeEach(() => { + vi.useFakeTimers({ + now: nowSeconds * 1000, + }); + }); + + it("should be visible when there is an inactive neuron", async () => { + neuronsStore.setNeurons({ + neurons: [activeNeuron, losingRewardsNeuron], + certified: true, + }); + const po = await renderComponent(); + + expect(await po.isVisible()).toBe(true); + }); + + it("should be hidden when there is no inactive neurons", async () => { + neuronsStore.setNeurons({ + neurons: [activeNeuron], + certified: true, + }); + const po = await renderComponent(); + + expect(await po.isVisible()).toBe(false); + }); + + it("should be hidden when there is no neurons", async () => { + neuronsStore.setNeurons({ + neurons: [], + certified: true, + }); + const po = await renderComponent(); + + expect(await po.isVisible()).toBe(false); + }); +}); diff --git a/frontend/src/tests/page-objects/NnsNeuronsMissingRewardsBadge.page-object.ts b/frontend/src/tests/page-objects/NnsNeuronsMissingRewardsBadge.page-object.ts new file mode 100644 index 0000000000..b2df3bafd0 --- /dev/null +++ b/frontend/src/tests/page-objects/NnsNeuronsMissingRewardsBadge.page-object.ts @@ -0,0 +1,16 @@ +import { BasePageObject } from "$tests/page-objects/base.page-object"; +import type { PageObjectElement } from "$tests/types/page-object.types"; + +export class NnsNeuronsMissingRewardsBadgePo extends BasePageObject { + private static readonly TID = "nns-neurons-missing-rewards-badge-component"; + + static under(element: PageObjectElement): NnsNeuronsMissingRewardsBadgePo { + return new NnsNeuronsMissingRewardsBadgePo( + element.byTestId(NnsNeuronsMissingRewardsBadgePo.TID) + ); + } + + isVisible(): Promise { + return this.root.byTestId("badge").isPresent(); + } +}