Skip to content

Commit aa76323

Browse files
authored
ERC20 and Mother e2e tests (#322)
* update fixtures * update cypress * add tsconfig for tests * remove unused package * tests for erc20 messages * create custom commands * fix ci * remove event * delete comments * display number inputs for numeric types * test complex inputs with mother.contract * fix tests
1 parent dacf9f4 commit aa76323

35 files changed

+353
-147
lines changed

.eslintignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
*.cjs
22
dist
3-
node_modules
3+
node_modules
4+
cypress
5+
*.config.ts

.github/workflows/build.yml

+7-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ jobs:
88
steps:
99
- name: Checkout
1010
uses: actions/checkout@v3
11+
- name: Setup node.js
12+
uses: actions/setup-node@v3
13+
with:
14+
node-version: 14
1115

1216
- name: Install and build
1317
run: |
@@ -25,7 +29,7 @@ jobs:
2529
runs-on: ubuntu-latest
2630
steps:
2731
- run: |
28-
curl -L "https://github.com/paritytech/substrate-contracts-node/releases/download/v0.15.1/substrate-contracts-node-linux.tar.gz" -O
32+
curl -L "https://github.com/paritytech/substrate-contracts-node/releases/download/v0.16.0/substrate-contracts-node-linux.tar.gz" -O
2933
ls -lash
3034
- name: Save node artifact
3135
uses: actions/upload-artifact@v3
@@ -66,6 +70,7 @@ jobs:
6670
browser: chrome
6771
record: true
6872
parallel: true
73+
config-file: cypress.config.ts
6974
group: 'UI - Chrome'
7075
env:
7176
CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }}
@@ -104,6 +109,7 @@ jobs:
104109
browser: firefox
105110
record: true
106111
parallel: true
112+
config-file: cypress.config.ts
107113
group: 'UI - Firefox'
108114
env:
109115
CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }}

cypress.config.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2022 @paritytech/contracts-ui authors & contributors
2+
// SPDX-License-Identifier: GPL-3.0-only
3+
4+
import { defineConfig } from 'cypress';
5+
6+
export default defineConfig({
7+
projectId: 'eup7bh',
8+
9+
e2e: {
10+
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
11+
},
12+
13+
component: {
14+
devServer: {
15+
framework: 'react',
16+
bundler: 'vite',
17+
},
18+
},
19+
});

cypress.json

-3
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
// Copyright 2021 @paritytech/contracts-ui authors & contributors
2-
// SPDX-License-Identifier: Apache-2.0
1+
// Copyright 2022 @paritytech/contracts-ui authors & contributors
2+
// SPDX-License-Identifier: GPL-3.0-only
33

