web/multinode: store and payouts components tests added

WHAT: unit tests for vue components and vuex store and environment for it

WHY: to cover frontend part with tests

Change-Id: I2aeaadb200176d4ba0a1406068304785e95d92cd
This commit is contained in:
NickolaiYurchenko 2021-04-30 12:33:36 +03:00 committed by Nikolay Yurchenko
parent 2ccbb32e14
commit e0f3166343
34 changed files with 6735 additions and 20306 deletions

View File

@ -1,4 +1,9 @@
{
"env": {
"es2020": true,
"node": true,
"jest": true
},
"plugins": [
"stylelint-scss"
],

View File

@ -0,0 +1,9 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
import { GlobalWithFetchMock } from 'jest-fetch-mock';
const customGlobal: GlobalWithFetchMock = global as unknown as GlobalWithFetchMock;
customGlobal.fetch = require('jest-fetch-mock');
customGlobal.fetchMock = customGlobal.fetch;

File diff suppressed because it is too large Load Diff

View File

@ -12,32 +12,79 @@
"vue": "2.6.11",
"vue-class-component": "7.2.6",
"vue-clipboard2": "0.3.1",
"vue-jest": "3.0.5",
"vue-property-decorator": "9.1.2",
"vue-router": "3.4.9",
"vuex": "3.6.0"
},
"devDependencies": {
"@babel/core": "7.12.10",
"@babel/plugin-proposal-object-rest-spread": "7.12.1",
"@vue/cli-plugin-babel": "4.5.9",
"@vue/cli-plugin-typescript": "4.5.9",
"@vue/cli-service": "4.5.9",
"babel-core": "6.26.3",
"core-js": "3.8.1",
"sass": "^1.32.0",
"@babel/core": "7.8.4",
"@babel/plugin-proposal-object-rest-spread": "7.8.3",
"@vue/cli-plugin-babel": "4.1.1",
"@vue/cli-plugin-typescript": "4.1.1",
"@vue/cli-plugin-unit-jest": "4.1.1",
"@vue/cli-service": "4.1.1",
"@vue/test-utils": "1.0.0-beta.30",
"babel-core": "7.0.0-bridge.0",
"compression-webpack-plugin": "6.0.0",
"core-js": "3.6.5",
"jest-fetch-mock": "3.0.0",
"node-sass": "4.14.1",
"sass-loader": "8.0.0",
"stylelint": "13.8.0",
"stylelint-config-standard": "20.0.0",
"sinon": "7.5.0",
"stylelint": "13.7.1",
"stylelint-config-standard": "19.0.0",
"stylelint-scss": "3.18.0",
"stylelint-webpack-plugin": "2.1.1",
"tslint": "6.1.3",
"stylelint-webpack-plugin": "1.2.1",
"ts-jest": "25.5.0",
"tslint": "5.20.1",
"tslint-consistent-codestyle": "1.16.0",
"tslint-loader": "3.5.4",
"typescript": "3.7.4",
"vue-svg-loader": "0.16.0",
"vue-svg-loader": "0.15.0",
"vue-template-compiler": "2.6.11",
"vue-tslint": "0.3.2",
"vue-tslint-loader": "3.5.6",
"webpack": "4.41.5"
},
"jest": {
"automock": false,
"setupFiles": [
"./jestSetup.ts"
],
"globals": {
"ts-jest": {
"diagnostics": false
}
},
"moduleFileExtensions": [
"js",
"jsx",
"json",
"vue",
"ts",
"tsx"
],
"collectCoverage": true,
"transform": {
"^.+\\.js$": "babel-jest",
"^.+\\.vue$": "vue-jest",
".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
"^.+\\.tsx?$": "ts-jest",
"^.+\\.svg$": "<rootDir>/tests/unit/mock/svgTransform.js"
},
"transformIgnorePatterns": [
"/node_modules/"
],
"moduleNameMapper": {
"^@/(.*)$": "<rootDir>/src/$1"
},
"snapshotSerializers": [
"jest-serializer-vue"
],
"testMatch": [
"<rootDir>/tests/unit/**/*.spec.(js|jsx|ts|tsx)|<rootDir>/__tests__/*.(js|jsx|ts|tsx)"
],
"testURL": "http://localhost/"
}
}

