Skip to content

Commit

Permalink
Refactor: extract filters to composable and add tests (#4264)
Browse files Browse the repository at this point in the history
* Refactor: extract filters to composable and add tests

* Fix tests
  • Loading branch information
agualis authored Oct 16, 2023
1 parent c13d37f commit c2f144c
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 76 deletions.
89 changes: 13 additions & 76 deletions src/components/contextual/pages/vebal/LMVoting/LMVoting.vue
Original file line number Diff line number Diff line change
@@ -1,46 +1,24 @@
<script lang="ts" setup>
import useDebouncedRef from '@/composables/useDebouncedRed';
import useVotingEscrowLocks from '@/composables/useVotingEscrowLocks';
import useVotingPools from '@/composables/useVotingPools';
import useNetwork from '@/composables/useNetwork';
import useNumbers from '@/composables/useNumbers';
import useVeBal from '@/composables/useVeBAL';
import { useVeBalLockInfo } from '@/composables/useVeBalLockInfo';
import configs from '@/lib/config';
import { Network } from '@/lib/config/types';
import useWeb3 from '@/services/web3/useWeb3';
import { isVotingCompleted, useVoting } from '../providers/voting.provider';
import { bpsToPercentage } from '../voting-utils';
import GaugesFilters from './GaugesFilters.vue';
import GaugesTable from './GaugesTable.vue';
import VotingAlert from './VotingAlert.vue';
import { bpsToPercentage, isGaugeExpired } from '../voting-utils';
import useNumbers from '@/composables/useNumbers';
import { isVotingCompleted, useVoting } from '../providers/voting.provider';
/**
* COMPOSABLES
*/
const router = useRouter();
import { useLMVotingFilters } from './composables/useLMVotingFilters';
/**
* DATA
*/
const tokenFilter = useDebouncedRef<string>('', 500);
const showExpiredGauges = useDebouncedRef<boolean>(false, 500);
const activeNetworkFilters = useDebouncedRef<Network[]>(
getDefaultActiveNetworkFilter(),
500
);
const networkFilters: Network[] = Object.entries(configs)
.filter(details => {
const config = details[1];
return (
!config.testNetwork && config.pools.Stakable.VotingGaugePools.length > 0
);
})
.map(details => Number(details[0]) as Network);
/**
* COMPOSABLES
*/
Expand Down Expand Up @@ -70,48 +48,21 @@ const {
loadRequestWithExistingVotes,
} = useVoting();
const {
showExpiredGauges,
activeNetworkFilters,
filteredVotingPools,
tokenFilter,
networkFilters,
} = useLMVotingFilters(votingPools, expiredGauges);
/**
* COMPUTED
*/
const unallocatedVotesFormatted = computed<string>(() =>
bpsToPercentage(unallocatedVotes.value, fNum)
);
const poolsFilteredByExpiring = computed(() => {
if (showExpiredGauges.value) {
return votingPools.value;
}
return votingPools.value.filter(pool => {
if (Number(pool.userVotes) > 0) {
return true;
}
return !isGaugeExpired(expiredGauges.value, pool.gauge.address);
});
});
const filteredVotingPools = computed(() => {
// put filter by expiring in separate computed to maintain readability
return poolsFilteredByExpiring.value.filter(pool => {
let showByNetwork = true;
if (
activeNetworkFilters.value.length > 0 &&
!activeNetworkFilters.value.includes(pool.network)
) {
showByNetwork = false;
}
return (
showByNetwork &&
pool.tokens.some(token => {
return token.symbol
?.toLowerCase()
.includes(tokenFilter.value.toLowerCase());
})
);
});
});
const selectVotesDisabled = computed(
(): boolean =>
isLoading.value ||
Expand Down Expand Up @@ -155,20 +106,6 @@ function addIntersectionObserver(): void {
observer.observe(intersectionSentinel.value);
}
function getDefaultActiveNetworkFilter() {
const param = router.currentRoute.value.query.chain;
if (!param || typeof param !== 'string') {
return [];
}
const networkToFilter = Network[param.toUpperCase()];
if (!networkToFilter) {
return [];
}
return [networkToFilter];
}
onMounted(() => {
addIntersectionObserver();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { mountComposableWithFakeTokensProvider as mountComposable } from '@tests/mount-helpers';

import { VotingPool } from '@/composables/queries/useVotingPoolsQuery';
import { Network } from '@/lib/config/types';
import { GqlChain } from '@/services/api/graphql/generated/api-types';
import { LocationQuery, createRouter, createWebHistory } from 'vue-router';
import { aVotingPool } from '../../MultiVoting/voting-pool.builders';
import { useLMVotingFilters } from './useLMVotingFilters';

function buildRouterMock() {
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/blank',
name: 'test route',
component: {},
},
],
});

// fake router params
router.currentRoute.value.params = {};
router.push = vi.fn();

function setRouteQuery(chain: LocationQuery) {
router.currentRoute.value.query = chain;
}

return { router, setRouteQuery };
}