44
describe('ERC20 Contract ', () => {
55
const initialSupply = 77;
66
const transferValue = 2;
7-
const timeout = 25000;
7+
const allowance = 25;
88

99
it('contract file uploads', () => {
1010
cy.visit('http://localhost:8081/instantiate');
1111
cy.get('[data-cy="file-input"]').attachFile('erc20.contract');
1212
cy.get('[data-cy="next-btn"]').should('not.be.disabled');
1313
});
14+
1415
it('moves to step 2', () => {
1516
cy.get('[data-cy="next-btn"]').click();
1617
cy.contains('Deployment Constructor').should('be.visible');
@@ -29,37 +30,44 @@ describe('ERC20 Contract ', () => {
2930
});
3031

3132
it('submits instantiate transaction', () => {
32-
cy.get('[data-cy="submit-btn"]').click();
33-
cy.get('[data-cy="transaction-complete"]', { timeout })
34-
.should('be.visible')
35-
.and('contain', 'contracts:Instantiated')
36-
.and('contain', 'system:NewAccount')
37-
.and('contain', 'balances:Endowed')
38-
.and('contain', 'balances:Transfer')
39-
.and('contain', 'balances:Reserved')
40-
.and('contain', 'balances:Withdraw')
41-
.and('contain', 'system:ExtrinsicSuccess');
42-
43-
cy.get('[data-cy="dismiss-notification"]').click();
33+
cy.instantiate();
4434
});
35+
4536
it('redirects to contract page after instantiation', () => {
4637
cy.url().should('contain', '/contract/');
4738
});
39+
4840
it(`calling totalSupply() returns ${initialSupply}`, () => {
49-
cy.contains('Read').click();
50-
cy.get('[data-cy="totalSupply"]').find('.return-value').should('contain', `${initialSupply}`);
41+
cy.assertReturnValue('totalSupply', `${initialSupply}`);
5142
});
43+
5244
it(`transfers ${transferValue} Units to another account`, () => {
53-
cy.get('.constructorDropdown').click().find('.dropdown__option').eq(3).click();
54-
cy.get('.constructorDropdown').find('.dropdown__single-value').should('contain', 'transfer');
45+
cy.selectMessage('transfer', 3);
5546
cy.get('.form-field.to').find('.dropdown').click().find('.dropdown__option').eq(3).click();
5647
cy.get('.form-field.value').find('input[type="text"]').eq(0).type(`${transferValue}`);
57-
cy.contains('Call').click();
58-
cy.get('[data-cy="transaction-complete"]', { timeout })
59-
.should('be.visible')
60-
.and('contain', 'system:ExtrinsicSuccess')
61-
.and('contain', 'balances:Transfer')
62-
.and('contain', 'balances:Reserved')
63-
.and('contain', 'balances:Withdraw');
48+
cy.call();
49+
cy.selectMessage('balanceOf', 1);
50+
cy.assertReturnValue('balanceOf', `${initialSupply - transferValue}`);
51+
});
52+
53+
it(`successfully approves allowance`, () => {
54+
cy.selectMessage('approve', 4);
55+
cy.get('.form-field.spender').find('.dropdown').click().find('.dropdown__option').eq(2).click();
56+
cy.get('.form-field.value').find('input[type="text"]').type(`${allowance}`);
57+
cy.call();
58+
cy.selectMessage('allowance', 2);
59+
cy.get('.form-field.spender').find('.dropdown').click().find('.dropdown__option').eq(2).click();
60+
cy.assertReturnValue('allowance', `${allowance}`);
61+
});
62+
63+
it(`transfers ${transferValue} on behalf of alice`, () => {
64+
cy.get('.form-field.caller').click().find('.dropdown__option').eq(2).click();
65+
cy.selectMessage('transferFrom', 5);
66+
cy.get('.form-field.to').find('.dropdown').click().find('.dropdown__option').eq(2).click();
67+
cy.get('.form-field.value').find('input[type="text"]').type(`${transferValue}`);
68+
cy.call();
69+
cy.selectMessage('balanceOf', 1);
70+
cy.get('.form-field.owner').find('.dropdown').click().find('.dropdown__option').eq(2).click();
71+
cy.assertReturnValue('balanceOf', `${transferValue}`);
6472
});
6573
});

cypress/integration/contracts/flipper.spec.ts cypress/e2e/contracts/flipper.spec.ts

+6-22
Original file line numberDiff line numberDiff line change
@@ -26,41 +26,25 @@ describe('Flipper Contract ', () => {
2626
});
2727

2828
it(`submits instantiate transaction`, () => {
29-
cy.get('[data-cy="submit-btn"]').click();
30-
cy.get('[data-cy="transaction-complete"]', { timeout })
31-
.should('be.visible')
32-
.and('contain', 'contracts:Instantiated')
33-
.and('contain', 'system:NewAccount')
34-
.and('contain', 'balances:Endowed')
35-
.and('contain', 'balances:Transfer')
36-
.and('contain', 'balances:Reserved')
37-
.and('contain', 'balances:Withdraw')
38-
.and('contain', 'system:ExtrinsicSuccess');
39-
40-
cy.get('[data-cy="dismiss-notification"]').click();
29+
cy.instantiate();
4130
});
4231
it('redirects to contract page after instantiation', () => {
4332
cy.url().should('contain', '/contract/');
4433
});
4534
it('calling get() returns true', () => {
46-
cy.get('.constructorDropdown').click().find('.dropdown__option').eq(1).click();
47-
cy.get('.constructorDropdown').find('.dropdown__single-value').should('contain', 'get');
48-
cy.contains('Read').click();
49-
cy.get('[data-cy="get"]').find('.return-value').should('contain', 'true');
35+
cy.selectMessage('get', 1);
36+
cy.assertReturnValue('get', 'true');
5037
});
5138
it(`submits flip() transaction`, () => {
52-
cy.get('.constructorDropdown').click().find('.dropdown__option').eq(0).click();
53-
cy.get('.constructorDropdown').find('.dropdown__single-value').should('contain', 'flip');
39+
cy.selectMessage('flip', 0);
5440
cy.contains('Call').click();
5541
cy.get('[data-cy="transaction-complete"]', { timeout })
5642
.should('be.visible')
5743
.and('contain', 'system:ExtrinsicSuccess')
5844
.and('contain', 'balances:Withdraw');
5945
});
6046
it('calling get() returns false', () => {
61-
cy.get('.constructorDropdown').click().find('.dropdown__option').eq(1).click();
62-
cy.get('.constructorDropdown').find('.dropdown__single-value').should('contain', 'get');
63-
cy.contains('Read').click();
64-
cy.get('[data-cy="get"]').find('.return-value').should('contain', 'false');
47+
cy.selectMessage('get', 1);
48+
cy.assertReturnValue('get', 'false');
6549
});
6650
});