View File

@ -2,7 +2,7 @@
// See LICENSE for copying information.
import { APIClient } from '@/api/index';
import { PayoutsSummary } from '@/payouts';
import { NodePayoutsSummary, PayoutsSummary } from '@/payouts';
/**
* client for nodes controller of MND api.
@ -26,7 +26,11 @@ export class PayoutsClient extends APIClient {
* Thrown if something goes wrong on server side.
*/
public async summary(satelliteId: string | null, period: string | null): Promise<PayoutsSummary> {
const path = `${this.ROOT_PATH}/summary?satelliteId=${satelliteId}&period=${period}`;
let path = `${this.ROOT_PATH}/summary`;
if (period) {
path += `/${period}`;
}
const response = await this.http.get(path);
@ -34,6 +38,18 @@ export class PayoutsClient extends APIClient {
await this.handleError(response);
}
return await response.json();
const result = await response.json();
return new PayoutsSummary(
result.totalEarned,
result.totalHeld,
result.totalPaid,
result.nodeSummary.map(item => new NodePayoutsSummary(
item.nodeId,
item.nodeName,
item.held,
item.paid,
)),
);
}
}

View File

@ -27,7 +27,7 @@ import MoreIcon from '@/../static/images/icons/more.svg';
MoreIcon,
},
})
export default class NodeItem extends Vue {
export default class NodeOptions extends Vue {
@Prop({default: ''})
public id: string;

View File

@ -41,10 +41,8 @@ export default class DetailsArea extends Vue {
public totalHeld: number;
@Prop({default: 0})
public totalPaid: number;
public get period(): string {
return this.$store.getters['payouts/periodString'];
}
@Prop({default: ''})
public period: string;
}
</script>

View File

@ -15,7 +15,7 @@
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { Component, Prop, Vue } from 'vue-property-decorator';
import PayoutPeriodCalendar from './PayoutPeriodCalendar.vue';
@ -23,11 +23,10 @@ import PayoutPeriodCalendar from './PayoutPeriodCalendar.vue';
components: { PayoutPeriodCalendar },
})
export default class Payout extends Vue {
public isCalendarShown: boolean = false;
@Prop({default: ''})
public period: string;
public get period(): string {
return this.$store.getters['payouts/periodString'];
}
public isCalendarShown: boolean = false;
public openCalendar(): void {
this.isCalendarShown = true;

View File

@ -3,11 +3,11 @@
<template>
<tr class="payouts-summary-item">
<th class="align-left node-name">{{ payoutsSummary.nodeName }}</th>
<th class="align-left node-name">{{ payoutsSummary.nodeName || payoutsSummary.nodeId }}</th>
<th>{{ payoutsSummary.held | centsToDollars }}</th>
<th>{{ payoutsSummary.paid | centsToDollars }}</th>
<th class="overflow-visible options">
<node-options :id="payoutsSummary.nodeID" />
<node-options :id="payoutsSummary.nodeId" />
</th>
</tr>
</template>

View File

@ -12,7 +12,7 @@
</tr>
</thead>
<tbody>
<payouts-summary-item v-for="payoutSummary in nodePayoutsSummary" :key="payoutSummary.nodeID" :payouts-summary="payoutSummary"/>
<payouts-summary-item v-for="payoutSummary in nodePayoutsSummary" :key="payoutSummary.nodeId" :payouts-summary="payoutSummary"/>
</tbody>
</table>
</template>

View File

@ -34,7 +34,7 @@ export class PayoutsModule implements Module<PayoutsState, RootState> {
this.namespaced = true;
this.state = new PayoutsState();
this.mutations = {
populate: this.populate,
setSummary: this.setSummary,
setPayoutPeriod: this.setPayoutPeriod,
};
this.actions = {
@ -51,7 +51,7 @@ export class PayoutsModule implements Module<PayoutsState, RootState> {
* @param state - state of the module.
* @param summary - payouts summary information depends on selected time and satellite.
*/
public populate(state: PayoutsState, summary: PayoutsSummary): void {
public setSummary(state: PayoutsState, summary: PayoutsSummary): void {
state.summary = summary;
}
@ -71,9 +71,10 @@ export class PayoutsModule implements Module<PayoutsState, RootState> {
*/
public async summary(ctx: ActionContext<PayoutsState, RootState>): Promise<void> {
// @ts-ignore
const summary = await this.payouts.summary(ctx.rootState.nodes.selectedSatellite.id, ctx.state.selectedPayoutPeriod);
const selectedSatelliteId = ctx.rootState.nodes.selectedSatellite ? ctx.rootState.nodes.selectedSatellite.id : null;
const summary = await this.payouts.summary(selectedSatelliteId, ctx.state.selectedPayoutPeriod);
ctx.commit('populate', summary);
ctx.commit('setSummary', summary);
}
// Getters

View File

@ -8,7 +8,7 @@
<div class="payouts__left-area">
<div class="payouts__left-area__dropdowns">
<satellite-selection-dropdown />
<payout-period-calendar-button />
<payout-period-calendar-button :period="period" />
</div>
<payouts-summary-table
class="payouts__left-area__table"
@ -21,8 +21,9 @@
:total-earned="payoutsSummary.totalEarned"
:total-held="payoutsSummary.totalHeld"
:total-paid="payoutsSummary.totalPaid"
:period="period"
/>
<payout-history-block />
<!-- <payout-history-block />-->
</div>
</div>
</div>
@ -70,6 +71,13 @@ export default class PayoutsPage extends Vue {
public get payoutsSummary(): PayoutsSummary {
return this.$store.state.payouts.summary;
}
/**
* period selected payout period from store.
*/
public get period(): string {
return this.$store.getters['payouts/periodString'];
}
}
</script>

View File

@ -66,8 +66,8 @@ export class CreateNodeFields {
*/
export class NodeURL {
public constructor(
public id: string,
public address: string,
public id: string = '',
public address: string = '',
) {}
}
@ -76,7 +76,7 @@ export class NodeURL {
*/
export class UpdateNodeModel {
public constructor(
public id: string,
public name: string,
public id: string = '',
public name: string = '',
) {}
}

View File

@ -12,7 +12,7 @@ const PRICE_DIVIDER: number = 10000;
*/
export class NodePayoutsSummary {
public constructor(
public nodeID: string = '',
public nodeId: string = '',
public nodeName: string = '',
public held: number = 0,
public paid: number = 0,

View File

@ -2,7 +2,7 @@
// See LICENSE for copying information.
import { PayoutsClient } from '@/api/payouts';
import { NodePayoutsSummary, PayoutsSummary } from '@/payouts/index';
import { PayoutsSummary } from '@/payouts/index';
/**
* exposes all payouts related logic
@ -30,18 +30,6 @@ export class Payouts {
* Thrown if something goes wrong on server side.
*/
public async summary(satelliteId: string | null, period: string | null): Promise<PayoutsSummary> {
const result = await this.payouts.summary(satelliteId, period);
return new PayoutsSummary(
result.totalEarned,
result.totalHeld,
result.totalPaid,
result.nodeSummary.map(item => new NodePayoutsSummary(
item.nodeID,
item.nodeName,
item.held,
item.paid,
)),
);
return await this.payouts.summary(satelliteId, period);
}
}

View File

@ -0,0 +1,20 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
import NodeOptions from '@/app/components/common/NodeOptions.vue';
import { shallowMount } from '@vue/test-utils';
describe('NodeOptions', (): void => {
it('renders correctly', async (): Promise<void> => {
const wrapper = shallowMount(NodeOptions, {
propsData: { id: 'id' },
});
expect(wrapper).toMatchSnapshot();
await wrapper.find('.options-button').trigger('click');
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`NodeOptions renders correctly 1`] = `
<div class="options-button">
<more-icon-stub></more-icon-stub>
<!---->
</div>
`;
exports[`NodeOptions renders correctly 2`] = `
<div class="options-button">
<more-icon-stub></more-icon-stub>
<div class="options">
<div class="options__item">Copy Node ID</div>
<delete-node-stub nodeid="id"></delete-node-stub>
<update-name-stub nodeid="id"></update-name-stub>
</div>
</div>
`;

View File

@ -0,0 +1,32 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
import Vuex from 'vuex';
import DetailsArea from '@/app/components/payouts/DetailsArea.vue';
import { Currency } from '@/app/utils/currency';
import { createLocalVue, shallowMount } from '@vue/test-utils';
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.filter('centsToDollars', (cents: number): string => {
return Currency.dollarsFromCents(cents);
});
describe('DetailsArea', (): void => {
it('renders correctly', (): void => {
const wrapper = shallowMount(DetailsArea, {
localVue,
propsData: {
totalEarned: 5000,
totalPaid: 60000,
totalHeld: 700,
period: 'April, 2021',
},
});
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,14 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
import PayoutHistoryBlock from '@/app/components/payouts/PayoutHistoryBlock.vue';
import { shallowMount } from '@vue/test-utils';
describe('PayoutHistoryBlock', (): void => {
it('renders correctly', (): void => {
const wrapper = shallowMount(PayoutHistoryBlock);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,37 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
import Vuex from 'vuex';
import PayoutPeriodCalendarButton from '@/app/components/payouts/PayoutPeriodCalendarButton.vue';
import { createLocalVue, shallowMount } from '@vue/test-utils';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('PayoutPeriodCalendarButton', (): void => {
it('renders correctly', (): void => {
const wrapper = shallowMount(PayoutPeriodCalendarButton, {
localVue,
propsData: {
period: 'April, 2021',
},
});
expect(wrapper).toMatchSnapshot();
});
it('triggers open calendar correctly', async (): Promise<void> => {
const wrapper = shallowMount(PayoutPeriodCalendarButton, {
localVue,
propsData: {
period: 'April, 2021',
},
});
await wrapper.find('.calendar-button').trigger('click');
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,30 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
import Vuex from 'vuex';
import PayoutsSummaryItem from '@/app/components/payouts/tables/PayoutsSummaryItem.vue';
import { Currency } from '@/app/utils/currency';
import { NodePayoutsSummary } from '@/payouts';
import { createLocalVue, shallowMount } from '@vue/test-utils';
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.filter('centsToDollars', (cents: number): string => {
return Currency.dollarsFromCents(cents);
});
describe('PayoutsSummaryItem', (): void => {
it('renders correctly', (): void => {
const payoutsSummary = new NodePayoutsSummary('1', 'name1', 5000, 4000);
const wrapper = shallowMount(PayoutsSummaryItem, {
localVue,
propsData: { payoutsSummary },
});
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,28 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
import Vuex from 'vuex';
import PayoutsSummaryTable from '@/app/components/payouts/tables/PayoutsSummaryTable.vue';
import { NodePayoutsSummary } from '@/payouts';
import { createLocalVue, shallowMount } from '@vue/test-utils';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('PayoutsSummaryTable', (): void => {
it('renders correctly', (): void => {
const nodePayoutsSummary = [
new NodePayoutsSummary('1', 'name1', 5000, 4000),
new NodePayoutsSummary('2', 'name2', 500, 1000),
];
const wrapper = shallowMount(PayoutsSummaryTable, {
localVue,
propsData: { nodePayoutsSummary },
});
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DetailsArea renders correctly 1`] = `
<info-block-stub>
<div class="details-area">
<header class="details-area__header">
<h3 class="details-area__header__title">Details</h3>
<p class="details-area__header__period">April, 2021</p>
</header>
<div class="details-area__content">
<div class="details-area__content__item">
<p class="details-area__content__item__label">EARNED</p>
<p class="details-area__content__item__value">$50.00</p>
</div>
<div class="details-area__content__item">
<p class="details-area__content__item__label">HELD</p>
<p class="details-area__content__item__value">$7.00</p>
</div>
<div class="details-area__content__item">
<p class="details-area__content__item__label">PAID</p>
<p class="details-area__content__item__value">$600.00</p>
</div>
</div>
</div>
</info-block-stub>
`;

View File

@ -0,0 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PayoutHistoryBlock renders correctly 1`] = `
<info-block-stub>
<div class="payouts-history-block">
<h3 class="payouts-history-block__title">Payout History</h3>
<v-button-stub label="Download" width="100%" height="48px" iswhite="true" onpress="function () {
/* istanbul ignore next */
cov_1hc5kf5lze.f[3]++;
}" class="payouts-history-block__button"></v-button-stub>
</div>
</info-block-stub>
`;

View File

@ -0,0 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PayoutPeriodCalendarButton renders correctly 1`] = `
<div class="calendar-button"><span class="label">April, 2021</span> <svg width="8" height="4" viewBox="0 0 8 4" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.33657 3.73107C3.70296 4.09114 4.29941 4.08814 4.66237 3.73107L7.79796 0.650836C8.16435 0.291517 8.01864 0 7.47247 0L0.526407 0C-0.0197628 0 -0.16292 0.294525 0.200917 0.650836L3.33657 3.73107Z" fill="#131D3A"></path>
</svg>
<!---->
</div>
`;
exports[`PayoutPeriodCalendarButton triggers open calendar correctly 1`] = `
<div class="calendar-button"><span class="label">April, 2021</span> <svg width="8" height="4" viewBox="0 0 8 4" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.33657 3.73107C3.70296 4.09114 4.29941 4.08814 4.66237 3.73107L7.79796 0.650836C8.16435 0.291517 8.01864 0 7.47247 0L0.526407 0C-0.0197628 0 -0.16292 0.294525 0.200917 0.650836L3.33657 3.73107Z" fill="#131D3A"></path>
</svg>
<payout-period-calendar-stub class="calendar-button__calendar"></payout-period-calendar-stub>
</div>
`;

View File

@ -0,0 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PayoutsSummaryItem renders correctly 1`] = `
<tr class="payouts-summary-item">
<th class="align-left node-name">name1</th>
<th>$50.00</th>
<th>$40.00</th>
<th class="overflow-visible options">
<node-options-stub id="1"></node-options-stub>
</th>
</tr>
`;

View File

@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PayoutsSummaryTable renders correctly 1`] = `
<table border="0" cellpadding="0" cellspacing="0" class="payouts-summary-table">
<thead>
<tr>
<th class="align-left">NODE</th>
<th>HELD</th>
<th>PAID</th>
<th class="options"></th>
</tr>
</thead>
<tbody>
<payouts-summary-item-stub payoutssummary="[object Object]"></payouts-summary-item-stub>
<payouts-summary-item-stub payoutssummary="[object Object]"></payouts-summary-item-stub>
</tbody>
</table>
`;

View File

@ -0,0 +1,23 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
import Vuex from 'vuex';
import PayoutsPage from '@/app/views/PayoutsPage.vue';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import store from '../../mock/store';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('PayoutsPage', (): void => {
it('renders correctly', (): void => {
const wrapper = shallowMount(PayoutsPage, {
store,
localVue,
});
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PayoutsPage renders correctly 1`] = `
<div class="payouts">
<h1 class="payouts__title">Payouts</h1>
<div class="payouts__content-area">
<div class="payouts__left-area">
<div class="payouts__left-area__dropdowns">
<satellite-selection-dropdown-stub></satellite-selection-dropdown-stub>
<payout-period-calendar-button-stub period="All time"></payout-period-calendar-button-stub>
</div>
<payouts-summary-table-stub nodepayoutssummary="" class="payouts__left-area__table"></payouts-summary-table-stub>
</div>
<div class="payouts__right-area">
<details-area-stub totalearned="0" totalheld="0" totalpaid="0" period="All time"></details-area-stub>
<payout-history-block-stub></payout-history-block-stub>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,27 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
import Vuex from 'vuex';
import { NodesClient } from '@/api/nodes';
import { PayoutsClient } from '@/api/payouts';
import { NodesModule } from '@/app/store/nodes';
import { PayoutsModule } from '@/app/store/payouts';
import { Nodes } from '@/nodes/service';
import { Payouts } from '@/payouts/service';
import { createLocalVue } from '@vue/test-utils';
const Vue = createLocalVue();
Vue.use(Vuex);
const nodesClient: NodesClient = new NodesClient();
export const nodesService: Nodes = new Nodes(nodesClient);
const nodesModule: NodesModule = new NodesModule(nodesService);
const payoutsClient: PayoutsClient = new PayoutsClient();
export const payoutsService: Payouts = new Payouts(payoutsClient);
const payoutsModule: PayoutsModule = new PayoutsModule(payoutsService);
const store = new Vuex.Store({ modules: { payouts: payoutsModule, nodes: nodesModule }});
export default store;

View File

@ -0,0 +1,17 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
const vueJest = require('vue-jest/lib/template-compiler');
module.exports = {
process(content) {
const { render } = vueJest({
content,
attrs: {
functional: false,
},
});
return `module.exports = { render: ${render} }`;
},
};

View File

@ -0,0 +1,176 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
import Vuex from 'vuex';
import { CreateNodeFields, Node, NodeURL } from '@/nodes';
import { createLocalVue } from '@vue/test-utils';
import store, { nodesService } from '../mock/store';
const node = new Node();
const nodes = [ node ];
const satellite = new NodeURL('testId', '127.0.0.1:test');
const trustedSatellites = [ satellite ];
const nodeToAdd = new CreateNodeFields('newId', 'secret', 'newAddress');
const state = store.state as any;
describe('mutations', () => {
beforeEach(() => {
createLocalVue().use(Vuex);
});
it('populates', () => {
store.commit('nodes/populate', nodes);
expect(state.nodes.nodes.length).toBe(1);
});
it('saves trusted satellites', () => {
store.commit('nodes/saveTrustedSatellites', trustedSatellites);
expect(state.nodes.trustedSatellites.length).toBe(1);
});
it('saves selected satellite', () => {
store.commit('nodes/setSelectedSatellite', satellite.id);
expect(state.nodes.selectedSatellite.address).toBe(satellite.address);
});
});
describe('actions', () => {
beforeEach(() => {
jest.resetAllMocks();
store.commit('nodes/populate', []);
store.commit('nodes/saveTrustedSatellites', []);
store.commit('nodes/setSelectedSatellite', null);
});
it('throws error on failed nodes fetch', async () => {
jest.spyOn(nodesService, 'list').mockImplementation(() => { throw new Error(); });
try {
await store.dispatch('nodes/fetch');
expect(true).toBe(false);
} catch (error) {
expect(state.nodes.nodes.length).toBe(0);
}
});
it('success get nodes', async () => {
jest.spyOn(nodesService, 'list').mockReturnValue(
Promise.resolve(nodes),
);
await store.dispatch('nodes/fetch');
expect(state.nodes.nodes.length).toBe(1);
});
it('throws error on failed node addition', async () => {
jest.spyOn(nodesService, 'add').mockImplementation(() => { throw new Error(); });
try {
await store.dispatch('nodes/add');
expect(true).toBe(false);
} catch (error) {
expect(state.nodes.nodes.length).toBe(0);
}
});
it('success adds node', async () => {
const addSpy = jest.fn();
jest.spyOn(nodesService, 'add').mockImplementation(addSpy);
await store.dispatch('nodes/add', nodeToAdd);
expect(addSpy).toBeCalled();
});
it('throws error on failed node deletion', async () => {
jest.spyOn(nodesService, 'delete').mockImplementation(() => { throw new Error(); });
try {
await store.dispatch('nodes/delete');
expect(true).toBe(false);
} catch (error) {
expect(state.nodes.nodes.length).toBe(0);
}
});
it('success deletes node', async () => {
const deleteSpy = jest.fn();
jest.spyOn(nodesService, 'delete').mockImplementation(deleteSpy);
await store.dispatch('nodes/delete', node.id);
expect(deleteSpy).toBeCalled();
});
it('throws error on failed node name update', async () => {
jest.spyOn(nodesService, 'updateName').mockImplementation(() => { throw new Error(); });
try {
await store.dispatch('nodes/updateName');
expect(true).toBe(false);
} catch (error) {
expect(state.nodes.nodes.length).toBe(0);
}
});
it('success updates node name', async () => {
const updateNameSpy = jest.fn();
jest.spyOn(nodesService, 'updateName').mockImplementation(updateNameSpy);
await store.dispatch('nodes/updateName', nodeToAdd);
expect(updateNameSpy).toBeCalled();
});
it('throws error on failed trusted satellites', async () => {
jest.spyOn(nodesService, 'trustedSatellites').mockImplementation(() => { throw new Error(); });
try {
await store.dispatch('nodes/trustedSatellites');
expect(true).toBe(false);
} catch (error) {
expect(state.nodes.trustedSatellites.length).toBe(0);
}
});
it('success get trusted satellites', async () => {
jest.spyOn(nodesService, 'trustedSatellites').mockReturnValue(
Promise.resolve(trustedSatellites),
);
await store.dispatch('nodes/trustedSatellites');
expect(state.nodes.trustedSatellites.length).toBe(1);
});
it('throws error on failed satellite selection', async () => {
jest.spyOn(nodesService, 'list').mockImplementation(() => { throw new Error(); });
try {
await store.dispatch('nodes/selectSatellite');
expect(true).toBe(false);
} catch (error) {
expect(state.nodes.selectedSatellite).toBe(null);
}
});
it('success get trusted satellites', async () => {
jest.spyOn(nodesService, 'listBySatellite').mockReturnValue(
Promise.resolve(nodes),
);
store.commit('nodes/saveTrustedSatellites', trustedSatellites);
await store.dispatch('nodes/selectSatellite', satellite.id);
expect(state.nodes.selectedSatellite.address).toBe(satellite.address);
expect(state.nodes.nodes.length).toBe(1);
});
});

View File

@ -0,0 +1,68 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
import Vuex from 'vuex';
import { PayoutsSummary } from '@/payouts';
import { createLocalVue } from '@vue/test-utils';
import store, { payoutsService } from '../mock/store';
const state = store.state as any;
const summary = new PayoutsSummary(5000000, 6000000, 9000000);
const period = '2021-04';
describe('mutations', () => {
beforeEach(() => {
createLocalVue().use(Vuex);
});
it('sets summary', () => {
store.commit('payouts/setSummary', summary);
expect(state.payouts.summary.totalPaid).toBe(summary.totalPaid);
});
it('sets payout period', () => {
store.commit('payouts/setPayoutPeriod', period);
expect(state.payouts.selectedPayoutPeriod).toBe(period);
});
});
describe('actions', () => {
beforeEach(() => {
jest.resetAllMocks();
store.commit('payouts/setSummary', new PayoutsSummary());
});
it('throws error on failed summary fetch', async () => {
jest.spyOn(payoutsService, 'summary').mockImplementation(() => { throw new Error(); });
try {
await store.dispatch('payouts/summary');
expect(true).toBe(false);
} catch (error) {
expect(state.payouts.summary.totalPaid).toBe(0);
}
});
it('success fetches payouts summary', async () => {
jest.spyOn(payoutsService, 'summary').mockReturnValue(
Promise.resolve(summary),
);
await store.dispatch('payouts/summary');
expect(state.payouts.summary.totalPaid).toBe(summary.totalPaid);
});
});
describe('getters', () => {
it('getter monthsOnNetwork returns correct value', () => {
store.commit('payouts/setPayoutPeriod', period);
expect(store.getters['payouts/periodString']).toBe('April, 2021');
});
});

View File

@ -15,6 +15,8 @@
"strictPropertyInitialization": false,
"types": [
"webpack-env",
"jest",
"@types/jest"
],
"paths": {
"@/*": [
@ -31,7 +33,8 @@
"include": [
"src/**/*.ts",
"src/**/*.vue",
"tests/**/*.ts"
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"