async function mountLmVotingFilters(
votingPools: ComputedRef<VotingPool[]>,
expiredGauges: Ref<readonly string[] | undefined>,
routerMock = buildRouterMock().router
) {
const { result } = await mountComposable(
() => useLMVotingFilters(votingPools, expiredGauges),
{ routerMock }
);
return result;
}

const expiredGauges = ref([]);

it('empty filters by default', async () => {
const votingPools = computed(() => [aVotingPool()]);
const { activeNetworkFilters } = await mountLmVotingFilters(
votingPools,
expiredGauges
);
expect(activeNetworkFilters.value).toEqual([]);
});

it('when the url chain filter in the query string', async () => {
const { router, setRouteQuery } = buildRouterMock();
setRouteQuery({ chain: 'arbitrum' });

const anArbitrumPool = aVotingPool({
network: Network.ARBITRUM,
});
const anMainnetPool = aVotingPool({ chain: GqlChain.Mainnet });

const votingPools = computed(() => [anArbitrumPool, anMainnetPool]);

const { activeNetworkFilters, filteredVotingPools } =
await mountLmVotingFilters(votingPools, expiredGauges, router);

expect(activeNetworkFilters.value).toEqual([42161]);
expect(filteredVotingPools.value).toEqual([anArbitrumPool]);
});

it('filters by token symbol', async () => {
const { router, setRouteQuery } = buildRouterMock();
setRouteQuery({});
const aVotingPoolWithGoldToken = aVotingPool();
aVotingPoolWithGoldToken.tokens[0].symbol = 'Gold';

const votingPools = computed(() => [
aVotingPoolWithGoldToken,
aVotingPool({ tokens: [] }),
]);

const { filteredVotingPools, tokenFilter, activeNetworkFilters } =
await mountLmVotingFilters(votingPools, expiredGauges, router);

tokenFilter.value = 'Gol';

expect(activeNetworkFilters.value).toEqual([]);

expect(filteredVotingPools.value).toEqual([aVotingPoolWithGoldToken]);
});

it('calculates networkFilters', async () => {
const { networkFilters } = await mountLmVotingFilters(
computed(() => []),
expiredGauges
);

expect(networkFilters).toEqual([1, 10, 100, 137, 1101, 8453, 42161, 43114]);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { VotingPool } from '@/composables/queries/useVotingPoolsQuery';
import useDebouncedRef from '@/composables/useDebouncedRed';
import { isGaugeExpired } from '../../voting-utils';
import { Network } from '@/lib/config/types';
import configs from '@/lib/config';

export function useLMVotingFilters(
votingPools: ComputedRef<VotingPool[]>,
expiredGauges: Ref<readonly string[] | undefined>
) {
const router = useRouter();

const showExpiredGauges = useDebouncedRef<boolean>(false, 500);
const tokenFilter = useDebouncedRef<string>('', 500);
const activeNetworkFilters = useDebouncedRef<Network[]>(
getDefaultActiveNetworkFilter(),
500
);

const poolsFilteredByExpiring = computed(() => {
if (showExpiredGauges.value) {
return votingPools.value;
}

return votingPools.value.filter(pool => {
if (Number(pool.userVotes) > 0) {
return true;
}
return !isGaugeExpired(expiredGauges.value, pool.gauge.address);
});
});

function getDefaultActiveNetworkFilter() {
const param = router.currentRoute.value.query.chain;

if (!param || typeof param !== 'string') {
return [];
}

const networkToFilter = Network[param.toUpperCase()];
if (!networkToFilter) {
return [];
}

return [networkToFilter];
}

const filteredVotingPools = computed(() => {
// put filter by expiring in separate computed to maintain readability
return poolsFilteredByExpiring.value.filter(pool => {
let showByNetwork = true;
if (
activeNetworkFilters.value.length > 0 &&
!activeNetworkFilters.value.includes(pool.network)
) {
showByNetwork = false;
}

return (
showByNetwork &&
pool.tokens.some(token => {
return token.symbol
?.toLowerCase()
.includes(tokenFilter.value.toLowerCase());
})
);
});
});

const networkFilters: Network[] = Object.entries(configs)
.filter(details => {
const config = details[1];
return (
!config.testNetwork && config.pools.Stakable.VotingGaugePools.length > 0
);
})
.map(details => Number(details[0]) as Network);
return {
showExpiredGauges,
activeNetworkFilters,
filteredVotingPools,
tokenFilter,
networkFilters,
};
}

1 comment on commit c2f144c

@vercel
Copy link

@vercel vercel bot commented on c2f144c Oct 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.