cypress/e2e/contracts/mother.spec.ts

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Copyright 2022 @paritytech/contracts-ui authors & contributors
2+
// SPDX-License-Identifier: GPL-3.0-only
3+
4+
describe('Mother Contract ', () => {
5+
it('contract file uploads', () => {
6+
cy.visit('http://localhost:8081/instantiate');
7+
cy.get('[data-cy="file-input"]').attachFile('mother.contract');
8+
cy.get('[data-cy="upload-confirmation"]').should('contain', 'mother.contract');
9+
cy.get('.validation.success').should('be.visible').and('contain', 'Valid contract bundle');
10+
});
11+
12+
it('moves to step 2', () => {
13+
cy.get('[data-cy="next-btn"]').should('not.be.disabled');
14+
cy.get('[data-cy="next-btn"]').click();
15+
cy.contains('Deployment Constructor').should('be.visible');
16+
cy.contains('Deployment Salt').scrollIntoView().should('be.visible');
17+
cy.contains('Max Gas Allowed').should('be.visible');
18+
cy.contains('Storage Deposit Limit').should('be.visible');
19+
});
20+
21+
it('displays `name: Text` input correctly', () => {
22+
cy.get('.form-field.name')
23+
.scrollIntoView()
24+
.within(() => {
25+
cy.contains('name: Text').should('be.visible');
26+
cy.get("input[type='text']").should('be.visible').and('be.empty');
27+
});
28+
});
29+
30+
it('displays `subject: Hash` input correctly', () => {
31+
cy.get('.form-field.subject').within(() => {
32+
cy.contains('subject: Hash').should('be.visible');
33+
cy.get("input[type='text']").should('be.visible');
34+
});
35+
});
36+
37+
it('displays `bids: Auction` input correctly ', () => {
38+
cy.get('.form-field.bids').within(() => {
39+
cy.contains('Vec<Vec<Option<(AccountId,u128)>>>').should('be.visible');
40+
cy.contains('Vec<Option<(AccountId,u128)>>').should('be.visible');
41+
cy.get('.vector-field-1').should('have.lengthOf', 1);
42+
cy.get('.vector-field-2')
43+
.should('have.lengthOf', 1)
44+
.find("input[type='text']")
45+
.should('have.attr', 'placeholder', 'Do not supply');
46+
cy.get('.vector-field-3').should('not.exist');
47+
});
48+
});
49+
50+
it('adds inputs on a parent Vector component', () => {
51+
cy.get('.form-field.bids').within(() => {
52+
cy.get('[data-cy="vector-add-1"]').click().click();
53+
cy.get('.vector-field-1').should('have.lengthOf', 3);
54+
});
55+
});
56+
57+
it('adds inputs on a nested Vector component', () => {
58+
cy.get('.form-field.bids .vector-field-1')
59+
.first()
60+
.within(() => {
61+
cy.get('[data-cy="vector-add-2"]').click().click();
62+
cy.get('.vector-field-2').should('have.lengthOf', 3);
63+
});
64+
});
65+
it('displays inputs for `Option<(AccountId,u128)` and sets values', () => {
66+
cy.get('.form-field.bids .vector-field-2')
67+
.first()
68+
.each($el => {
69+
cy.wrap($el)
70+
.scrollIntoView()
71+
.within(() => {
72+
cy.get('[data-cy="switch-button"]').click();
73+
cy.contains('0: AccountId').should('be.visible');
74+
cy.get('.dropdown').should('have.lengthOf', 1);
75+
cy.contains('1: u128').should('be.visible');
76+
cy.get("input[type='number']").should('have.lengthOf', 1).type('99999');
77+
cy.selectAccount('bob', 2);
78+
});
79+
});
80+
});
81+
it('removes inputs on a parent Vector component', () => {
82+
cy.get('.form-field.bids').within(() => {
83+
cy.get('[data-cy="vector-remove-1"]').click().click();
84+
cy.get('.vector-field-1').should('have.lengthOf', 1);
85+
});
86+
});
87+
88+
it('displays 3 number inputs for `terms: [u32;3]` and sets their value', () => {
89+
cy.get('.form-field.terms')
90+
.scrollIntoView()
91+
.within(() => {
92+
cy.contains('terms: [BlockNumber;3]').should('be.visible');
93+
cy.get("input[type='number']").should('have.lengthOf', 3).and('be.visible');
94+
cy.get("input[type='number']").each($el => {
95+
cy.wrap($el).type('123');
96+
});
97+
});
98+
});
99+
100+
it('displays nested enum variants for status: Status', () => {
101+
cy.get('.form-field.status')
102+
.scrollIntoView()
103+
.within(() => {
104+
cy.contains('status: Status').should('be.visible');
105+
cy.get('.dropdown').should('have.lengthOf', 1).and('be.visible');
106+
cy.get('.dropdown').click().find('.dropdown__option').eq(2).click();
107+
cy.get('.dropdown').find('.dropdown__single-value').should('contain', 'EndingPeriod');
108+
cy.contains('BlockNumber').should('exist');
109+
cy.get("input[type='number']").should('have.lengthOf', 1).type('99999');
110+
cy.get('.dropdown').click().find('.dropdown__option').eq(3).click();
111+
cy.get('.dropdown').should('have.lengthOf', 2);
112+
113+
cy.get('.dropdown').first().find('.dropdown__single-value').should('contain', 'Ended');
114+
cy.contains('Outline').should('exist');
115+
cy.get('.dropdown').eq(1).find('.dropdown__single-value').should('contain', 'NoWinner');
116+
});
117+
});
118+
119+
it.skip('moves to step 3', () => {
120+
cy.get('[data-cy="next-btn"]').click();
121+
cy.get('[data-cy="transaction-queued"]').should('be.visible');
122+
});
123+
// todo: find out why gas estimation is too low when the app runs in cypress env
124+
// and why setting custom gas doesn't work
125+
it.skip('submits instantiate transaction', () => {
126+
cy.instantiate();
127+
});
128+
129+
it.skip('redirects to contract page after instantiation', () => {
130+
cy.url().should('contain', '/contract/');
131+
});
132+
});

