-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# Motivation If there are inactive neurons, the user should be able to reset their voting-power-refreshed timestamp. In this PR, we add a button that makes the necessary service calls to refresh the user’s neurons. # Changes - Add `ConfirmFollowingButton` component. # Tests - Added. - Tested locally: https://github.com/user-attachments/assets/6e71aace-4f5f-4426-9896-2de33bec290e # Todos - [ ] Add entry to changelog (if necessary). Not necessary.
- Loading branch information
1 parent
96920de
commit ac1e65b
Showing
6 changed files
with
156 additions
and
0 deletions.
There are no files selected for viewing
32 changes: 32 additions & 0 deletions
32
frontend/src/lib/components/neuron-detail/actions/ConfirmFollowingButton.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<script lang="ts"> | ||
import { i18n } from "$lib/stores/i18n"; | ||
import { startBusy, stopBusy } from "$lib/stores/busy.store"; | ||
import { refreshVotingPowerForNeurons } from "$lib/services/neurons.services"; | ||
import { createEventDispatcher } from "svelte"; | ||
export let neuronIds: bigint[]; | ||
const dispatcher = createEventDispatcher<{ | ||
nnsComplete: { successCount: number; totalCount: number }; | ||
}>(); | ||
const onClick = async () => { | ||
startBusy({ | ||
initiator: "refresh-voting-power", | ||
labelKey: "losing_rewards.confirming", | ||
}); | ||
const totalCount = neuronIds.length; | ||
const { successCount } = await refreshVotingPowerForNeurons({ neuronIds }); | ||
stopBusy("refresh-voting-power"); | ||
dispatcher("nnsComplete", { successCount, totalCount }); | ||
}; | ||
</script> | ||
|
||
<button | ||
on:click={onClick} | ||
class="secondary" | ||
data-tid="confirm-following-button-component" | ||
>{$i18n.losing_rewards.confirm}</button | ||
> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
frontend/src/tests/lib/components/neuron-detail/actions/ConfirmFollowingButton.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import * as api from "$lib/api/governance.api"; | ||
import ConfirmFollowingButton from "$lib/components/neuron-detail/actions/ConfirmFollowingButton.svelte"; | ||
import { neuronsStore } from "$lib/stores/neurons.store"; | ||
import { mockIdentity, resetIdentity } from "$tests/mocks/auth.store.mock"; | ||
import { mockFullNeuron, mockNeuron } from "$tests/mocks/neurons.mock"; | ||
import { ConfirmFollowingButtonPo } from "$tests/page-objects/ConfirmFollowingButton.page-object"; | ||
import { JestPageObjectElement } from "$tests/page-objects/jest.page-object"; | ||
import { allowLoggingInOneTestForDebugging } from "$tests/utils/console.test-utils"; | ||
import { runResolvedPromises } from "$tests/utils/timers.test-utils"; | ||
import { busyStore } from "@dfinity/gix-components"; | ||
import { nonNullish } from "@dfinity/utils"; | ||
import { render } from "@testing-library/svelte"; | ||
import { get } from "svelte/store"; | ||
|
||
describe("ConfirmFollowingButton", () => { | ||
const neuronId1 = 1n; | ||
const neuronId2 = 22n; | ||
const neuronIds = [neuronId1, neuronId2]; | ||
const neurons = neuronIds.map((neuronId) => ({ | ||
...mockNeuron, | ||
neuronId, | ||
fullNeuron: { | ||
...mockFullNeuron, | ||
neuronId, | ||
controller: mockIdentity.getPrincipal().toText(), | ||
}, | ||
})); | ||
|
||
let spyQueryNeurons; | ||
|
||
beforeEach(() => { | ||
resetIdentity(); | ||
allowLoggingInOneTestForDebugging(); | ||
|
||
neuronsStore.pushNeurons({ neurons, certified: true }); | ||
spyQueryNeurons = vi.spyOn(api, "queryNeurons").mockResolvedValue(neurons); | ||
vi.spyOn(api, "refreshVotingPower").mockResolvedValue(); | ||
}); | ||
|
||
const renderComponent = async ({ nnsComplete = undefined, ...props }) => { | ||
const { container, component } = render(ConfirmFollowingButton, props); | ||
|
||
if (nonNullish(nnsComplete)) component.$on("nnsComplete", nnsComplete); | ||
|
||
return ConfirmFollowingButtonPo.under(new JestPageObjectElement(container)); | ||
}; | ||
|
||
it("displays the button", async () => { | ||
const po = await renderComponent({ | ||
neuronIds: [neuronId1, neuronId2], | ||
}); | ||
|
||
expect(await po.getText()).toBe("Confirm Following"); | ||
}); | ||
|
||
it("returns the refreshed neuron count", async () => { | ||
vi.spyOn(console, "error").mockReturnValue(); | ||
|
||
neuronsStore.pushNeurons({ neurons, certified: true }); | ||
let resolveRefreshVotingPower; | ||
const spyRefreshVotingPower = vi | ||
.spyOn(api, "refreshVotingPower") | ||
.mockRejectedValueOnce(new Error()) | ||
.mockImplementationOnce( | ||
() => new Promise((resolve) => (resolveRefreshVotingPower = resolve)) | ||
); | ||
|
||
expect(spyQueryNeurons).toBeCalledTimes(0); | ||
expect(spyRefreshVotingPower).toBeCalledTimes(0); | ||
|
||
const nnsComplete = vi.fn(); | ||
const po = await renderComponent({ | ||
neuronIds: [neuronId1, neuronId2], | ||
nnsComplete, | ||
}); | ||
|
||
expect(get(busyStore)).toEqual([]); | ||
|
||
await po.click(); | ||
|
||
// Wait for busy screen to show | ||
await runResolvedPromises(); | ||
expect(get(busyStore)).toEqual([ | ||
{ | ||
initiator: "refresh-voting-power", | ||
text: "Confirming following. This may take a moment.", | ||
}, | ||
]); | ||
|
||
resolveRefreshVotingPower(); | ||
|
||
// Wait for busy screen to hide | ||
await runResolvedPromises(); | ||
expect(get(busyStore)).toEqual([]); | ||
|
||
expect(spyQueryNeurons).toBeCalledTimes(2); | ||
expect(spyRefreshVotingPower).toBeCalledTimes(2); | ||
|
||
expect(nnsComplete).toBeCalledTimes(1); | ||
expect(nnsComplete).toBeCalledWith( | ||
expect.objectContaining({ | ||
detail: { | ||
successCount: 1, | ||
totalCount: 2, | ||
}, | ||
}) | ||
); | ||
}); | ||
}); |
12 changes: 12 additions & 0 deletions
12
frontend/src/tests/page-objects/ConfirmFollowingButton.page-object.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { BasePageObject } from "$tests/page-objects/base.page-object"; | ||
import type { PageObjectElement } from "$tests/types/page-object.types"; | ||
|
||
export class ConfirmFollowingButtonPo extends BasePageObject { | ||
private static readonly TID = "confirm-following-button-component"; | ||
|
||
static under(element: PageObjectElement): ConfirmFollowingButtonPo { | ||
return new ConfirmFollowingButtonPo( | ||
element.byTestId(ConfirmFollowingButtonPo.TID) | ||
); | ||
} | ||
} |