Fix BountyType with new PalletBountiesBounty (#11204)

* fix: types for Bounties test

* fix: types for bounties page and related components
This commit is contained in:
Arjun Porwal 2025-01-15 20:28:22 +05:30 committed by GitHub
parent a9118af7e7
commit e97d1567cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 58 additions and 70 deletions

View File

@ -8,7 +8,7 @@
import type { ApiPromise } from '@polkadot/api';
import type { SubmittableExtrinsic } from '@polkadot/api/types';
import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import type { BountyIndex, BountyStatus } from '@polkadot/types/interfaces';
import type { BountyIndex } from '@polkadot/types/interfaces';
import type { PalletBountiesBounty, PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import { fireEvent } from '@testing-library/react';
@ -55,7 +55,7 @@ let aProposal: (extrinsic: SubmittableExtrinsic<'promise'>, ayes?: string[], nay
let augmentedApi: ApiPromise;
let aBounty: ({ status, value }?: Partial<PalletBountiesBounty>) => PalletBountiesBounty;
let aBountyIndex: (index?: number) => BountyIndex;
let bountyStatusWith: ({ curator, status, updateDue }: { curator?: string, status?: string, updateDue?: number}) => BountyStatus;
let bountyStatusWith: ({ curator, status, updateDue }: { curator?: string, status?: string, updateDue?: number}) => PalletBountiesBountyStatus;
let bountyWith: ({ status, value }: { status?: string, value?: number }) => PalletBountiesBounty;
describe('Bounties', () => {
@ -347,8 +347,7 @@ describe('Bounties', () => {
describe('Reject curator modal', () => {
it('creates extrinsic', async () => {
// FIXME: https://github.com/polkadot-js/apps/issues/11192
const bounty = aBounty({ status: bountyStatusWith({ curator: bob, status: 'CuratorProposed' }) as unknown as PalletBountiesBountyStatus });
const bounty = aBounty({ status: bountyStatusWith({ curator: bob, status: 'CuratorProposed' }) });
bountiesPage.renderOne(bounty);
@ -359,8 +358,7 @@ describe('Bounties', () => {
});
it('shows options for all roles', async () => {
// FIXME: https://github.com/polkadot-js/apps/issues/11192
const bounty = aBounty({ status: bountyStatusWith({ curator: bob, status: 'Active' }) as unknown as PalletBountiesBountyStatus });
const bounty = aBounty({ status: bountyStatusWith({ curator: bob, status: 'Active' }) });
bountiesPage.renderOne(bounty);
@ -374,8 +372,7 @@ describe('Bounties', () => {
it('creates extrinsic', async () => {
const bounty = aBounty({
fee: balanceOf(20),
// FIXME: https://github.com/polkadot-js/apps/issues/11192
status: bountyStatusWith({ curator: bob, status: 'CuratorProposed' }) as unknown as PalletBountiesBountyStatus
status: bountyStatusWith({ curator: bob, status: 'CuratorProposed' })
});
bountiesPage.renderOne(bounty);
@ -393,8 +390,7 @@ describe('Bounties', () => {
describe('extend bounty expiry action modal', () => {
it('queues extend bounty expiry extrinsic on submit', async () => {
// FIXME: https://github.com/polkadot-js/apps/issues/11192
const bounty = aBounty({ status: bountyStatusWith({ curator: alice }) as unknown as PalletBountiesBountyStatus });
const bounty = aBounty({ status: bountyStatusWith({ curator: alice }) });
bountiesPage.renderOne(bounty);
await bountiesPage.openExtendExpiry();
@ -409,8 +405,7 @@ describe('Bounties', () => {
describe('give up curator modal', () => {
it('gives up on the Curator role of an Active bounty', async () => {
// FIXME: https://github.com/polkadot-js/apps/issues/11192
const bounty = aBounty({ status: bountyStatusWith({ curator: alice }) as unknown as PalletBountiesBountyStatus });
const bounty = aBounty({ status: bountyStatusWith({ curator: alice }) });
bountiesPage.renderOne(bounty);
await bountiesPage.openGiveUpCuratorsRole();
@ -439,8 +434,7 @@ describe('Bounties', () => {
describe('award beneficiary action modal', () => {
it('awards the beneficiary', async () => {
// FIXME: https://github.com/polkadot-js/apps/issues/11192
const bounty = aBounty({ status: bountyStatusWith({ curator: alice }) as unknown as PalletBountiesBountyStatus });
const bounty = aBounty({ status: bountyStatusWith({ curator: alice }) });
bountiesPage.renderOne(bounty);
@ -460,8 +454,7 @@ describe('Bounties', () => {
curator: alice,
status: 'Active',
updateDue: defaultBountyUpdatePeriod.muln(BLOCKS_PERCENTAGE_LEFT_TO_SHOW_WARNING).divn(100).toNumber() - 1
// FIXME: https://github.com/polkadot-js/apps/issues/11192
}) as unknown as PalletBountiesBountyStatus });
}) });
bountiesPage.renderOne(bounty);
@ -474,8 +467,7 @@ describe('Bounties', () => {
curator: alice,
status: 'Active',
updateDue: mockBountyHooks.bountyApi.bestNumber?.toNumber()
// FIXME: https://github.com/polkadot-js/apps/issues/11192
}) as unknown as PalletBountiesBountyStatus });
}) });
bountiesPage.renderOne(bounty);
@ -511,8 +503,7 @@ describe('Bounties', () => {
curator: alice,
status: 'Active',
updateDue: defaultBountyUpdatePeriod.muln(BLOCKS_PERCENTAGE_LEFT_TO_SHOW_WARNING).divn(100).toNumber() + 1
// FIXME: https://github.com/polkadot-js/apps/issues/11192
}) as unknown as PalletBountiesBountyStatus });
}) });
bountiesPage.renderOne(bounty);
await bountiesPage.rendered();

View File

@ -1,8 +1,6 @@
// Copyright 2017-2025 @polkadot/app-bounties authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Bounty as BountyType } from '@polkadot/types/interfaces';
import React, { useMemo, useRef } from 'react';
import { Button, styled, Table } from '@polkadot/react-components';
@ -48,10 +46,7 @@ function Bounties ({ className }: Props): React.ReactElement {
{sorted && bestNumber && sorted.map(({ bounty, description, index, proposals }) => (
<Bounty
bestNumber={bestNumber}
// TODO: The bounty type here is now expected as PalletBountiesBounty type in the latest substrate release.
// This will require forward and backwards compatibility with the older Bounty type and the newer lookup type.
// ref: https://github.com/polkadot-js/apps/issues/11192
bounty={bounty as unknown as BountyType}
bounty={bounty}
description={description}
index={index}
key={index.toNumber()}

View File

@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import type { Bounty as BountyType, BountyIndex } from '@polkadot/types/interfaces';
import type { BountyIndex } from '@polkadot/types/interfaces';
import type { PalletBountiesBounty } from '@polkadot/types/lookup';
import type { BN } from '@polkadot/util';
import React, { useMemo } from 'react';
@ -25,7 +26,7 @@ import VotersColumn from './VotersColumn.js';
interface Props {
bestNumber: BN;
bounty: BountyType;
bounty: PalletBountiesBounty;
className?: string;
description: string;
index: BountyIndex;

View File

@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import type { Balance, BountyIndex, BountyStatus } from '@polkadot/types/interfaces';
import type { Balance, BountyIndex } from '@polkadot/types/interfaces';
import type { PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import type { BN } from '@polkadot/util';
import React, { useMemo } from 'react';
@ -20,7 +21,7 @@ interface Props {
fee?: BN;
index: BountyIndex;
proposals?: DeriveCollectiveProposal[];
status: BountyStatus;
status: PalletBountiesBountyStatus;
value: Balance;
}

View File

@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import type { BountyIndex, BountyStatus } from '@polkadot/types/interfaces';
import type { BountyIndex } from '@polkadot/types/interfaces';
import type { PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import type { BN } from '@polkadot/util';
import type { ValidUnassignCuratorAction } from '../types.js';
@ -26,7 +27,7 @@ interface Props {
description: string;
index: BountyIndex;
proposals?: DeriveCollectiveProposal[];
status: BountyStatus;
status: PalletBountiesBountyStatus;
}
function Index ({ bestNumber, className, description, index, proposals, status }: Props): React.ReactElement<Props> | null {

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import type { BountyStatus } from '@polkadot/types/interfaces';
import type { PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import React, { useRef } from 'react';
@ -14,7 +14,7 @@ import { useTranslation } from '../translate.js';
interface Props {
className?: string;
proposal: DeriveCollectiveProposal;
status: BountyStatus;
status: PalletBountiesBountyStatus;
}
function VotingDescriptionInfo ({ className, proposal, status }: Props): React.ReactElement<Props> {

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import type { BountyStatus } from '@polkadot/types/interfaces';
import type { PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import React, { useMemo } from 'react';
@ -16,7 +16,7 @@ import VotingLink from './VotingLink.js';
interface Props {
className?: string;
proposal: DeriveCollectiveProposal;
status: BountyStatus;
status: PalletBountiesBountyStatus;
}
function VotingSummary ({ className, proposal, status }: Props): React.ReactElement<Props> {

View File

@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import type { AccountId, BountyStatus } from '@polkadot/types/interfaces';
import type { AccountId } from '@polkadot/types/interfaces';
import type { PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import React, { useMemo } from 'react';
@ -16,7 +17,7 @@ import VotingSummary from './VotingSummary.js';
interface Props {
beneficiary?: AccountId;
proposals?: DeriveCollectiveProposal[];
status: BountyStatus;
status: PalletBountiesBountyStatus;
}
function BountyInfos ({ beneficiary, proposals, status }: Props): React.ReactElement<Props> {

View File

@ -1,7 +1,7 @@
// Copyright 2017-2025 @polkadot/app-bounties authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BountyStatus } from '@polkadot/types/interfaces';
import type { PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import React, { useMemo } from 'react';
@ -14,7 +14,7 @@ import BountyInfo from './BountyInfo.js';
interface Props {
bestNumber: BN;
blocksUntilUpdate?: BN;
status: BountyStatus;
status: PalletBountiesBountyStatus;
}
export const BLOCKS_PERCENTAGE_LEFT_TO_SHOW_WARNING = 10;

View File

@ -3,7 +3,7 @@
import type { IconName } from '@fortawesome/fontawesome-svg-core';
import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import type { BountyStatus } from '@polkadot/types/interfaces';
import type { PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import React, { useMemo } from 'react';
@ -16,7 +16,7 @@ interface Props {
className?: string;
option: 'ayes' | 'nays';
proposals: DeriveCollectiveProposal[];
status: BountyStatus;
status: PalletBountiesBountyStatus;
}
const icons: Record<string, IconName> = {

View File

@ -1,13 +1,13 @@
// Copyright 2017-2025 @polkadot/app-bounties authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BountyStatus } from '@polkadot/types/interfaces';
import type { PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import type { BN } from '@polkadot/util';
import type { UserRole, ValidUnassignCuratorAction } from '../types.js';
import { BN_ZERO } from '@polkadot/util';
export function determineUnassignCuratorAction (roles: UserRole[], status: BountyStatus, blocksUntilUpdate?: BN): ValidUnassignCuratorAction[] {
export function determineUnassignCuratorAction (roles: UserRole[], status: PalletBountiesBountyStatus, blocksUntilUpdate?: BN): ValidUnassignCuratorAction[] {
const actions: ValidUnassignCuratorAction[] = [];
if (status.isCuratorProposed && roles.includes('Member')) {

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import type { BountyStatus } from '@polkadot/types/interfaces';
import type { PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import type { BountyVotingStatuses, StatusName } from '../types.js';
const validProposalNames: BountyVotingStatuses = {
@ -14,7 +14,7 @@ const validProposalNames: BountyVotingStatuses = {
Proposed: ['approveBounty', 'closeBounty']
};
function validMethods (status: BountyStatus): string[] {
function validMethods (status: PalletBountiesBountyStatus): string[] {
return validProposalNames[status.type as StatusName];
}
@ -22,13 +22,13 @@ function getProposalByMethod (bountyProposals: DeriveCollectiveProposal[], metho
return bountyProposals.find(({ proposal }) => proposal && proposal.method === method);
}
function bestValidProposalName (bountyProposals: DeriveCollectiveProposal[], status: BountyStatus): string | undefined {
function bestValidProposalName (bountyProposals: DeriveCollectiveProposal[], status: PalletBountiesBountyStatus): string | undefined {
const methods = bountyProposals.map(({ proposal }) => proposal?.method);
return validMethods(status).find((method) => methods.includes(method));
}
export function proposalNameToDisplay (bountyProposal: DeriveCollectiveProposal, status: BountyStatus): string | undefined {
export function proposalNameToDisplay (bountyProposal: DeriveCollectiveProposal, status: PalletBountiesBountyStatus): string | undefined {
if (bountyProposal.proposal && bountyProposal.proposal.method !== 'unassignCurator') {
return bountyProposal.proposal.method;
}
@ -36,7 +36,7 @@ export function proposalNameToDisplay (bountyProposal: DeriveCollectiveProposal,
return status.isCuratorProposed ? 'unassignCurator' : 'slashCurator';
}
export function getProposalToDisplay (bountyProposals: DeriveCollectiveProposal[], status: BountyStatus): DeriveCollectiveProposal | null {
export function getProposalToDisplay (bountyProposals: DeriveCollectiveProposal[], status: PalletBountiesBountyStatus): DeriveCollectiveProposal | null {
const method = bestValidProposalName(bountyProposals, status);
return getProposalByMethod(bountyProposals, method) ?? null;

View File

@ -15,14 +15,14 @@ describe('get bounty status', () => {
});
it('for CuratorProposed state it has curator defined', () => {
const bountyStatus = getBountyStatus(registry.createType('BountyStatus', 'CuratorProposed'));
const bountyStatus = getBountyStatus(registry.createType('PalletBountiesBountyStatus', 'CuratorProposed'));
expect(bountyStatus.bountyStatus).toEqual('CuratorProposed');
expect(bountyStatus.curator).toBeDefined();
});
it('for Active state it has curator and update due defined', () => {
const bountyStatus = getBountyStatus(registry.createType('BountyStatus', 'Active'));
const bountyStatus = getBountyStatus(registry.createType('PalletBountiesBountyStatus', 'Active'));
expect(bountyStatus.bountyStatus).toEqual('Active');
expect(bountyStatus.curator).toBeDefined();
@ -30,7 +30,7 @@ describe('get bounty status', () => {
});
it('for PendingPayout state it has curator, beneficiary and unlock_at defined', () => {
const bountyStatus = getBountyStatus(registry.createType('BountyStatus', 'PendingPayout'));
const bountyStatus = getBountyStatus(registry.createType('PalletBountiesBountyStatus', 'PendingPayout'));
expect(bountyStatus.bountyStatus).toEqual('PendingPayout');
expect(bountyStatus.curator).toBeDefined();

View File

@ -1,10 +1,10 @@
// Copyright 2017-2025 @polkadot/app-bounties authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BountyStatus } from '@polkadot/types/interfaces';
import type { PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import type { BountyStatusType, StatusName } from '../types.js';
export const getBountyStatus = (status: BountyStatus): BountyStatusType => {
export const getBountyStatus = (status: PalletBountiesBountyStatus): BountyStatusType => {
const statusAsString = status.type as StatusName;
let result: BountyStatusType = {

View File

@ -1,7 +1,7 @@
// Copyright 2017-2025 @polkadot/app-bounties authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BountyStatus } from '@polkadot/types/interfaces';
import type { PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import type { BountyStatusType } from '../types.js';
import { useCallback } from 'react';
@ -10,7 +10,7 @@ import { createNamedHook } from '@polkadot/react-hooks';
import { getBountyStatus } from '../helpers/index.js';
function useBountyStatusImpl (status: BountyStatus): BountyStatusType {
function useBountyStatusImpl (status: PalletBountiesBountyStatus): BountyStatusType {
const updateStatus = useCallback(() => getBountyStatus(status), [status]);
return updateStatus();

View File

@ -8,8 +8,8 @@ import type { ApiPromise } from '@polkadot/api';
import type { DeriveCollectiveProposal } from '@polkadot/api-derive/types';
import type { ApiProps } from '@polkadot/react-api/types';
import type { PartialQueueTxExtrinsic, QueueProps, QueueTxExtrinsicAdd } from '@polkadot/react-components/Status/types';
import type { BountyIndex, BountyStatus } from '@polkadot/types/interfaces';
import type { PalletBountiesBounty } from '@polkadot/types/lookup';
import type { BountyIndex } from '@polkadot/types/interfaces';
import type { PalletBountiesBounty, PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import type { BountyApi } from '../../src/hooks/index.js';
import { fireEvent, render, within } from '@testing-library/react';
@ -59,8 +59,8 @@ interface RenderedBountiesPage {
export class BountiesPage {
aBounty: ({ status, value }?: Partial<PalletBountiesBounty>) => PalletBountiesBounty;
aBountyIndex: (index?: number) => BountyIndex;
aBountyStatus: (status: string) => BountyStatus;
bountyStatusWith: ({ curator, status }: { curator?: string, status?: string, }) => BountyStatus;
aBountyStatus: (status: string) => PalletBountiesBountyStatus;
bountyStatusWith: ({ curator, status }: { curator?: string, status?: string, }) => PalletBountiesBountyStatus;
bountyWith: ({ status, value }: { status?: string, value?: number }) => PalletBountiesBounty;
findByRole?: FindOne;

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import type { ApiPromise } from '@polkadot/api';
import type { BountyIndex, BountyStatus } from '@polkadot/types/interfaces';
import type { BountyIndex } from '@polkadot/types/interfaces';
import type { PalletBountiesBounty, PalletBountiesBountyStatus } from '@polkadot/types/lookup';
import type { Registry } from '@polkadot/types/types';
@ -23,26 +23,24 @@ export class BountyFactory {
public defaultBounty = (): PalletBountiesBounty =>
this.#registry.createType<PalletBountiesBounty>('Bounty');
public aBountyStatus = (status: string): BountyStatus =>
this.#registry.createType('BountyStatus', status);
public aBountyStatus = (status: string): PalletBountiesBountyStatus =>
this.#registry.createType('PalletBountiesBountyStatus', status);
public bountyStatusWith = ({ curator = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', status = 'Active', updateDue = 100000 } = {}): BountyStatus => {
public bountyStatusWith = ({ curator = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', status = 'Active', updateDue = 100000 } = {}): PalletBountiesBountyStatus => {
if (status === 'Active') {
return this.#registry.createType('BountyStatus', { active: { curator, updateDue }, status });
return this.#registry.createType('PalletBountiesBountyStatus', { active: { curator, updateDue }, status });
}
if (status === 'CuratorProposed') {
return this.#registry.createType('BountyStatus', { curatorProposed: { curator }, status });
return this.#registry.createType('PalletBountiesBountyStatus', { curatorProposed: { curator }, status });
}
throw new Error('Unsupported status');
};
public bountyWith = ({ status = 'Proposed', value = 1 } = {}): PalletBountiesBounty =>
// FIXME: https://github.com/polkadot-js/apps/issues/11192
this.aBounty({ status: this.aBountyStatus(status) as unknown as PalletBountiesBountyStatus, value: balanceOf(value) });
this.aBounty({ status: this.aBountyStatus(status), value: balanceOf(value) });
// FIXME: https://github.com/polkadot-js/apps/issues/11192
public aBounty = ({ fee = balanceOf(10), status = this.aBountyStatus('Proposed') as unknown as PalletBountiesBountyStatus, value = balanceOf(500) }: Partial<PalletBountiesBounty> = {}): PalletBountiesBounty =>
public aBounty = ({ fee = balanceOf(10), status = this.aBountyStatus('Proposed'), value = balanceOf(500) }: Partial<PalletBountiesBounty> = {}): PalletBountiesBounty =>
this.#registry.createType<PalletBountiesBounty>('Bounty', { fee, status, value });
}