cypress/fixtures/erc20.contract

+1-1
Large diffs are not rendered by default.

cypress/fixtures/flipper.contract

+1-1
Large diffs are not rendered by default.

cypress/fixtures/mother.contract

+1
Large diffs are not rendered by default.

cypress/support/commands.js

-5
This file was deleted.

cypress/support/commands.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference types="cypress" />
2+
import 'cypress-file-upload';
3+
4+
declare global {
5+
namespace Cypress {
6+
interface Chainable {
7+
instantiate(): Chainable<Element>;
8+
call(): Chainable<Element>;
9+
selectMessage(name: string, index: number): Chainable<Element>;
10+
selectAccount(name: string, index: number): Chainable<Element>;
11+
assertReturnValue(messageName: string, value: string): Chainable<Element>;
12+
}
13+
}
14+
}

cypress/support/component-index.html

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
7+
<title>Components App</title>
8+
</head>
9+
<body>
10+
<div data-cy-root></div>
11+
</body>
12+
</html>

cypress/support/component.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import './commands';
2+
3+
import { mount } from 'cypress/react';
4+
declare global {
5+
namespace Cypress {
6+
interface Chainable {
7+
mount: typeof mount;
8+
}
9+
}
10+
}
11+
12+
Cypress.Commands.add('mount', mount);

0 commit comments

Comments
 (0)