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:
parent
2ccbb32e14
commit
e0f3166343
@ -1,4 +1,9 @@
|
||||
{
|
||||
"env": {
|
||||
"es2020": true,
|
||||
"node": true,
|
||||
"jest": true
|
||||
},
|
||||
"plugins": [
|
||||
"stylelint-scss"
|
||||
],
|
||||
|
9
web/multinode/jestSetup.ts
Normal file
9
web/multinode/jestSetup.ts
Normal 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;
|
26204
web/multinode/package-lock.json
generated
26204
web/multinode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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/"
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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 = '',
|
||||
) {}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
@ -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>
|
||||
`;
|
@ -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();
|
||||
});
|
||||
});
|
@ -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();
|
||||
});
|
||||
});
|
@ -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();
|
||||
});
|
||||
});
|
@ -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();
|
||||
});
|
||||
});
|
@ -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();
|
||||
});
|
||||
});
|
@ -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>
|
||||
`;
|
@ -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>
|
||||
`;
|
@ -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>
|
||||
`;
|
@ -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>
|
||||
`;
|
@ -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>
|
||||
`;
|
@ -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();
|
||||
});
|
||||
});
|
@ -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>
|
||||
`;
|
27
web/multinode/tests/unit/mock/store.ts
Normal file
27
web/multinode/tests/unit/mock/store.ts
Normal 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;
|
17
web/multinode/tests/unit/mock/svgTransform.js
Normal file
17
web/multinode/tests/unit/mock/svgTransform.js
Normal 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} }`;
|
||||
},
|
||||
};
|
176
web/multinode/tests/unit/store/nodes.spec.ts
Normal file
176
web/multinode/tests/unit/store/nodes.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
68
web/multinode/tests/unit/store/payouts.spec.ts
Normal file
68
web/multinode/tests/unit/store/payouts.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user