Compare commits
1222
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2eedc434d | ||
|
|
4eacbbba1c | ||
|
|
e9188344cf | ||
|
|
16c18b972d | ||
|
|
d4a1a9b193 | ||
|
|
376c5241a8 | ||
|
|
8e6d8517cf | ||
|
|
5f9ec222c1 | ||
|
|
8954c41c0a | ||
|
|
4414ab4f47 | ||
|
|
d1d4bacc68 | ||
|
|
f476688c7f | ||
|
|
8845222700 | ||
|
|
e64c464091 | ||
|
|
b2c2d0d7d6 | ||
|
|
1fb61d313c | ||
|
|
ccf5ff632c | ||
|
|
54d6aaf56f | ||
|
|
736b262947 | ||
|
|
6a07127185 | ||
|
|
45f0ba7c0e | ||
|
|
6f4ea68778 | ||
|
|
90dda20891 | ||
|
|
278eb01c2f | ||
|
|
c3157de146 | ||
|
|
d28db485c5 | ||
|
|
caa0076396 | ||
|
|
451da40ed2 | ||
|
|
47694e7b72 | ||
|
|
e01fb7f9ae | ||
|
|
ffcdfb6a6a | ||
|
|
86f0d013bb | ||
|
|
c12ac45890 | ||
|
|
47ce68455a | ||
|
|
c1675e4b49 | ||
|
|
f8c4d39b93 | ||
|
|
e55cf5ecc6 | ||
|
|
406cf22810 | ||
|
|
8ff46bab5b | ||
|
|
5f177785b6 | ||
|
|
06779f36ca | ||
|
|
29991015b5 | ||
|
|
071a6ff8bb | ||
|
|
5b8df4c414 | ||
|
|
ce6873fe54 | ||
|
|
d0e64364e3 | ||
|
|
737ffb4c35 | ||
|
|
53048ac8ef | ||
|
|
05ca1a09b9 | ||
|
|
94e398dd1c | ||
|
|
7c0a4f61e9 | ||
|
|
056d3215e8 | ||
|
|
a68b2093e4 | ||
|
|
f440f57be2 | ||
|
|
edbdbcf38e | ||
|
|
94a067e34b | ||
|
|
8b6b904cd6 | ||
|
|
27cd8086e1 | ||
|
|
6aa109131e | ||
|
|
74777e54f9 | ||
|
|
61e7450906 | ||
|
|
6a55319e04 | ||
|
|
5692d4e74c | ||
|
|
ffe89d0fe0 | ||
|
|
57e1ecac6c | ||
|
|
77e1390686 | ||
|
|
981c8649a2 | ||
|
|
501ffbfc80 | ||
|
|
f391e8c351 | ||
|
|
0e10b2108e | ||
|
|
162a934408 | ||
|
|
4581e117c4 | ||
|
|
e89b818e4c | ||
|
|
0665ac85db | ||
|
|
3c71a86b48 | ||
|
|
b381f16ace | ||
|
|
fc6ce9e99b | ||
|
|
f6fc4df1c5 | ||
|
|
fc8f12d6fc | ||
|
|
26f4c1c983 | ||
|
|
efd632f5c6 | ||
|
|
ce3da1762b | ||
|
|
5040fbfd07 | ||
|
|
74f2cfa4a5 | ||
|
|
8c8fe6878a | ||
|
|
6e0577aee4 | ||
|
|
87b41a30d8 | ||
|
|
0850f31855 | ||
|
|
43aff8e359 | ||
|
|
6e9e7c2a5c | ||
|
|
f054f4c516 | ||
|
|
f382078ee6 | ||
|
|
ebc058bcbe | ||
|
|
d3df339696 | ||
|
|
a31008ea26 | ||
|
|
5e93e98f07 | ||
|
|
bf3ff8fb6f | ||
|
|
16538ca3a3 | ||
|
|
2fa00dacaa | ||
|
|
45b7c2ad4d | ||
|
|
ed2c82487d | ||
|
|
55143331f1 | ||
|
|
cafb3b1c57 | ||
|
|
019b2d7d89 | ||
|
|
de0dc4af8e | ||
|
|
6765f4d03a | ||
|
|
7bea90e4d1 | ||
|
|
ac4762f9d7 | ||
|
|
1195309b61 | ||
|
|
8ce77e23ba | ||
|
|
5cae2ce400 | ||
|
|
b8c9f9772e | ||
|
|
c3e39b6e15 | ||
|
|
77a391448b | ||
|
|
484d7888cf | ||
|
|
187f1929fe | ||
|
|
0a4333645b | ||
|
|
310506e5af | ||
|
|
0b33ed4299 | ||
|
|
13f2e51798 | ||
|
|
fc047feeee | ||
|
|
005455c870 | ||
|
|
2c2bc391e8 | ||
|
|
445a085190 | ||
|
|
bb1b236cdf | ||
|
|
da66b7b20d | ||
|
|
ec12811f72 | ||
|
|
180de8cf25 | ||
|
|
df88e77cdf | ||
|
|
9441aee8cf | ||
|
|
2353812834 | ||
|
|
bba2b3c177 | ||
|
|
ae57bd92f4 | ||
|
|
115b642140 | ||
|
|
ce3da97a8a | ||
|
|
cabd99d3ef | ||
|
|
597e07608f | ||
|
|
e451dc54b3 | ||
|
|
9703c3b7a6 | ||
|
|
c22b6f3ce9 | ||
|
|
c740c11eaa | ||
|
|
d1fabb31a3 | ||
|
|
94f045a60b | ||
|
|
1af375bab8 | ||
|
|
5a48ba4a33 | ||
|
|
d1fd9184ce | ||
|
|
8f29824f9e | ||
|
|
fc3c73ad24 | ||
|
|
1027827576 | ||
|
|
0089920d4c | ||
|
|
35896a9cf5 | ||
|
|
5a90ebe12e | ||
|
|
6be117086a | ||
|
|
9c6dd9f254 | ||
|
|
a675576a52 | ||
|
|
98d248e02e | ||
|
|
c8d55c6293 | ||
|
|
3467feb3e5 | ||
|
|
bdf1663709 | ||
|
|
d775573543 | ||
|
|
c0fdc8d570 | ||
|
|
a9ebb36b9b | ||
|
|
f6c0082f2d | ||
|
|
931ad75cc9 | ||
|
|
f25a58a3c3 | ||
|
|
37340b4dc3 | ||
|
|
17ba3eb351 | ||
|
|
8f966a4ba6 | ||
|
|
cc7f403718 | ||
|
|
9184c2374c | ||
|
|
37247a504a | ||
|
|
2e7a6a6458 | ||
|
|
bf6c13f523 | ||
|
|
a6f3a08d2f | ||
|
|
f13e456a29 | ||
|
|
5cab040189 | ||
|
|
e47b868c53 | ||
|
|
b6286cd0f3 | ||
|
|
f66e976b66 | ||
|
|
866e7232db | ||
|
|
5a8ff90890 | ||
|
|
7b8f654906 | ||
|
|
d6f39049bd | ||
|
|
1d06be8f4e | ||
|
|
5eba8fe28f | ||
|
|
2ba0e9a1b2 | ||
|
|
43d3754c64 | ||
|
|
aae5c44fa4 | ||
|
|
878bed9c7a | ||
|
|
6f9f432c90 | ||
|
|
3f01b93159 | ||
|
|
473c244d7b | ||
|
|
d6a32f5090 | ||
|
|
71540a90fb | ||
|
|
cefdbe6a3c | ||
|
|
3928dd5c0e | ||
|
|
1521bab4c4 | ||
|
|
b3036d520f | ||
|
|
429a5a23d3 | ||
|
|
5b02fd5d54 | ||
|
|
b8309a76e7 | ||
|
|
e8ae085c06 | ||
|
|
fdcd24847c | ||
|
|
e054db39b5 | ||
|
|
a01e48d508 | ||
|
|
0d96c487d9 | ||
|
|
011e4e4cec | ||
|
|
d6e2432955 | ||
|
|
18c034b910 | ||
|
|
849cbb43c4 | ||
|
|
cd34cb181f | ||
|
|
97b3f4decf | ||
|
|
0465fb8ca9 | ||
|
|
22af89e78c | ||
|
|
adfd2f41b6 | ||
|
|
d9b1aa6da3 | ||
|
|
205f369cdc | ||
|
|
9f63fde123 | ||
|
|
f2c70e6da4 | ||
|
|
e8ff1b0b60 | ||
|
|
f5989b0022 | ||
|
|
653ca034f2 | ||
|
|
ef25552311 | ||
|
|
0a8439d125 | ||
|
|
702ea0c6b4 | ||
|
|
0f2b86770a | ||
|
|
cd6d350047 | ||
|
|
c5a06c57ac | ||
|
|
5bb31fa38d | ||
|
|
fd8f38803c | ||
|
|
a22f8f0ef1 | ||
|
|
c2d5c71f35 | ||
|
|
f88c35648e | ||
|
|
348c61530e | ||
|
|
f364dabe2f | ||
|
|
1ae1fdff5e | ||
|
|
3ed615c36e | ||
|
|
6b50816234 | ||
|
|
f5e67a0c2d | ||
|
|
e4d4646878 | ||
|
|
bbd1536c18 | ||
|
|
f8444c0c88 | ||
|
|
b1adf776c0 | ||
|
|
ad898ab0e8 | ||
|
|
5b0fffc5cb | ||
|
|
a9dbc0f6eb | ||
|
|
a71786817f | ||
|
|
9ee0cb7b65 | ||
|
|
8c4aa41240 | ||
|
|
91ac977704 | ||
|
|
1190033776 | ||
|
|
b99e2235b4 | ||
|
|
002bc4f188 | ||
|
|
0c2af8c75f | ||
|
|
a305c5b8a9 | ||
|
|
4c3ceb94f3 | ||
|
|
2215864d0c | ||
|
|
589785ce09 | ||
|
|
babd99088d | ||
|
|
d155909fe4 | ||
|
|
1d3298cdc9 | ||
|
|
c2baaa2dd9 | ||
|
|
e2c4c4a841 | ||
|
|
7a89dc984d | ||
|
|
19580fb9ce | ||
|
|
0721099d7d | ||
|
|
9ee2810c51 | ||
|
|
9f8f99b50b | ||
|
|
3208f762fd | ||
|
|
7a06c946d1 | ||
|
|
70e172045d | ||
|
|
06c540666e | ||
|
|
f81503b8f7 | ||
|
|
1699e33cbd | ||
|
|
5280b79927 | ||
|
|
34526d527e | ||
|
|
7b9aae700d | ||
|
|
e9cb2ad217 | ||
|
|
6dd4536fdd | ||
|
|
522eb521b9 | ||
|
|
3aa0e974c2 | ||
|
|
a0eb381ab1 | ||
|
|
a51ab4d98d | ||
|
|
67643afd74 | ||
|
|
145539e0c0 | ||
|
|
48109b584c | ||
|
|
a4512e6064 | ||
|
|
45adac2932 | ||
|
|
fff8fdd78b | ||
|
|
079d8bc48d | ||
|
|
9658363b56 | ||
|
|
e24289330e | ||
|
|
0fbd2bfa72 | ||
|
|
43ef4fad6f | ||
|
|
828dcf6e93 | ||
|
|
4a583ccf09 | ||
|
|
8bcad28f50 | ||
|
|
b375681076 | ||
|
|
8a2db407cf | ||
|
|
491844d3fd | ||
|
|
f1f51c5ecd | ||
|
|
9bc78f8ccd | ||
|
|
a03031a283 | ||
|
|
b499c293e3 | ||
|
|
65747e8d26 | ||
|
|
9a84eda21b | ||
|
|
fba98f2fd0 | ||
|
|
3e32d0f396 | ||
|
|
b75d189054 | ||
|
|
afa627864c | ||
|
|
b13a866b2a | ||
|
|
c65f07eee6 | ||
|
|
5bd18cc127 | ||
|
|
ff3519279d | ||
|
|
fd338c7400 | ||
|
|
0db5d0ce87 | ||
|
|
42c316c8e1 | ||
|
|
9e2474d39a | ||
|
|
2533e5ec44 | ||
|
|
22a43249ea | ||
|
|
b40ee0caf5 | ||
|
|
95aca70434 | ||
|
|
5e13266250 | ||
|
|
5c0588887c | ||
|
|
089a7daac7 | ||
|
|
02b6251b1a | ||
|
|
9ce8907861 | ||
|
|
3be9126906 | ||
|
|
77316092d1 | ||
|
|
fca229e62a | ||
|
|
162b167860 | ||
|
|
a4149cc55e | ||
|
|
4161a74eaf | ||
|
|
5008e57a56 | ||
|
|
89031bf826 | ||
|
|
61501d4d14 | ||
|
|
f8f3ce7df9 | ||
|
|
f7aab5a78a | ||
|
|
2904975fd3 | ||
|
|
4f55c946f0 | ||
|
|
de637fee13 | ||
|
|
69c6db8ec2 | ||
|
|
8dc465c1d1 | ||
|
|
0be934edca | ||
|
|
653688c1dd | ||
|
|
34400be18a | ||
|
|
d4a0a4a42c | ||
|
|
fabf643ce7 | ||
|
|
45e210092c | ||
|
|
1dc3b3fd05 | ||
|
|
0a80e3e39d | ||
|
|
7b8d9dc94b | ||
|
|
c0320c2468 | ||
|
|
11173d6614 | ||
|
|
e3324331e2 | ||
|
|
0a6e3cb6f5 | ||
|
|
68ede90609 | ||
|
|
09778a5d3f | ||
|
|
59993e3ea6 | ||
|
|
54161a6dba | ||
|
|
26f524ea07 | ||
|
|
db13e6f21d | ||
|
|
4a679813ba | ||
|
|
a4279d5b5e | ||
|
|
1a274a67c3 | ||
|
|
1e9ff31fd7 | ||
|
|
aa2b755b93 | ||
|
|
4a7296312a | ||
|
|
1418a2259f | ||
|
|
7e5ef0157a | ||
|
|
0cf764f11e | ||
|
|
1c626aeeb2 | ||
|
|
c4cec47b0b | ||
|
|
1a67c41bb4 | ||
|
|
fc66240390 | ||
|
|
fc0da98683 | ||
|
|
4d0dafb113 | ||
|
|
eda431be9e | ||
|
|
4ff1749e54 | ||
|
|
e7a108fd7a | ||
|
|
5d3f2ea7d8 | ||
|
|
1ab27574ca | ||
|
|
0ea3c43e78 | ||
|
|
9e183568b0 | ||
|
|
69fd5b26b6 | ||
|
|
2d5b926609 | ||
|
|
98a2ba79a8 | ||
|
|
0e6e986937 | ||
|
|
15d6ea0c05 | ||
|
|
0358ce6293 | ||
|
|
0791be4636 | ||
|
|
07442848ee | ||
|
|
d5f0bd419e | ||
|
|
2c7934a8c7 | ||
|
|
fae61c28ca | ||
|
|
fcd4541625 | ||
|
|
8e0a92fdec | ||
|
|
6e7109ed87 | ||
|
|
f2c698ce06 | ||
|
|
ad9a3a3400 | ||
|
|
efeac49d58 | ||
|
|
2d430710ab | ||
|
|
bca1a98985 | ||
|
|
8a080d3279 | ||
|
|
f121836b4e | ||
|
|
b641c82ad8 | ||
|
|
bf73559c30 | ||
|
|
653cec2592 | ||
|
|
579c884a5a | ||
|
|
b2279c7e47 | ||
|
|
a8c17b6807 | ||
|
|
b4c7dc6f59 | ||
|
|
d44392bebf | ||
|
|
bd679957e2 | ||
|
|
e30d48555e | ||
|
|
b4b2416780 | ||
|
|
dc7832ac81 | ||
|
|
91207d31ee | ||
|
|
58efb460a6 | ||
|
|
044f98777b | ||
|
|
00fdc5e81a | ||
|
|
adfad3bafb | ||
|
|
e6ae88905c | ||
|
|
3953ed9953 | ||
|
|
b48ab58e6a | ||
|
|
e44cf1ff53 | ||
|
|
bded1d32ba | ||
|
|
76ddf45f4c | ||
|
|
6a2c9004d8 | ||
|
|
77ee88b43e | ||
|
|
e4958ce3c0 | ||
|
|
ecedb879f8 | ||
|
|
89700803d6 | ||
|
|
65c9fa5df2 | ||
|
|
02399c40fc | ||
|
|
2cb3b55a3b | ||
|
|
3acc0e2401 | ||
|
|
c734556eb4 | ||
|
|
792ff1fc1f | ||
|
|
3740a97d7e | ||
|
|
20350ae2cf | ||
|
|
50e93dad64 | ||
|
|
c87c4bbc91 | ||
|
|
00679c75f2 | ||
|
|
7fe722dad0 | ||
|
|
0d8a74dfd0 | ||
|
|
928b899c46 | ||
|
|
72d93bb568 | ||
|
|
db75261cd5 | ||
|
|
66fbe28cf0 | ||
|
|
9f3b8ecade | ||
|
|
cd13c61e90 | ||
|
|
246577f401 | ||
|
|
503ca26f68 | ||
|
|
13e374a6bb | ||
|
|
bed411040c | ||
|
|
d0173f15b2 | ||
|
|
106b040977 | ||
|
|
bf84b04a30 | ||
|
|
9f442698a4 | ||
|
|
d1265a6af7 | ||
|
|
313eff1c95 | ||
|
|
7f949a276c | ||
|
|
76a53dc7e3 | ||
|
|
72821dd183 | ||
|
|
40d2e033e0 | ||
|
|
e6d0b6ac1f | ||
|
|
99161497fe | ||
|
|
bf6ab32230 | ||
|
|
28bcb4ada1 | ||
|
|
5812c8858d | ||
|
|
abf146895d | ||
|
|
4476e6c6d1 | ||
|
|
d967000e8f | ||
|
|
91d3d97399 | ||
|
|
ca700aa103 | ||
|
|
265bf7fb35 | ||
|
|
7a5c6f3a25 | ||
|
|
91e3460e9d | ||
|
|
cf0c45aaa5 | ||
|
|
bf63f9d46d | ||
|
|
023a83e0f7 | ||
|
|
4f6ed1eadf | ||
|
|
85e293f3ec | ||
|
|
7b15d2db10 | ||
|
|
b1b9695998 | ||
|
|
6b8b33a78b | ||
|
|
4fd0f6819a | ||
|
|
5032d87f98 | ||
|
|
b15d9cb7dc | ||
|
|
6aa7cc5bab | ||
|
|
c37f9ebe66 | ||
|
|
768b3b29f0 | ||
|
|
378946f22b | ||
|
|
523598645a | ||
|
|
a9267de653 | ||
|
|
ce22da1c9a | ||
|
|
2460a62a3e | ||
|
|
f37a8720a2 | ||
|
|
f5fe3cc18d | ||
|
|
288f075cac | ||
|
|
ca46861fd8 | ||
|
|
2cb6ede492 | ||
|
|
eefcc96149 | ||
|
|
7a99ded8e9 | ||
|
|
4a4cdaa2b8 | ||
|
|
cd969549ab | ||
|
|
5a73b7e769 | ||
|
|
a4f41eabb6 | ||
|
|
351a20abad | ||
|
|
8d42481130 | ||
|
|
8a59722f09 | ||
|
|
de48ffa563 | ||
|
|
00c5252aea | ||
|
|
9acc083c11 | ||
|
|
77264e80f1 | ||
|
|
50378bc8d2 | ||
|
|
b88da51c68 | ||
|
|
2ae56bb113 | ||
|
|
752af9a983 | ||
|
|
a01df081eb | ||
|
|
c2d14d1abe | ||
|
|
a76ff44ffd | ||
|
|
ac2e262bf3 | ||
|
|
ab180875b1 | ||
|
|
66d3e89b9e | ||
|
|
c9cce87606 | ||
|
|
50232ab089 | ||
|
|
e86fb6d0b4 | ||
|
|
473ef3ae83 | ||
|
|
77ab719b37 | ||
|
|
a10f67d7ab | ||
|
|
66d2b83513 | ||
|
|
e4fd040933 | ||
|
|
30c1dbd019 | ||
|
|
5e558c4cd3 | ||
|
|
85060a0421 | ||
|
|
b336291b49 | ||
|
|
0f7bffd38b | ||
|
|
3665375b75 | ||
|
|
8572c0a570 | ||
|
|
6a29dc82ef | ||
|
|
c19bcc5e0c | ||
|
|
6593bbf37b | ||
|
|
1e5e57b978 | ||
|
|
5394b00349 | ||
|
|
32f5eaade5 | ||
|
|
b7a440132d | ||
|
|
d6ecdf80fa | ||
|
|
21e545d941 | ||
|
|
ef5fa7cba6 | ||
|
|
d1f465e2af | ||
|
|
2740df48a3 | ||
|
|
cf1f10e324 | ||
|
|
f09a1abcfa | ||
|
|
3de83c6eb8 | ||
|
|
e6545c13af | ||
|
|
613582ee00 | ||
|
|
77582a62a8 | ||
|
|
384c96376a | ||
|
|
0a92f62a66 | ||
|
|
f589f4a9fa | ||
|
|
b9869da492 | ||
|
|
653fec7025 | ||
|
|
8bf948578c | ||
|
|
11b08d16e4 | ||
|
|
5081b11c93 | ||
|
|
a1366e59ec | ||
|
|
0b035d5064 | ||
|
|
e1a38f2197 | ||
|
|
8201e87050 | ||
|
|
3aaf4e724d | ||
|
|
6fe06e9f62 | ||
|
|
cd8b0e7caf | ||
|
|
4282faa9d9 | ||
|
|
a24846fd06 | ||
|
|
72de53cf10 | ||
|
|
f3c0313a59 | ||
|
|
4c0de8f22b | ||
|
|
254fb57af0 | ||
|
|
6371199537 | ||
|
|
75cb48a4b9 | ||
|
|
933878acf5 | ||
|
|
5995df8e68 | ||
|
|
4d5b7cf5e7 | ||
|
|
e1f60446f0 | ||
|
|
7f271069ab | ||
|
|
1ae7af5d59 | ||
|
|
b9c5d9ee96 | ||
|
|
ae1768a669 | ||
|
|
a1bfe7f1a7 | ||
|
|
b0f1aeedae | ||
|
|
da59e83b19 | ||
|
|
360624282d | ||
|
|
9e054c7c85 | ||
|
|
911e927ab0 | ||
|
|
e4a51061a3 | ||
|
|
c15051d457 | ||
|
|
460ccdb3a2 | ||
|
|
959fb9cf62 | ||
|
|
2c7170d0e9 | ||
|
|
8a28a4afd4 | ||
|
|
48072eb4c3 | ||
|
|
ae92ff2580 | ||
|
|
c99b7fbb02 | ||
|
|
a9b1767a42 | ||
|
|
3cd60ad3e6 | ||
|
|
2f4bc9484b | ||
|
|
d4f5643799 | ||
|
|
72c49e62a8 | ||
|
|
0988edc129 | ||
|
|
4d266963ae | ||
|
|
ec12c7ecfd | ||
|
|
045dace274 | ||
|
|
2346b2f1a0 | ||
|
|
31859cb779 | ||
|
|
20507ecdd1 | ||
|
|
13ba661ea0 | ||
|
|
03f5b9fc7b | ||
|
|
b9fed65615 | ||
|
|
d671e51edf | ||
|
|
4f3295d3fc | ||
|
|
7be7bab1ca | ||
|
|
61380f06b9 | ||
|
|
c113856d63 | ||
|
|
90884beee8 | ||
|
|
d9d2e428f4 | ||
|
|
ba8d54b761 | ||
|
|
dce4edb943 | ||
|
|
b468a0fd84 | ||
|
|
c98fbe7453 | ||
|
|
d20928ec06 | ||
|
|
289559fe4f | ||
|
|
071fd6128c | ||
|
|
554d07a31b | ||
|
|
48b62380b0 | ||
|
|
a9f4604bad | ||
|
|
54f2519a62 | ||
|
|
260e7bf9cf | ||
|
|
83db796cdb | ||
|
|
2f6800212e | ||
|
|
77e16ad1b7 | ||
|
|
dcce983842 | ||
|
|
53ce9bfaa2 | ||
|
|
6ae34d296f | ||
|
|
ee2aafb99c | ||
|
|
e8fdb63323 | ||
|
|
3df9270b57 | ||
|
|
ab26667265 | ||
|
|
9fc6b255c8 | ||
|
|
91f61d8bb5 | ||
|
|
bbfda65bcc | ||
|
|
45f2e926c3 | ||
|
|
ccadbc2cb6 | ||
|
|
7128acb7b8 | ||
|
|
357bdaa4f3 | ||
|
|
f3fe43724e | ||
|
|
03f5ca3096 | ||
|
|
03d19349af | ||
|
|
1f53b2cbb5 | ||
|
|
b010c98346 | ||
|
|
29d57af2ba | ||
|
|
fd7940c4cf | ||
|
|
8ab7ac8414 | ||
|
|
ce7cd03c3e | ||
|
|
dfa3355a68 | ||
|
|
8553904d81 | ||
|
|
10afa3c1d4 | ||
|
|
ecd362615e | ||
|
|
0d9bd67465 | ||
|
|
b5aa92f007 | ||
|
|
2955790be1 | ||
|
|
89ff9309e9 | ||
|
|
e1df3489f6 | ||
|
|
f0fa9ca5e1 | ||
|
|
9d92477c2a | ||
|
|
b1a198be85 | ||
|
|
5fb385d43c | ||
|
|
8014b279fe | ||
|
|
f4bde57294 | ||
|
|
2e5ff07824 | ||
|
|
7accb42793 | ||
|
|
9229e7f686 | ||
|
|
b570b9c4c1 | ||
|
|
ed78261aad | ||
|
|
363ca9c6e1 | ||
|
|
bafd3c384c | ||
|
|
3b830abe07 | ||
|
|
1787e22b69 | ||
|
|
f105133959 | ||
|
|
aff0e46d23 | ||
|
|
7e4aafcb77 | ||
|
|
643d5408cd | ||
|
|
2a2af3ba9c | ||
|
|
8d4e4a1228 | ||
|
|
84068c8081 | ||
|
|
e1185b9a96 | ||
|
|
f390448c24 | ||
|
|
70943c523c | ||
|
|
dcb79e70d3 | ||
|
|
cc72cdbe16 | ||
|
|
3fd1817e1e | ||
|
|
b483c13f81 | ||
|
|
bde7a9fbf9 | ||
|
|
5fd93ee9c8 | ||
|
|
9ac07fd98a | ||
|
|
9c975ed822 | ||
|
|
138bf7da00 | ||
|
|
3e7fe517f3 | ||
|
|
4f960d09b3 | ||
|
|
351a5aaf96 | ||
|
|
381d9011a0 | ||
|
|
861760b4f9 | ||
|
|
18f1c0014c | ||
|
|
474543d91b | ||
|
|
662753c74b | ||
|
|
71aa8882bc | ||
|
|
ecfbccf8ed | ||
|
|
2aad6b1a14 | ||
|
|
8463d371ad | ||
|
|
0914e7ce4b | ||
|
|
7c82144f37 | ||
|
|
17ad0cc975 | ||
|
|
eb58dcd350 | ||
|
|
c8c0bf2cc0 | ||
|
|
00579fcb1f | ||
|
|
5e13173efb | ||
|
|
3a6c0554cc | ||
|
|
d4b237a6ba | ||
|
|
5e765f3172 | ||
|
|
5ef8c11e6e | ||
|
|
84bae12def | ||
|
|
c5b22f5bfb | ||
|
|
071dcc61a8 | ||
|
|
ce0ccdfebc | ||
|
|
f1524d3fcd | ||
|
|
82e5128ba1 | ||
|
|
2bfc3abd15 | ||
|
|
bd3b557f3d | ||
|
|
c899da52c2 | ||
|
|
b9c4057ce5 | ||
|
|
d02feee5c6 | ||
|
|
6d1ab36a72 | ||
|
|
5690fe91a8 | ||
|
|
1daae6a233 | ||
|
|
cd35a34246 | ||
|
|
77ada964b5 | ||
|
|
d7404bcd76 | ||
|
|
91862701a7 | ||
|
|
0136efd78f | ||
|
|
c3a4954d0d | ||
|
|
528fd96721 | ||
|
|
155923ff8e | ||
|
|
5f7e60d22f | ||
|
|
351583ce8f | ||
|
|
88bf58b974 | ||
|
|
fcda4d7fcd | ||
|
|
b1b0039b33 | ||
|
|
6854980b32 | ||
|
|
d8aac83f2d | ||
|
|
5c17045752 | ||
|
|
8863209342 | ||
|
|
462bf6b8b5 | ||
|
|
e0b701e05a | ||
|
|
eca212eee0 | ||
|
|
c25858037a | ||
|
|
081f5b48e2 | ||
|
|
e306d1c9fa | ||
|
|
737dec4f5f | ||
|
|
6780b2f618 | ||
|
|
08dc60086d | ||
|
|
4595db07b0 | ||
|
|
c3da31146f | ||
|
|
b6a9faaa7c | ||
|
|
477d523582 | ||
|
|
a2b112d898 | ||
|
|
621ba497e8 | ||
|
|
a6964bc3cf | ||
|
|
48a9998441 | ||
|
|
bd1bd94ee7 | ||
|
|
2fdebbd694 | ||
|
|
96af8035d0 | ||
|
|
051e3da366 | ||
|
|
af5e666d5e | ||
|
|
333a7d6206 | ||
|
|
cb6b988466 | ||
|
|
806ea74f7e | ||
|
|
8693f3bfd8 | ||
|
|
58c6652e29 | ||
|
|
e8adff25c4 | ||
|
|
3e594a783a | ||
|
|
1f7117075b | ||
|
|
4c850ecead | ||
|
|
19f8fa909e | ||
|
|
8214858685 | ||
|
|
868f8e21dc | ||
|
|
6ead84b57c | ||
|
|
3975477985 | ||
|
|
8a0c15ac11 | ||
|
|
a4f512f946 | ||
|
|
88f3c278f8 | ||
|
|
34d5f3fd13 | ||
|
|
a060f94146 | ||
|
|
f649d78565 | ||
|
|
b611d30b31 | ||
|
|
545324ccd6 | ||
|
|
8c16759621 | ||
|
|
e724aac2f1 | ||
|
|
e3a6b75130 | ||
|
|
8d8f6b4928 | ||
|
|
6c1c5bf2a9 | ||
|
|
74a3aa8566 | ||
|
|
4cbd7a4928 | ||
|
|
201cf07a01 | ||
|
|
90ceeebf15 | ||
|
|
965f7f6e2d | ||
|
|
18633efec3 | ||
|
|
a0b758334a | ||
|
|
9f3026aa4b | ||
|
|
faa359e2fb | ||
|
|
1d178b940f | ||
|
|
b2dd053621 | ||
|
|
77f5193b7a | ||
|
|
055a0070db | ||
|
|
29b221c86a | ||
|
|
3e619acd4b | ||
|
|
c720719ac5 | ||
|
|
c676a7c1e2 | ||
|
|
ca63a671c8 | ||
|
|
4165f3905f | ||
|
|
a359ccb755 | ||
|
|
e584737005 | ||
|
|
b12c5ed39f | ||
|
|
d3dbd64cf1 | ||
|
|
2df62bca61 | ||
|
|
fd2a434b46 | ||
|
|
4e7a78f988 | ||
|
|
327619652a | ||
|
|
d2339205cc | ||
|
|
63df4cf465 | ||
|
|
5e34178291 | ||
|
|
0c44664aa5 | ||
|
|
7ef1d75529 | ||
|
|
84795b2d6a | ||
|
|
300019f108 | ||
|
|
2d0aebd3fe | ||
|
|
9e86d2aff5 | ||
|
|
0f7e76ae01 | ||
|
|
56732d1503 | ||
|
|
5c19263653 | ||
|
|
0d911b3029 | ||
|
|
9f059e7862 | ||
|
|
92ea93dadb | ||
|
|
6165aefad7 | ||
|
|
c95369d914 | ||
|
|
5dbc5d7997 | ||
|
|
47b3d5b55f | ||
|
|
da7c0b84f7 | ||
|
|
67e602ebb1 | ||
|
|
f407110e95 | ||
|
|
66a85cce2a | ||
|
|
93f8450ecb | ||
|
|
0447a6cf5d | ||
|
|
498be838c7 | ||
|
|
fc3b1bd9ab | ||
|
|
090d850647 | ||
|
|
95280f5b26 | ||
|
|
101ae00a57 | ||
|
|
613cce3683 | ||
|
|
41fa18ed45 | ||
|
|
c941893cdd | ||
|
|
d049b471d8 | ||
|
|
010ffc0f46 | ||
|
|
dd56e4083b | ||
|
|
09d1ba68ba | ||
|
|
3959daa2b7 | ||
|
|
c599b02c89 | ||
|
|
e5ebaf02d2 | ||
|
|
ae54616c1f | ||
|
|
8f3cf390c5 | ||
|
|
64cf0c90a7 | ||
|
|
3edd91eadb | ||
|
|
676aeb07a4 | ||
|
|
52ee998bf0 | ||
|
|
727e2cd2ff | ||
|
|
06b2dd18f9 | ||
|
|
a189f38cdc | ||
|
|
d18681a5f0 | ||
|
|
9e2d03853a | ||
|
|
18c36d5e6b | ||
|
|
dcfb8f9527 | ||
|
|
a64acecf4b | ||
|
|
0998952196 | ||
|
|
2fd4d379cf | ||
|
|
c99d055b24 | ||
|
|
f1fa13e0bd | ||
|
|
ae020a8dca | ||
|
|
fa871a49c7 | ||
|
|
4205dd471a | ||
|
|
1040077c2b | ||
|
|
9d346d7846 | ||
|
|
b68136ba3f | ||
|
|
f5ad2de5a5 | ||
|
|
b3bfb90804 | ||
|
|
327ebf3496 | ||
|
|
1098accb84 | ||
|
|
52183555a9 | ||
|
|
51187029aa | ||
|
|
f7f7be59d3 | ||
|
|
49e3baf094 | ||
|
|
a575b4c502 | ||
|
|
af2e52d59c | ||
|
|
2725bef159 | ||
|
|
070f1905e2 | ||
|
|
6705eb4398 | ||
|
|
9d3fc04597 | ||
|
|
d3929b8d4a | ||
|
|
8a5579b1cc | ||
|
|
197f2e8097 | ||
|
|
8f5a2276de | ||
|
|
46513685c8 | ||
|
|
e66e96f12d | ||
|
|
31c8365812 | ||
|
|
460f534c7a | ||
|
|
24cd080dd4 | ||
|
|
f10c33748f | ||
|
|
ac163d0194 | ||
|
|
e7616e64f8 | ||
|
|
adca4600c2 | ||
|
|
a8eef1cb53 | ||
|
|
58d4bd1459 | ||
|
|
26020b5f71 | ||
|
|
16d345eb4a | ||
|
|
3b3fcab4f4 | ||
|
|
f54a629179 | ||
|
|
56f48671cc | ||
|
|
a65ee358ff | ||
|
|
7b8d814724 | ||
|
|
cd237dc491 | ||
|
|
9ad00adc5a | ||
|
|
fab055bb34 | ||
|
|
d66956d080 | ||
|
|
9848c8ffff | ||
|
|
b0e8ed62d2 | ||
|
|
289ff3b5b3 | ||
|
|
1d965187dc | ||
|
|
8bf3615001 | ||
|
|
1b77800b62 | ||
|
|
979dc2904b | ||
|
|
0fec3bcf3c | ||
|
|
4542e25ec9 | ||
|
|
acd96fbf21 | ||
|
|
96bf12d4dd | ||
|
|
47b81188d4 | ||
|
|
efada06256 | ||
|
|
311ef41138 | ||
|
|
5dd0cafeca | ||
|
|
f3d6aa1129 | ||
|
|
93dbc32f22 | ||
|
|
fd1827ef67 | ||
|
|
9c9948e4c1 | ||
|
|
79b0868ff7 | ||
|
|
a8401d4c6f | ||
|
|
c030264211 | ||
|
|
068f7b3fc3 | ||
|
|
4430f0c5f6 | ||
|
|
0a14000708 | ||
|
|
9ca6fe3cbf | ||
|
|
4f96472375 | ||
|
|
cddf2ba00b | ||
|
|
d01d6f5815 | ||
|
|
2998e165f6 | ||
|
|
886b0cb6d3 | ||
|
|
6799a06acd | ||
|
|
47a6bcff78 | ||
|
|
06dcd00b53 | ||
|
|
fd7aa5bd78 | ||
|
|
384cfbc70b | ||
|
|
9d14f603b3 | ||
|
|
8fab8dad6c | ||
|
|
76bf45b461 | ||
|
|
f128f41ea0 | ||
|
|
fcb992b64e | ||
|
|
1251c8b89b | ||
|
|
a796a38200 | ||
|
|
cf83fe9469 | ||
|
|
43c48741e2 | ||
|
|
cc6ea6176a | ||
|
|
6b280d8a70 | ||
|
|
891b441427 | ||
|
|
86739dfaad | ||
|
|
d87adfd7d5 | ||
|
|
7364abbbf2 | ||
|
|
1bd56c9bdb | ||
|
|
a985bc7317 | ||
|
|
1cab9abfd5 | ||
|
|
26a82ab2ab | ||
|
|
69a0830e80 | ||
|
|
4eb6e09bd4 | ||
|
|
a02100b079 | ||
|
|
4bb78e9891 | ||
|
|
79f360dc24 | ||
|
|
a3fcd6b7dc | ||
|
|
23ce480daa | ||
|
|
da209d96b0 | ||
|
|
9166a85a55 | ||
|
|
a5ec1f708f | ||
|
|
3afdc0c8da | ||
|
|
757d545d97 | ||
|
|
fc4d40f3cf | ||
|
|
ff47eda692 | ||
|
|
7483d9ce1d | ||
|
|
f9209cf327 | ||
|
|
420a29680d | ||
|
|
19c9e79647 | ||
|
|
4cd856fcb8 | ||
|
|
649cc61280 | ||
|
|
35f47b75ac | ||
|
|
0697acdac9 | ||
|
|
92ec7166bc | ||
|
|
56b5214dbf | ||
|
|
94509a29c5 | ||
|
|
4e2b6b2d04 | ||
|
|
c29087cc96 | ||
|
|
5bde096977 | ||
|
|
923dc09eb6 | ||
|
|
4fbdb337dd | ||
|
|
6f4a5b9097 | ||
|
|
0c26d99ce2 | ||
|
|
5aedeba4ff | ||
|
|
5789d3496b | ||
|
|
d95fdca0d4 | ||
|
|
e4bf61c2e2 | ||
|
|
0ba54cd8a4 | ||
|
|
eee85c231b | ||
|
|
afdb387742 | ||
|
|
63698fbb80 | ||
|
|
58d8f7857e | ||
|
|
0931730582 | ||
|
|
3aefa10a4f | ||
|
|
efeccc7972 | ||
|
|
95f4e489b2 | ||
|
|
0033f3c5f5 | ||
|
|
abf69786ba | ||
|
|
0a5fb9d917 | ||
|
|
8e82bf43fb | ||
|
|
4bb57e9c47 | ||
|
|
ce6d4cb35d | ||
|
|
647f04656f | ||
|
|
67186bf4c0 | ||
|
|
96007bd230 | ||
|
|
554d9ecb06 | ||
|
|
a12a5b3a15 | ||
|
|
a79e2f85bc | ||
|
|
a2190fdde0 | ||
|
|
849415a78e | ||
|
|
d33f5428b6 | ||
|
|
c73b01d549 | ||
|
|
4286bc37b3 | ||
|
|
d4ecb88fb4 | ||
|
|
57a8955795 | ||
|
|
ac53b1f97a | ||
|
|
4b83a10475 | ||
|
|
7e957a2841 | ||
|
|
38c0e70bbc | ||
|
|
94c40cce7d | ||
|
|
0059050440 | ||
|
|
f7be890eb9 | ||
|
|
59ae971a07 | ||
|
|
cfcd5efa2c | ||
|
|
fb1e85893c | ||
|
|
084972577e | ||
|
|
94df5ca048 | ||
|
|
6142a7afbb | ||
|
|
d2e0c57676 | ||
|
|
e48d8d8715 | ||
|
|
7de333e1ea | ||
|
|
2123afc4b0 | ||
|
|
ea91fa1723 | ||
|
|
4dcdec7a35 | ||
|
|
196f1914a3 | ||
|
|
ea4c7a4a37 | ||
|
|
c22fec97b4 | ||
|
|
989a0456c0 | ||
|
|
8c79a94a8c | ||
|
|
a7a6a820c0 | ||
|
|
1fa09a690e | ||
|
|
b0a84970f2 | ||
|
|
b2a115f935 | ||
|
|
9dfed0a211 | ||
|
|
5283676be7 | ||
|
|
a82509f0e0 | ||
|
|
64c92ce91d | ||
|
|
b7426e73d8 | ||
|
|
969bfd6945 | ||
|
|
200487a6fc | ||
|
|
3f330e78c5 | ||
|
|
05726c6a5a | ||
|
|
fcace22393 | ||
|
|
b95462ea38 | ||
|
|
42cd32f376 | ||
|
|
55d6dd4dce | ||
|
|
7017e24adf | ||
|
|
a4d597d426 | ||
|
|
b51376ba0d | ||
|
|
c72b5d7912 | ||
|
|
462a5bc84b | ||
|
|
3edbc71787 | ||
|
|
ee67736ac5 | ||
|
|
8bb6b7a942 | ||
|
|
c1c0d960eb | ||
|
|
a25e2ad101 | ||
|
|
7ee8bd47c8 | ||
|
|
b72ed66bb3 | ||
|
|
589bd8d29f | ||
|
|
7f7c425e34 | ||
|
|
062ea5e968 | ||
|
|
87e187c225 | ||
|
|
c0f15d8212 | ||
|
|
499de6e836 | ||
|
|
e72d117a62 | ||
|
|
abede79a4c | ||
|
|
fa415318ff | ||
|
|
718959b920 | ||
|
|
6d0ebc3a1b | ||
|
|
5f5cb965e3 | ||
|
|
86090c295c | ||
|
|
6983587f28 | ||
|
|
5e87baf174 | ||
|
|
fc93bbd7c5 | ||
|
|
ce832ad6f4 | ||
|
|
a5d53eee77 | ||
|
|
4dd63da62b | ||
|
|
2e8dd294de | ||
|
|
8917ceb08a | ||
|
|
87f116daee | ||
|
|
9e91746488 | ||
|
|
5b4ed1a0c3 | ||
|
|
56731e34cc | ||
|
|
026e5f5679 | ||
|
|
e75c160579 | ||
|
|
5d613396e1 | ||
|
|
da551f9d3c | ||
|
|
47e4861fdb | ||
|
|
1a85a89440 | ||
|
|
7ab412a8f7 | ||
|
|
5a14174a81 | ||
|
|
5e00b93783 | ||
|
|
31d9b023e6 | ||
|
|
103e503a47 | ||
|
|
1952cb0e78 | ||
|
|
69340f4ddf | ||
|
|
c52bf2200e | ||
|
|
8dccee69f2 | ||
|
|
c7a6fdd879 | ||
|
|
8bcdaf4cda | ||
|
|
3eb1359504 | ||
|
|
39cf8ad4d6 | ||
|
|
faef98f0ae | ||
|
|
710e2daa27 | ||
|
|
06ea3924fa | ||
|
|
965e2d8972 | ||
|
|
73de2fed43 | ||
|
|
536859e067 | ||
|
|
54bfaef473 | ||
|
|
e2cad707a7 | ||
|
|
ec8d9798ab | ||
|
|
e629c1e81a | ||
|
|
25feed793c | ||
|
|
2867367e16 | ||
|
|
50b408e74d | ||
|
|
8f531c7c05 | ||
|
|
350d16fee1 | ||
|
|
c052c66266 | ||
|
|
e2032df236 | ||
|
|
9ece19869d | ||
|
|
22abc8160c | ||
|
|
b1a7a22bf9 | ||
|
|
c4b38cc0dc | ||
|
|
44d6d931d5 | ||
|
|
7f1e47d7fd | ||
|
|
0eddfa4073 | ||
|
|
83a034ce91 | ||
|
|
5ab5aa01a2 | ||
|
|
b22f9156b5 | ||
|
|
bab5e2018b | ||
|
|
3f7692ce75 | ||
|
|
b69fb3b0e5 | ||
|
|
83af6e74f6 | ||
|
|
b9908003ba | ||
|
|
1f9af79215 | ||
|
|
e31e40a7ef | ||
|
|
81f6b3cc15 | ||
|
|
8adb208a91 | ||
|
|
545c3a17d2 | ||
|
|
ad76988f63 | ||
|
|
d2bbd7e1fc | ||
|
|
fb1ebb3bdf | ||
|
|
b2489537ef | ||
|
|
382a7ea411 | ||
|
|
1ae2b4c153 | ||
|
|
e8875e817e | ||
|
|
4a01c880f3 | ||
|
|
f7f00dc7bb | ||
|
|
d151b6e923 | ||
|
|
8f8e9c1061 | ||
|
|
b40358a723 | ||
|
|
01f0934da3 | ||
|
|
df7755dbc4 | ||
|
|
9073234040 | ||
|
|
053cd0fa27 | ||
|
|
961dc88dc7 | ||
|
|
b16607a844 | ||
|
|
d0d804631c | ||
|
|
3e778a88dc | ||
|
|
dbb09c9c79 | ||
|
|
06f44b67de | ||
|
|
1861852852 | ||
|
|
3ee0eee80d | ||
|
|
69ce870a81 | ||
|
|
d8467c3206 | ||
|
|
de87f40a01 |
@@ -3,3 +3,7 @@ apps/**/node_modules/*
|
|||||||
tmp/*
|
tmp/*
|
||||||
.dockerignore
|
.dockerignore
|
||||||
dockerfiles
|
dockerfiles
|
||||||
|
node_modules
|
||||||
|
.git
|
||||||
|
.github
|
||||||
|
.vscode
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
+13
-7
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"root": true,
|
"root": true,
|
||||||
"ignorePatterns": ["**/*"],
|
"ignorePatterns": ["**/*"],
|
||||||
"plugins": ["@nrwl/nx", "eslint-plugin-unicorn", "jsx-a11y", "jest"],
|
"plugins": ["@nx", "eslint-plugin-unicorn", "jsx-a11y", "jest"],
|
||||||
"settings": {
|
"settings": {
|
||||||
"jsx-a11y": {
|
"jsx-a11y": {
|
||||||
"components": {
|
"components": {
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
"extends": ["plugin:jsx-a11y/strict"],
|
"extends": ["plugin:jsx-a11y/strict"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@nrwl/nx/enforce-module-boundaries": [
|
"@nx/enforce-module-boundaries": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
"enforceBuildableLibDependency": true,
|
"enforceBuildableLibDependency": true,
|
||||||
@@ -51,12 +51,13 @@
|
|||||||
"ul": ["list"],
|
"ul": ["list"],
|
||||||
"ol": ["list"]
|
"ol": ["list"]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"no-console": ["error", { "allow": ["warn", "error"] }]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": ["*.ts", "*.tsx"],
|
"files": ["*.ts", "*.tsx"],
|
||||||
"extends": ["plugin:@nrwl/nx/typescript"],
|
"extends": ["plugin:@nx/typescript"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/ban-ts-comment": [
|
"@typescript-eslint/ban-ts-comment": [
|
||||||
"error",
|
"error",
|
||||||
@@ -80,14 +81,19 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": ["*.spec.ts", "*.spec.tsx"],
|
"files": ["*.spec.ts", "*.spec.tsx"],
|
||||||
"extends": ["plugin:@nrwl/nx/typescript", "plugin:jest/recommended"],
|
"extends": ["plugin:@nx/typescript", "plugin:jest/recommended"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"jest/consistent-test-it": ["error", { "fn": "it" }]
|
"jest/consistent-test-it": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"fn": "it"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": ["*.js", "*.jsx"],
|
"files": ["*.js", "*.jsx"],
|
||||||
"extends": ["plugin:@nrwl/nx/javascript"],
|
"extends": ["plugin:@nx/javascript"],
|
||||||
"rules": {}
|
"rules": {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ What we need to achieve and who for
|
|||||||
|
|
||||||
## Tasks
|
## Tasks
|
||||||
|
|
||||||
- [ ] What do we need to do first
|
- [ ]
|
||||||
- [ ] and then what?
|
- [ ]
|
||||||
- [ ] Etc.
|
|
||||||
|
|
||||||
## Additional details / background info
|
## Additional details / background info
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
name: Feature Epic
|
||||||
|
about: A template to capture and scope user requirements, high level process, and basic mockups for an upcoming feature as part of the initial core spec review process.
|
||||||
|
title: 'Epic: '
|
||||||
|
labels: feature-epic
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Feature
|
||||||
|
|
||||||
|
<Name>
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Define high level requirements
|
||||||
|
- [ ] Create basic mockups
|
||||||
|
- [ ] Update "API Requirements" in core spec
|
||||||
|
- [ ] Update "User-Interface Spec" in relevant front end repo
|
||||||
|
- [ ] Create detailed user stories using normal template
|
||||||
|
|
||||||
|
## High Level Requirements
|
||||||
|
|
||||||
|
## Basic Mockups
|
||||||
|
|
||||||
|
## Link to API Requirements in Core spec
|
||||||
|
|
||||||
|
## Link to User Interface Specs
|
||||||
|
|
||||||
|
## Linked User Stories
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
name: Release
|
||||||
|
about: A template to outline the steps needed to for a successful release of our frontend apps
|
||||||
|
title: 'Release [add dapp version]-core-[add core version]'
|
||||||
|
labels:
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
|
||||||
|
- [ ] Review [link to core release](xxx)
|
||||||
|
- [ ] Tag frontend-monorepo
|
||||||
|
- [ ] Create release and generate release notes
|
||||||
|
- [ ] Run `@smoke` tests
|
||||||
|
- [ ] Run `@regression` tests
|
||||||
|
- [ ] Run `@slow` tests
|
||||||
|
- [ ] Explorative testing of key flows
|
||||||
|
- [ ] Set `release/[network]` to tagged commit
|
||||||
|
- [ ] Verify builds (on Netlify and Fleek) are successful
|
||||||
|
- [ ] Verify build has been deployed
|
||||||
|
- [ ] Smoke testing on deployed app
|
||||||
@@ -22,11 +22,14 @@ So that
|
|||||||
|
|
||||||
## Tasks
|
## Tasks
|
||||||
|
|
||||||
- [ ] Explore and sketch
|
- [ ] UX (if needed)
|
||||||
|
- [ ] Design (if needed)
|
||||||
- [ ] Team and stakeholder review
|
- [ ] Team and stakeholder review
|
||||||
- [ ] Visual Design
|
- [ ] Specs reviewed and created or adjusted
|
||||||
- [ ] Team review
|
- [ ] Implementation
|
||||||
- [ ] Etc.
|
- [ ] Testing (unit and/or e2e)
|
||||||
|
- [ ] Code review
|
||||||
|
- [ ] QA review
|
||||||
|
|
||||||
## Sketch
|
## Sketch
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
name: After Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
after-release:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 45
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Log in to the Container registry (ghcr)
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Log in to the Container registry (docker hub)
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Wait for publish to complete
|
||||||
|
uses: lewagon/wait-on-check-action@v1.3.1
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.release.tag_name }}
|
||||||
|
check-name: '(CD) publish dist / trading'
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
wait-interval: 10
|
||||||
|
|
||||||
|
- name: resolve ipfs hashes for release
|
||||||
|
run: |
|
||||||
|
echo "Name: ${{ github.event.release.name }}"
|
||||||
|
echo "Description: ${{ github.event.release.body }}"
|
||||||
|
echo "Tag: ${{ github.event.release.tag_name }}"
|
||||||
|
docker run --rm vegaprotocol/trading:mainnet cat /ipfs-hash > ipfs-hash
|
||||||
|
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
||||||
|
tar -xzf kubo.tgz
|
||||||
|
export PATH="$PATH:$PWD/kubo"
|
||||||
|
which ipfs
|
||||||
|
echo IPFS_V0=$(cat ipfs-hash) >> $GITHUB_ENV
|
||||||
|
echo IPFS_V1=$(ipfs cid format -v 1 -b base32 $(cat ipfs-hash)) >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Edit Release
|
||||||
|
uses: irongut/EditRelease@v1.2.0
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
id: ${{ github.event.release.id }}
|
||||||
|
body: |
|
||||||
|
|
||||||
|
___
|
||||||
|
|
||||||
|
# Deployments
|
||||||
|
* https://explorer.vega.xyz
|
||||||
|
* https://governance.vega.xyz
|
||||||
|
|
||||||
|
# IPFS releases
|
||||||
|
The IPFS hash of this release of the Trading app is:
|
||||||
|
|
||||||
|
CIDv0: ${{ env.IPFS_V0 }}
|
||||||
|
CIDv1: ${{ env.IPFS_V1 }}
|
||||||
|
|
||||||
|
You can always access the latest IPFS release by visiting [console.vega.xyz](https://console.vega.xyz).
|
||||||
|
|
||||||
|
You can also access Trading directly from an IPFS gateway.
|
||||||
|
BEWARE: The Trading interface uses [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported. You should always use an IPFS gateway that enforces [origin separation](https://ipfs.github.io/public-gateway-checker/).
|
||||||
|
|
||||||
|
Your settings are not remembered across different URLs.
|
||||||
|
|
||||||
|
IPFS gateways:
|
||||||
|
|
||||||
|
https://${{ env.IPFS_V1 }}.ipfs.dweb.link/
|
||||||
|
https://${{ env.IPFS_V1 }}.ipfs.cf-ipfs.com/
|
||||||
|
ipfs://${{ env.IPFS_V0 }}/
|
||||||
@@ -6,14 +6,14 @@ name: 'Add Issues To Project Board'
|
|||||||
types:
|
types:
|
||||||
- opened
|
- opened
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_NEW_CARD_TO_PROJECT }}
|
GH_TOKEN: ${{ secrets.PROJECT_MANAGE_ACTION }}
|
||||||
PROJECT_ID: ${{ secrets.FRONT_END_PROJECT_ID }}
|
PROJECT_ID: ${{ secrets.FRONT_END_PROJECT_ID }}
|
||||||
ISSUE_ID: ${{ github.event.issue.node_id }}
|
ISSUE_ID: ${{ github.event.issue.node_id }}
|
||||||
USER: ${{ github.actor }}
|
USER: ${{ github.actor }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
add_issue:
|
add_issue:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: 'Add issue to project board'
|
- name: 'Add issue to project board'
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
name: 'Check if branch is shorter than 52 chars'
|
||||||
|
on: pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
branch-naming-rules:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
# echo "branches that are longer than 51 chars can't be parsed by kubernetes to create previews. Each app has prefix of it's name like: 'governance-' (12 chars), what leaves 51 max branch length"
|
||||||
|
# current parsable length: $( git rev-parse --abbrev-ref HEAD | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | wc -c)
|
||||||
|
- uses: deepakputhraya/action-branch-name@master
|
||||||
|
with:
|
||||||
|
# regex: '([a-z])+\/([a-z])+' # Regex the branch should match. This example enforces grouping
|
||||||
|
# allowed_prefixes: 'feature,stable,fix' # All branches should start with the given prefix
|
||||||
|
# ignore: master,develop # Ignore exactly matching branch names from convention
|
||||||
|
min_length: 1 # Min length of the branch name
|
||||||
|
max_length: 51 # Max length of the branch name
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
name: CI/CD
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- release/*
|
||||||
|
- develop
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- ready_for_review
|
||||||
|
- reopened
|
||||||
|
- edited
|
||||||
|
- synchronize
|
||||||
|
jobs:
|
||||||
|
node-modules:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
name: 'Cache yarn modules'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
|
- name: Cache node modules
|
||||||
|
id: cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||||
|
# comment out "restore-keys" if you need to rebuild yarn from 0
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cache-node-modules-
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
|
- name: yarn install
|
||||||
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
run: yarn install --pure-lockfile
|
||||||
|
|
||||||
|
lint-pr-title:
|
||||||
|
needs: node-modules
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
name: Verify PR title
|
||||||
|
uses: ./.github/workflows/lint-pr.yml
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
lint-test-build:
|
||||||
|
timeout-minutes: 60
|
||||||
|
needs: node-modules
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
name: '(CI) lint + unit test + build'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
|
- name: Cache node modules
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||||
|
|
||||||
|
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
||||||
|
uses: nrwl/nx-set-shas@v3
|
||||||
|
with:
|
||||||
|
main-branch-name: develop
|
||||||
|
|
||||||
|
# See affected apps
|
||||||
|
- name: See affected apps
|
||||||
|
run: |
|
||||||
|
branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
|
||||||
|
python3 tools/ci/check-affected.py --github-ref="${{ github.ref }}" --branch-slug="$branch_slug" --event-name="${{ github.event_name }}"
|
||||||
|
|
||||||
|
- name: Verify script result
|
||||||
|
run: |
|
||||||
|
echo "Check outputs from script"
|
||||||
|
echo "projects: ${{ env.PROJECTS }}"
|
||||||
|
echo "projects-e2e: ${{ env.PROJECTS_E2E }}"
|
||||||
|
echo "preview_governance: ${{ env.PREVIEW_GOVERNANCE }}"
|
||||||
|
echo "preview_trading: ${{ env.PREVIEW_TRADING }}"
|
||||||
|
echo "preview_explorer: ${{ env.PREVIEW_EXPLORER }}"
|
||||||
|
echo "preview_tools: ${{ env.PREVIEW_TOOLS }}"
|
||||||
|
|
||||||
|
- name: Check formatting
|
||||||
|
run: yarn nx format:check
|
||||||
|
|
||||||
|
- name: Lint affected
|
||||||
|
run: yarn nx affected:lint --max-warnings=0
|
||||||
|
|
||||||
|
- name: Build affected spec
|
||||||
|
run: yarn nx affected --target=build-spec
|
||||||
|
|
||||||
|
- name: Test affected
|
||||||
|
run: yarn nx affected:test
|
||||||
|
|
||||||
|
- name: Build affected
|
||||||
|
run: yarn nx affected:build || (yarn install && yarn nx affected:build)
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
projects: ${{ env.PROJECTS }}
|
||||||
|
projects-e2e: ${{ env.PROJECTS_E2E }}
|
||||||
|
preview_governance: ${{ env.PREVIEW_GOVERNANCE }}
|
||||||
|
preview_trading: ${{ env.PREVIEW_TRADING }}
|
||||||
|
preview_explorer: ${{ env.PREVIEW_EXPLORER }}
|
||||||
|
preview_tools: ${{ env.PREVIEW_TOOLS }}
|
||||||
|
|
||||||
|
console-e2e:
|
||||||
|
needs: lint-test-build
|
||||||
|
name: '(CI) console python'
|
||||||
|
uses: ./.github/workflows/console-test-run.yml
|
||||||
|
secrets: inherit
|
||||||
|
if: ${{ contains(fromJSON(needs.lint-test-build.outputs.projects), 'trading') && github.event_name == 'pull_request' }}
|
||||||
|
with:
|
||||||
|
github-sha: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
|
cypress:
|
||||||
|
needs: lint-test-build
|
||||||
|
name: '(CI) cypress'
|
||||||
|
# if: ${{ needs.lint-test-build.outputs.projects-e2e != '[]' }}
|
||||||
|
uses: ./.github/workflows/cypress-run.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
projects: ${{ needs.lint-test-build.outputs.projects-e2e }}
|
||||||
|
tags: '@smoke @regression'
|
||||||
|
|
||||||
|
publish-dist:
|
||||||
|
needs: lint-test-build
|
||||||
|
name: '(CD) publish dist'
|
||||||
|
if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'vegaprotocol/frontend-monorepo') || github.event_name == 'push' }}
|
||||||
|
uses: ./.github/workflows/publish-dist.yml
|
||||||
|
secrets: inherit
|
||||||
|
with:
|
||||||
|
projects: ${{ needs.lint-test-build.outputs.projects }}
|
||||||
|
|
||||||
|
dist-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- publish-dist
|
||||||
|
- lint-test-build
|
||||||
|
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'vegaprotocol/frontend-monorepo' }}
|
||||||
|
timeout-minutes: 60
|
||||||
|
name: '(CD) comment preview links'
|
||||||
|
steps:
|
||||||
|
- name: Find Comment
|
||||||
|
uses: peter-evans/find-comment@v2
|
||||||
|
id: fc
|
||||||
|
with:
|
||||||
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
|
body-includes: Previews
|
||||||
|
|
||||||
|
- name: Wait for deployments
|
||||||
|
run: |
|
||||||
|
# https://stackoverflow.com/questions/3183444/check-for-valid-link-url
|
||||||
|
regex='(https?|ftp|file)://[-[:alnum:]\+&@#/%?=~_|!:,.;]*[-[:alnum:]\+&@#/%=~_|]'
|
||||||
|
if [[ "${{ needs.lint-test-build.outputs.preview_governance }}" =~ $regex ]]; then
|
||||||
|
until curl --insecure --location --fail "${{ needs.lint-test-build.outputs.preview_governance }}"; do
|
||||||
|
echo "waiting for governance preview: ${{ needs.lint-test-build.outputs.preview_governance }}"
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [[ "${{ needs.lint-test-build.outputs.preview_explorer }}" =~ $regex ]]; then
|
||||||
|
until curl --insecure --location --fail "${{ needs.lint-test-build.outputs.preview_explorer }}"; do
|
||||||
|
echo "waiting for explorer preview: ${{ needs.lint-test-build.outputs.preview_explorer }}"
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [[ "${{ needs.lint-test-build.outputs.preview_trading }}" =~ $regex ]]; then
|
||||||
|
until curl --insecure --location --fail "${{ needs.lint-test-build.outputs.preview_trading }}"; do
|
||||||
|
echo "waiting for trading preview: ${{ needs.lint-test-build.outputs.preview_trading }}"
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [[ "${{ needs.lint-test-build.outputs.preview_tools }}" =~ $regex ]]; then
|
||||||
|
until curl --insecure --location --fail "${{ needs.lint-test-build.outputs.preview_tools }}"; do
|
||||||
|
echo "waiting for tools preview: ${{ needs.lint-test-build.outputs.preview_tools }}"
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create comment
|
||||||
|
uses: peter-evans/create-or-update-comment@v3
|
||||||
|
if: ${{ steps.fc.outputs.comment-id == 0 }}
|
||||||
|
with:
|
||||||
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
|
body: |
|
||||||
|
Previews
|
||||||
|
* governance: ${{ needs.lint-test-build.outputs.preview_governance }}
|
||||||
|
* explorer: ${{ needs.lint-test-build.outputs.preview_explorer }}
|
||||||
|
* trading: ${{ needs.lint-test-build.outputs.preview_trading }}
|
||||||
|
* tools: ${{ needs.lint-test-build.outputs.preview_tools }}
|
||||||
|
|
||||||
|
# Report single result at the end, to avoid mess with required checks in PR
|
||||||
|
cypress-check:
|
||||||
|
name: '(CI) cypress - check'
|
||||||
|
if: ${{ always() }}
|
||||||
|
needs: cypress
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
result="${{ needs.cypress.result }}"
|
||||||
|
if [[ $result == "success" || $result == "skipped" ]]; then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
name: (CI) Console tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
github-sha:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run-tests:
|
||||||
|
name: run-tests
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 25
|
||||||
|
steps:
|
||||||
|
#----------------------------------------------
|
||||||
|
# check-out frontend-monorepo
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Checkout console test repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ inputs.github-sha }}
|
||||||
|
#----------------------------------------------
|
||||||
|
# cache node modules
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Cache node modules
|
||||||
|
id: cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||||
|
# comment out "restore-keys" if you need to rebuild yarn from 0
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cache-node-modules-
|
||||||
|
#----------------------------------------------
|
||||||
|
# setup node
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||||
|
cache: yarn
|
||||||
|
#----------------------------------------------
|
||||||
|
# install deps if cache missing
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: yarn install
|
||||||
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
run: yarn install --pure-lockfile
|
||||||
|
#----------------------------------------------
|
||||||
|
# build trading
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Build affected spec
|
||||||
|
run: |
|
||||||
|
yarn env-cmd -f ./apps/trading/.env.stagnet1 yarn nx export trading
|
||||||
|
#----------------------------------------------
|
||||||
|
# run trading server
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Run trading server
|
||||||
|
run: |
|
||||||
|
docker run -d -p 80:4200 -v $PWD/docker/nginx.conf:/etc/nginx/conf.d/default.conf -v $PWD/dist/apps/trading/exported:/usr/share/nginx/html nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
|
||||||
|
sleep 5
|
||||||
|
docker ps
|
||||||
|
#----------------------------------------------
|
||||||
|
# check if container persists between runs
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Check server
|
||||||
|
run: |
|
||||||
|
docker ps
|
||||||
|
#----------------------------------------------
|
||||||
|
# check-out tests repo
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Checkout console test repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: vegaprotocol/console-test
|
||||||
|
path: './console-test'
|
||||||
|
#----------------------------------------------
|
||||||
|
# set-up python
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Set up python
|
||||||
|
id: setup-python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.10.11'
|
||||||
|
#----------------------------------------------
|
||||||
|
# ----- install & configure poetry -----
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Install Poetry
|
||||||
|
uses: snok/install-poetry@v1
|
||||||
|
with:
|
||||||
|
virtualenvs-create: true
|
||||||
|
virtualenvs-in-project: true
|
||||||
|
virtualenvs-path: console-test/.venv
|
||||||
|
#----------------------------------------------
|
||||||
|
# load cached venv if cache exists
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Load cached venv
|
||||||
|
id: cached-poetry-dependencies
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: console-test/.venv
|
||||||
|
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
|
||||||
|
#----------------------------------------------
|
||||||
|
# install dependencies if cache does not exist
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Install dependencies
|
||||||
|
working-directory: ./console-test
|
||||||
|
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
|
||||||
|
run: poetry install --no-interaction --no-root
|
||||||
|
#----------------------------------------------
|
||||||
|
# install vega binaries
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Install vega binaries
|
||||||
|
working-directory: ./console-test
|
||||||
|
run: poetry run python -m vega_sim.tools.load_binaries --force
|
||||||
|
#----------------------------------------------
|
||||||
|
# install playwright
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: install playwright
|
||||||
|
run: poetry run playwright install
|
||||||
|
working-directory: ./console-test
|
||||||
|
#----------------------------------------------
|
||||||
|
# run tests
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Run tests
|
||||||
|
working-directory: ./console-test
|
||||||
|
run: poetry run pytest --numprocesses auto
|
||||||
|
- name: Check files
|
||||||
|
run: |
|
||||||
|
ls -al .
|
||||||
|
ls -al console-test
|
||||||
|
#----------------------------------------------
|
||||||
|
# upload traces
|
||||||
|
#----------------------------------------------
|
||||||
|
- name: Upload Playwright Trace
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: playwright-trace
|
||||||
|
path: ./traces/
|
||||||
|
retention-days: 15
|
||||||
@@ -13,11 +13,17 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
cypress-run:
|
cypress-run:
|
||||||
name: Run Cypress Trading tests -- live environment
|
name: Run Cypress Trading tests -- live environment
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Use Node.js 16
|
||||||
|
id: Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16.15.1
|
||||||
|
|
||||||
- name: Run Cypress tests
|
- name: Run Cypress tests
|
||||||
uses: cypress-io/github-action@v4
|
uses: cypress-io/github-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -27,5 +33,4 @@ jobs:
|
|||||||
config: baseUrl=${{ github.event.inputs.url }}
|
config: baseUrl=${{ github.event.inputs.url }}
|
||||||
env: grepTags=@live
|
env: grepTags=@live
|
||||||
env:
|
env:
|
||||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ on:
|
|||||||
type: choice
|
type: choice
|
||||||
options:
|
options:
|
||||||
- explorer-e2e
|
- explorer-e2e
|
||||||
- token-e2e
|
- governance-e2e
|
||||||
- trading-e2e
|
- trading-e2e
|
||||||
tags:
|
tags:
|
||||||
description: 'Test tags to run'
|
description: 'Test tags to run'
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ jobs:
|
|||||||
uses: ./.github/workflows/cypress-run.yml
|
uses: ./.github/workflows/cypress-run.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
projects: '["explorer-e2e","token-e2e","trading-e2e"]'
|
projects: '["explorer-e2e","governance-e2e","trading-e2e"]'
|
||||||
tags: '@smoke @regression @slow'
|
tags: '@smoke @regression @slow'
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
name: Cypress tests - PR
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- develop
|
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
types:
|
|
||||||
- opened
|
|
||||||
- reopened
|
|
||||||
- synchronize
|
|
||||||
- ready_for_review
|
|
||||||
jobs:
|
|
||||||
pr:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout frontend mono repo
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup node
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
|
|
||||||
- name: Remove package.json & yarn.lock to avoid installing everything
|
|
||||||
run: rm package.json yarn.lock
|
|
||||||
|
|
||||||
- name: Install nx
|
|
||||||
run: yarn add nx
|
|
||||||
|
|
||||||
# Check SHAs
|
|
||||||
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
|
||||||
uses: nrwl/nx-set-shas@v2
|
|
||||||
with:
|
|
||||||
main-branch-name: ${{ github.base_ref || github.ref_name }}
|
|
||||||
set-environment-variables-for-job: true
|
|
||||||
|
|
||||||
# See affected apps
|
|
||||||
- name: See affected apps
|
|
||||||
run: |
|
|
||||||
affected=$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)
|
|
||||||
echo -n "Affected projects: $affected"
|
|
||||||
projects=""
|
|
||||||
if [[ $affected == *"token"* ]]; then projects+='"token-e2e" '; fi
|
|
||||||
if [[ $affected == *"trading"* ]]; then projects+='"trading-e2e" '; fi
|
|
||||||
if [[ $affected == *"explorer"* ]]; then projects+='"explorer-e2e" '; fi
|
|
||||||
if [[ -z "$projects" ]]; then projects+='"token-e2e" "trading-e2e" "explorer-e2e" '; fi
|
|
||||||
projects=${projects%?}
|
|
||||||
projects=[${projects// /,}]
|
|
||||||
echo PROJECTS=$projects >> $GITHUB_ENV
|
|
||||||
|
|
||||||
outputs:
|
|
||||||
projects: ${{ env.PROJECTS }}
|
|
||||||
|
|
||||||
run:
|
|
||||||
needs: pr
|
|
||||||
if: ${{ needs.pr.outputs.projects != '[]' }}
|
|
||||||
uses: ./.github/workflows/cypress-run.yml
|
|
||||||
secrets: inherit
|
|
||||||
with:
|
|
||||||
projects: ${{ needs.pr.outputs.projects }}
|
|
||||||
tags: '@smoke @regression'
|
|
||||||
|
|
||||||
# Report single result at the end, to avoid mess with required checks in PR
|
|
||||||
result:
|
|
||||||
if: ${{ always() }}
|
|
||||||
needs: run
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Cypress result
|
|
||||||
steps:
|
|
||||||
- run: |
|
|
||||||
result="${{ needs.run.result }}"
|
|
||||||
if [[ $result == "success" || $result == "skipped" ]]; then
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
name: (CI) Cypress Run
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
inputs:
|
||||||
@@ -17,8 +18,9 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
project: ${{ fromJSON(inputs.projects) }}
|
project: ${{ fromJSON(inputs.projects) }}
|
||||||
|
name: ${{ matrix.project }}
|
||||||
runs-on: self-hosted-runner
|
runs-on: self-hosted-runner
|
||||||
timeout-minutes: 30
|
timeout-minutes: 100
|
||||||
steps:
|
steps:
|
||||||
# Checks if skip cache was requested
|
# Checks if skip cache was requested
|
||||||
- name: Set skip-nx-cache flag
|
- name: Set skip-nx-cache flag
|
||||||
@@ -31,6 +33,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
path: './frontend-monorepo'
|
path: './frontend-monorepo'
|
||||||
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
# Restore node_modules from cache if possible
|
# Restore node_modules from cache if possible
|
||||||
- name: Restore node_modules from cache
|
- name: Restore node_modules from cache
|
||||||
@@ -64,12 +67,11 @@ jobs:
|
|||||||
######
|
######
|
||||||
|
|
||||||
- name: Run Cypress tests
|
- name: Run Cypress tests
|
||||||
run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
|
run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --browser chrome --env.grepTags="${{ inputs.tags }}"
|
||||||
working-directory: frontend-monorepo
|
working-directory: frontend-monorepo
|
||||||
env:
|
env:
|
||||||
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
|
||||||
CYPRESS_VEGA_WALLET_API_TOKEN: ${{ steps.setup-vega.outputs.token }}
|
CYPRESS_VEGA_WALLET_API_TOKEN: ${{ steps.setup-vega.outputs.token }}
|
||||||
CYPRESS_grepTags: ${{ inputs.tags }}
|
|
||||||
|
|
||||||
######
|
######
|
||||||
## Upload logs
|
## Upload logs
|
||||||
@@ -83,8 +85,18 @@ jobs:
|
|||||||
mv "${file}" "$(echo ${file} | sed 's|:|-|g')"
|
mv "${file}" "$(echo ${file} | sed 's|:|-|g')"
|
||||||
done< <(find /home/runner/.vegacapsule/testnet/logs -type f)
|
done< <(find /home/runner/.vegacapsule/testnet/logs -type f)
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- name: Print logs files
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
|
run: ls -alsh /home/runner/.vegacapsule/testnet/logs/
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
if: ${{ failure() }}
|
||||||
with:
|
with:
|
||||||
name: logs-${{ matrix.project }}
|
name: logs-${{ matrix.project }}
|
||||||
path: /home/runner/.vegacapsule/testnet/logs
|
path: /home/runner/.vegacapsule/testnet/logs
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
if: ${{ failure() }}
|
||||||
|
with:
|
||||||
|
name: test-report-${{ matrix.project }}
|
||||||
|
path: frontend-monorepo/apps/${{ matrix.project }}/cypress/reports
|
||||||
|
|||||||
@@ -8,22 +8,25 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
master:
|
master:
|
||||||
name: Generate Queries
|
name: Generate Queries
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
node-version-file: '.nvmrc'
|
||||||
- name: Use Node.js 16
|
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||||
id: Node
|
cache: yarn
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: 16.14.0
|
|
||||||
- name: Install root dependencies
|
- name: Install root dependencies
|
||||||
run: yarn install
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
- name: Generate queries
|
- name: Generate queries
|
||||||
run: node ./scripts/get-queries.js
|
run: node ./scripts/get-queries.js
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: queries
|
name: queries
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
name: Verify PR title
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint_pr:
|
||||||
|
timeout-minutes: 10
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
|
- name: Cache node modules
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||||
|
|
||||||
|
- name: Check PR title
|
||||||
|
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
---
|
|
||||||
name: Verify PR title
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, ready_for_review, reopened, edited, synchronize]
|
|
||||||
jobs:
|
|
||||||
lint_pr:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Use Node.js 16
|
|
||||||
id: Node
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: 16.14.0
|
|
||||||
- name: Install commitlint cli and config
|
|
||||||
run: npm install @commitlint/cli @commitlint/config-conventional
|
|
||||||
- name: Create config
|
|
||||||
run: echo "module.exports = {extends:['@commitlint/config-conventional']};" > commitlint.config.js
|
|
||||||
- name: Check PR title
|
|
||||||
run: echo "${{ github.event.pull_request.title }}" | npx commitlint
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
name: Generate tranches
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 */6 * * *'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
master:
|
|
||||||
name: Generate Queries
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Use Node.js 16
|
|
||||||
id: Node
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: 16.14.0
|
|
||||||
- name: Install root dependencies
|
|
||||||
run: yarn install
|
|
||||||
- name: Generate queries
|
|
||||||
run: node ./scripts/get-tranches.js
|
|
||||||
- uses: stefanzweifel/git-auto-commit-action@v4
|
|
||||||
with:
|
|
||||||
commit_message: 'chore: update tranches'
|
|
||||||
commit_options: '--no-verify --signoff'
|
|
||||||
skip_fetch: true
|
|
||||||
skip_checkout: true
|
|
||||||
@@ -0,0 +1,329 @@
|
|||||||
|
name: (CD) Publish docker + s3
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
projects:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish-dist:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
app: ${{ fromJSON(inputs.projects) }}
|
||||||
|
name: ${{ matrix.app }}
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 25
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
|
- name: Init variables
|
||||||
|
run: |
|
||||||
|
echo IS_PR=false >> $GITHUB_ENV
|
||||||
|
echo IS_MAINNET_RELEASE=false >> $GITHUB_ENV
|
||||||
|
echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV
|
||||||
|
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
|
||||||
|
echo IS_S3_RELEASE=false >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Is PR
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
run: |
|
||||||
|
echo IS_PR=true >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Is mainnet release
|
||||||
|
if: ${{ contains(github.ref, 'release/mainnet') && !contains(github.ref, 'mirror') }}
|
||||||
|
run: |
|
||||||
|
echo IS_MAINNET_RELEASE=true >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Is testnet release
|
||||||
|
if: ${{ contains(github.ref, 'release/testnet') }}
|
||||||
|
run: |
|
||||||
|
echo IS_TESTNET_RELEASE=true >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Is IPFS Release
|
||||||
|
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( env.IS_MAINNET_RELEASE == 'true' || env.IS_TESTNET_RELEASE == 'true' ) }}
|
||||||
|
run: |
|
||||||
|
echo IS_IPFS_RELEASE=true >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Is S3 Release
|
||||||
|
if: ${{ env.IS_IPFS_RELEASE == 'false' && github.event_name == 'push' }}
|
||||||
|
run: |
|
||||||
|
echo IS_S3_RELEASE=true >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
id: quemu
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Available platforms
|
||||||
|
run: echo ${{ steps.qemu.outputs.platforms }}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Log in to the Container registry (ghcr)
|
||||||
|
if: ${{ env.IS_PR == 'true' }}
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Log in to the Container registry (docker hub)
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
|
- name: Cache node modules
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules
|
||||||
|
key: ${{ runner.os }}-cache-node-modules-${{ hashFiles('yarn.lock') }}
|
||||||
|
|
||||||
|
# https://docs.github.com/en/actions/learn-github-actions/contexts
|
||||||
|
- name: Define dist variables
|
||||||
|
if: ${{ github.event_name == 'push' }}
|
||||||
|
run: |
|
||||||
|
python3 tools/ci/define-dist-variables.py --github-ref="${{ github.ref }}" --app="${{ matrix.app }}"
|
||||||
|
|
||||||
|
- name: Verify script result
|
||||||
|
if: ${{ github.event_name == 'push' }}
|
||||||
|
run: |
|
||||||
|
echo "BUCKET_NAME=${{ env.BUCKET_NAME }}"
|
||||||
|
echo "ENV_NAME=${{ env.ENV_NAME }}"
|
||||||
|
|
||||||
|
- name: Build local dist
|
||||||
|
run: |
|
||||||
|
envCmd=""
|
||||||
|
if [[ ! -z "${{ env.ENV_NAME }}" ]]; then
|
||||||
|
envCmd="yarn env-cmd -f ./apps/${{ matrix.app }}/.env.${{ env.ENV_NAME }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${{ matrix.app }}" = "trading" ]; then
|
||||||
|
$envCmd yarn nx export trading || (yarn install && $envCmd yarn nx export trading)
|
||||||
|
DIST_LOCATION=dist/apps/trading/exported
|
||||||
|
elif [ "${{ matrix.app }}" = "ui-toolkit" ]; then
|
||||||
|
NODE_ENV=production yarn nx run ui-toolkit:build-storybook
|
||||||
|
DIST_LOCATION=dist/storybook/ui-toolkit
|
||||||
|
elif [ "${{ matrix.app }}" = "static" ]; then
|
||||||
|
yarn nx build static || (yarn install && yarn nx build static)
|
||||||
|
else
|
||||||
|
$envCmd yarn nx build ${{ matrix.app }} || (yarn install && $envCmd yarn nx build ${{ matrix.app }})
|
||||||
|
fi
|
||||||
|
if [[ -z "$DIST_LOCATION" ]]; then
|
||||||
|
DIST_LOCATION=dist/apps/${{ matrix.app }}
|
||||||
|
fi
|
||||||
|
mv $DIST_LOCATION dist-result
|
||||||
|
tree dist-result
|
||||||
|
|
||||||
|
- name: Build and export to local Docker
|
||||||
|
id: docker_build
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: docker/node-outside-docker.Dockerfile
|
||||||
|
load: true
|
||||||
|
build-args: |
|
||||||
|
APP=${{ matrix.app }}
|
||||||
|
ENV_NAME=${{ env.ENV_NAME }}
|
||||||
|
tags: |
|
||||||
|
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
|
||||||
|
|
||||||
|
- name: Image digest
|
||||||
|
if: ${{ env.IS_PR == 'true' }}
|
||||||
|
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||||
|
|
||||||
|
- name: Sanity check docker image
|
||||||
|
run: |
|
||||||
|
echo "Check ipfs-hash"
|
||||||
|
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
|
||||||
|
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash > ${{ matrix.app }}-ipfs-hash
|
||||||
|
echo "List html directory"
|
||||||
|
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree /usr/share/nginx/html'
|
||||||
|
|
||||||
|
- name: Publish dist as docker image (ghcr)
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
continue-on-error: true
|
||||||
|
id: ghcr-push
|
||||||
|
if: ${{ env.IS_PR == 'true' }}
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: docker/node-outside-docker.Dockerfile
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
APP=${{ matrix.app }}
|
||||||
|
ENV_NAME=${{ env.ENV_NAME }}
|
||||||
|
tags: |
|
||||||
|
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
|
- name: Publish dist as docker image (docker hub)
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
continue-on-error: true
|
||||||
|
id: dockerhub-push
|
||||||
|
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: docker/node-outside-docker.Dockerfile
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
APP=${{ matrix.app }}
|
||||||
|
ENV_NAME=${{ env.ENV_NAME }}
|
||||||
|
tags: |
|
||||||
|
vegaprotocol/${{ matrix.app }}:${{ github.sha }}
|
||||||
|
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }}
|
||||||
|
|
||||||
|
- name: Publish dist as docker image (ghcr - retry)
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
if: ${{ steps.ghcr-push.outcome == 'failure' }}
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: docker/node-outside-docker.Dockerfile
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
APP=${{ matrix.app }}
|
||||||
|
ENV_NAME=${{ env.ENV_NAME }}
|
||||||
|
tags: |
|
||||||
|
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
|
- name: Publish dist as docker image (docker hub - retry)
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
if: ${{ steps.dockerhub-push.outcome == 'failure' }}
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: docker/node-outside-docker.Dockerfile
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
APP=${{ matrix.app }}
|
||||||
|
ENV_NAME=${{ env.ENV_NAME }}
|
||||||
|
tags: |
|
||||||
|
vegaprotocol/${{ matrix.app }}:${{ github.sha }}
|
||||||
|
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }}
|
||||||
|
|
||||||
|
# bucket creation in github.com/vegaprotocol/terraform//frontend
|
||||||
|
- name: Publish dist to s3
|
||||||
|
uses: jakejarvis/s3-sync-action@master
|
||||||
|
# s3 releases are not happening for trading on mainnet - it's IPFS
|
||||||
|
if: ${{ env.IS_S3_RELEASE == 'true' }}
|
||||||
|
with:
|
||||||
|
args: --acl private --follow-symlinks --delete
|
||||||
|
env:
|
||||||
|
AWS_S3_BUCKET: ${{ env.BUCKET_NAME }}
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
AWS_REGION: 'eu-west-1'
|
||||||
|
SOURCE_DIR: 'dist-result'
|
||||||
|
|
||||||
|
- name: Install aws CLI
|
||||||
|
if: ${{ env.IS_S3_RELEASE == 'true' }}
|
||||||
|
uses: unfor19/install-aws-cli-action@master
|
||||||
|
|
||||||
|
- name: Perform cache invalidation
|
||||||
|
if: ${{ env.IS_S3_RELEASE == 'true' }}
|
||||||
|
env:
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
AWS_REGION: 'eu-west-1'
|
||||||
|
run: |
|
||||||
|
echo "Looking for distribution for bucket: ${{ env.BUCKET_NAME }}"
|
||||||
|
id=$(aws cloudfront list-distributions | jq -Mrc '.DistributionList.Items | .[] | select(.DefaultCacheBehavior.TargetOriginId == "${{ env.BUCKET_NAME }}") | .Id')
|
||||||
|
echo "Found id is: ${id}"
|
||||||
|
aws cloudfront create-invalidation --distribution-id $id --paths "/*"
|
||||||
|
|
||||||
|
- name: Add preview label
|
||||||
|
uses: actions-ecosystem/action-add-labels@v1
|
||||||
|
if: ${{ env.IS_PR == 'true' }}
|
||||||
|
with:
|
||||||
|
labels: ${{ matrix.app }}-preview
|
||||||
|
number: ${{ github.event.number }}
|
||||||
|
|
||||||
|
- name: Trigger fleek deployment
|
||||||
|
# release to ipfs happens only on mainnet (represented by main branch) for trading
|
||||||
|
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
|
||||||
|
run: |
|
||||||
|
if [[ "${{ env.IS_MAINNET_RELEASE }}" = "true" ]]; then
|
||||||
|
# display info about app
|
||||||
|
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"query": "query{getSiteById(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id latestDeploy{id status}}}"}' \
|
||||||
|
https://api.fleek.co/graphql
|
||||||
|
|
||||||
|
# trigger new deployment as base image is always set to vegaprotocol/trading:mainnet
|
||||||
|
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
|
||||||
|
https://api.fleek.co/graphql
|
||||||
|
|
||||||
|
elif [[ "${{ env.IS_TESTNET_RELEASE }}" = "true" ]]; then
|
||||||
|
# display info about app
|
||||||
|
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"query": "query{getSiteById(siteId:\"79baaeca-1952-4ae7-a256-f668cfc1d68e\"){id latestDeploy{id status}}}"}' \
|
||||||
|
https://api.fleek.co/graphql
|
||||||
|
|
||||||
|
# trigger new deployment as base image is always set to vegaprotocol/trading:mainnet
|
||||||
|
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"query": "mutation{triggerDeploy(siteId:\"79baaeca-1952-4ae7-a256-f668cfc1d68e\"){id status}}"}' \
|
||||||
|
https://api.fleek.co/graphql
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Check out ipfs-redirect
|
||||||
|
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: 'vegaprotocol/ipfs-redirect'
|
||||||
|
path: 'ipfs-redirect'
|
||||||
|
fetch-depth: '0'
|
||||||
|
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Update interstitial page to point to the new console
|
||||||
|
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
# set CID
|
||||||
|
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
||||||
|
tar -xzf kubo.tgz
|
||||||
|
export PATH="$PATH:$PWD/kubo"
|
||||||
|
which ipfs
|
||||||
|
new_hash=$(cat ${{ matrix.app }}-ipfs-hash)
|
||||||
|
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
|
||||||
|
|
||||||
|
(
|
||||||
|
cd ipfs-redirect
|
||||||
|
|
||||||
|
# configure git
|
||||||
|
git status
|
||||||
|
cat .git/config
|
||||||
|
git config --global user.email "vega-ci-bot@vega.xyz"
|
||||||
|
git config --global user.name "vega-ci-bot"
|
||||||
|
|
||||||
|
# update CID files
|
||||||
|
if [[ "${{ env.IS_MAINNET_RELEASE }}" = "true" ]]; then
|
||||||
|
echo $new_hash > cidv0-mainnet.txt
|
||||||
|
echo $new_cid > cidv1-mainnet.txt
|
||||||
|
git add cidv0-mainnet.txt cidv1-mainnet.txt
|
||||||
|
elif [[ "${{ env.IS_TESTNET_RELEASE }}" = "true" ]]; then
|
||||||
|
echo $new_hash > cidv0-fairground.txt
|
||||||
|
echo $new_cid > cidv1-fairground.txt
|
||||||
|
git add cidv0-fairground.txt cidv1-fairground.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
# create commit
|
||||||
|
commit_msg="Automated hash update from ${{ github.ref }}"
|
||||||
|
git commit -m "$commit_msg"
|
||||||
|
git push -u origin "main"
|
||||||
|
)
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
name: Publish docker containers
|
|
||||||
|
|
||||||
'on':
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
|
||||||
- 'v[0-9]+.[0-9]+.[0-9]+-*'
|
|
||||||
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
publish:
|
|
||||||
description: 'Publish tag to Docker Hub & GitHub Registry'
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
tag:
|
|
||||||
description: 'Git Tag to build and publish'
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: ''
|
|
||||||
apps:
|
|
||||||
description: 'Applications to build and publish'
|
|
||||||
required: false
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- '["explorer", "token", "trading"]'
|
|
||||||
- '["explorer"]'
|
|
||||||
- '["token"]'
|
|
||||||
- '["trading"]'
|
|
||||||
archs:
|
|
||||||
description: 'Architecture to build and publish'
|
|
||||||
required: false
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- linux/amd64, linux/arm64
|
|
||||||
- linux/amd64
|
|
||||||
- linux/arm64
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
master:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
app: ${{ fromJson(inputs.apps || '["explorer", "token", "trading"]') }}
|
|
||||||
name: Build the ${{ matrix.app }} image
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
ref: ${{ inputs.tag }}
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
|
||||||
id: quemu
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
|
|
||||||
- name: Available platforms
|
|
||||||
run: echo ${{ steps.qemu.outputs.platforms }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
|
||||||
if: ${{ inputs.publish || startsWith(github.ref, 'refs/tags/') }}
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Determine Docker Image tag
|
|
||||||
id: tags
|
|
||||||
run: |
|
|
||||||
hash=$(git rev-parse HEAD|cut -b1-8)
|
|
||||||
versionTag=${{ inputs.tag || startsWith(github.ref, 'refs/tags/') && github.ref_name || '${hash}' }}
|
|
||||||
echo ::set-output name=version::${versionTag}
|
|
||||||
echo ::set-output name=npmVersion::$(cat dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }} | grep FROM | head -n 1 | awk '{print $2}' | cut -d ':' -f 2 | cut -d '-' -f 1 )
|
|
||||||
|
|
||||||
- name: Print config
|
|
||||||
run: |
|
|
||||||
git rev-parse --verify HEAD
|
|
||||||
git status
|
|
||||||
echo "inputs.tag=${{ inputs.tag }}"
|
|
||||||
echo "inputs.publish=${{ inputs.publish }}"
|
|
||||||
echo "inputs.apps=${{ inputs.apps }}"
|
|
||||||
echo "inputs.archs=${{ inputs.archs }}"
|
|
||||||
echo "steps.tags.outputs.version=${{ steps.tags.outputs.version }}"
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: ${{ steps.tags.outputs.npmVersion }}
|
|
||||||
|
|
||||||
- name: Build frontend dists
|
|
||||||
run: |
|
|
||||||
yarn --verbose --pure-lockfile
|
|
||||||
yarn nx ${{ matrix.app =='trading' && 'export' || 'build' }} ${{ matrix.app }} --pure-lockfile
|
|
||||||
|
|
||||||
- name: Build and export to local Docker
|
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: false
|
|
||||||
file: dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }}.dist
|
|
||||||
build-args: APP=${{ matrix.app }}
|
|
||||||
load: true
|
|
||||||
tags: vegaprotocol/${{ matrix.app }}:local
|
|
||||||
|
|
||||||
- name: Sanity check docker image
|
|
||||||
run: |
|
|
||||||
docker run --rm vegaprotocol/${{ matrix.app }}:local cat .env
|
|
||||||
docker run --rm vegaprotocol/${{ matrix.app }}:local ls -lah
|
|
||||||
|
|
||||||
- name: Build and push to DockerHub
|
|
||||||
id: docker_build
|
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: ${{ inputs.publish || startsWith(github.ref, 'refs/tags/') }}
|
|
||||||
file: dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }}.dist
|
|
||||||
build-args: APP=${{ matrix.app }}
|
|
||||||
platforms: ${{ inputs.archs || 'linux/amd64, linux/arm64' }}
|
|
||||||
tags: |
|
|
||||||
vegaprotocol/${{ matrix.app }}:latest
|
|
||||||
vegaprotocol/${{ matrix.app }}:${{ steps.tags.outputs.version }}
|
|
||||||
|
|
||||||
- name: Image digest
|
|
||||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
name: Publish libs to npm
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
project:
|
||||||
|
description: 'Monorepo project to publish'
|
||||||
|
required: true
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- announcements
|
||||||
|
- ui-toolkit
|
||||||
|
- react-helpers
|
||||||
|
- tailwindcss-config
|
||||||
|
- types
|
||||||
|
- utils
|
||||||
|
- i18n
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
name: Build & Publish - Tag
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
permissions:
|
||||||
|
contents: 'read'
|
||||||
|
actions: 'read'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
|
- name: Install root dependencies
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build project
|
||||||
|
run: yarn nx build ${{inputs.project}}
|
||||||
|
|
||||||
|
- name: Publish project to @vegaprotocol
|
||||||
|
uses: JS-DevTools/npm-publish@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.NPM_TOKEN }}
|
||||||
|
package: dist/libs/${{inputs.project}}/package.json
|
||||||
|
access: public
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
name: 'Rollback console'
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Version that should be set on rollback'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
rollback:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Log in to the Container registry (docker hub)
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Retag mainnet
|
||||||
|
run: |
|
||||||
|
docker pull vegaprotocol/trading:${{ inputs.version }}
|
||||||
|
docker tag vegaprotocol/trading:${{ inputs.version }} vegaprotocol/trading:mainnet
|
||||||
|
docker push vegaprotocol/trading:mainnet
|
||||||
|
docker run --rm vegaprotocol/trading:mainnet cat /ipfs-hash > ipfs-hash
|
||||||
|
|
||||||
|
- name: Trigger fleek deployment
|
||||||
|
run: |
|
||||||
|
# display info about app
|
||||||
|
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"query": "query{getSiteById(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id latestDeploy{id status}}}"}' \
|
||||||
|
https://api.fleek.co/graphql
|
||||||
|
|
||||||
|
# trigger new deployment as base image is always set to vegaprotocol/trading:mainnet
|
||||||
|
curl -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
|
||||||
|
https://api.fleek.co/graphql
|
||||||
|
|
||||||
|
- name: Check out ipfs-redirect
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: 'vegaprotocol/ipfs-redirect'
|
||||||
|
path: 'ipfs-redirect'
|
||||||
|
fetch-depth: '0'
|
||||||
|
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Update console.vega.xyz DNS to redirect to the new console
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
||||||
|
tar -xzf kubo.tgz
|
||||||
|
export PATH="$PATH:$PWD/kubo"
|
||||||
|
which ipfs
|
||||||
|
new_hash=$(cat ipfs-hash)
|
||||||
|
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
|
||||||
|
git config --global user.email "vega-ci-bot@vega.xyz"
|
||||||
|
git config --global user.name "vega-ci-bot"
|
||||||
|
echo $new_hash > ipfs-redirect/cidv0.txt
|
||||||
|
echo $new_cid > ipfs-redirect/cidv1.txt
|
||||||
|
|
||||||
|
(
|
||||||
|
cd ipfs-redirect
|
||||||
|
|
||||||
|
git status
|
||||||
|
branch_name="rollback-to-$new_hash"
|
||||||
|
git checkout -b "$branch_name"
|
||||||
|
commit_msg="hash rollback to $new_hash"
|
||||||
|
git add cidv0.txt cidv1.txt
|
||||||
|
git commit -m "$commit_msg"
|
||||||
|
git push -u origin "$branch_name" --force-with-lease
|
||||||
|
pr_url="$(gh pr create --title "${commit_msg}" --body 'automated pull request to update CIDs')"
|
||||||
|
echo $pr_url
|
||||||
|
# once auto merge get's enabled on documentation repo let's do follow up
|
||||||
|
sleep 5
|
||||||
|
gh pr merge "${pr_url}" --delete-branch --squash --admin
|
||||||
|
)
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
name: Unit tests & build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- develop
|
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
jobs:
|
|
||||||
pr:
|
|
||||||
name: Test and lint - PR
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: 'read'
|
|
||||||
actions: 'read'
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Derive appropriate SHAs for base and head for `nx affected` commands
|
|
||||||
uses: nrwl/nx-set-shas@v2
|
|
||||||
with:
|
|
||||||
main-branch-name: ${{ github.base_ref }}
|
|
||||||
- name: Use Node.js 16
|
|
||||||
id: Node
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 16.14.0
|
|
||||||
- name: Restore node_modules from cache
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: '**/node_modules'
|
|
||||||
key: node_modules-${{ hashFiles('**/yarn.lock') }}
|
|
||||||
- name: Install root dependencies
|
|
||||||
run: yarn install --frozen-lockfile
|
|
||||||
- name: Check formatting
|
|
||||||
run: yarn nx format:check
|
|
||||||
- name: Lint affected
|
|
||||||
run: yarn nx affected:lint --max-warnings=0
|
|
||||||
- name: Test affected
|
|
||||||
run: yarn nx affected:test
|
|
||||||
- name: Build affected
|
|
||||||
run: yarn nx affected:build
|
|
||||||
- name: Build affected spec
|
|
||||||
run: yarn nx affected --target=build-spec
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
# compiled output
|
# compiled output
|
||||||
/dist
|
/dist
|
||||||
|
/dist-result
|
||||||
/tmp
|
/tmp
|
||||||
/out-tsc
|
/out-tsc
|
||||||
/tools/executors/**/*.js
|
/tools/executors/**/*.js
|
||||||
@@ -46,3 +47,8 @@ cypress.env.json
|
|||||||
|
|
||||||
# Next.js
|
# Next.js
|
||||||
.next
|
.next
|
||||||
|
|
||||||
|
#cypress
|
||||||
|
/apps/**/cypress/reports/
|
||||||
|
/apps/**/cypress/downloads/
|
||||||
|
/apps/**/fixtures/wallet/node**
|
||||||
|
|||||||
@@ -7,5 +7,4 @@ __generated___
|
|||||||
|
|
||||||
apps/static/src/assets/devnet-tranches.json
|
apps/static/src/assets/devnet-tranches.json
|
||||||
apps/static/src/assets/mainnet-tranches.json
|
apps/static/src/assets/mainnet-tranches.json
|
||||||
apps/static/src/assets/stagnet3-tranches.json
|
|
||||||
apps/static/src/assets/testnet-tranches.json
|
apps/static/src/assets/testnet-tranches.json
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
stories: [],
|
|
||||||
addons: [
|
|
||||||
'@storybook/addon-actions',
|
|
||||||
'@storybook/addon-viewport',
|
|
||||||
{
|
|
||||||
name: '@storybook/addon-docs',
|
|
||||||
options: {
|
|
||||||
configureJSX: true,
|
|
||||||
babelOptions: {},
|
|
||||||
sourceLoaderOptions: null,
|
|
||||||
transcludeMarkdown: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'@storybook/addon-controls',
|
|
||||||
'@storybook/addon-backgrounds',
|
|
||||||
'@storybook/addon-toolbars',
|
|
||||||
'@storybook/addon-measure',
|
|
||||||
'@storybook/addon-outline',
|
|
||||||
'@storybook/addon-a11y',
|
|
||||||
],
|
|
||||||
// uncomment the property below if you want to apply some webpack config globally
|
|
||||||
// webpackFinal: async (config, { configType }) => {
|
|
||||||
// // Make whatever fine-grained changes you need that should apply to all storybook configs
|
|
||||||
|
|
||||||
// // Return the altered config
|
|
||||||
// return config;
|
|
||||||
// },
|
|
||||||
};
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../tsconfig.base.json",
|
|
||||||
"exclude": [
|
|
||||||
"../**/*.spec.js",
|
|
||||||
"../**/*.test.js",
|
|
||||||
"../**/*.spec.ts",
|
|
||||||
"../**/*.test.ts",
|
|
||||||
"../**/*.spec.tsx",
|
|
||||||
"../**/*.test.tsx",
|
|
||||||
"../**/*.spec.jsx",
|
|
||||||
"../**/*.test.jsx"
|
|
||||||
],
|
|
||||||
"include": ["../**/*"]
|
|
||||||
}
|
|
||||||
Vendored
+1
-19
@@ -1,20 +1,2 @@
|
|||||||
@Library('vega-shared-library') _
|
@Library('vega-shared-library') _
|
||||||
|
runApprobation ignoreFailure: false, frontendBranch: env.BRANCH_NAME, type: 'frontend'
|
||||||
def commitHash = 'UNKNOWN'
|
|
||||||
|
|
||||||
pipeline {
|
|
||||||
agent any
|
|
||||||
options {
|
|
||||||
skipDefaultCheckout true
|
|
||||||
parallelsAlwaysFailFast()
|
|
||||||
}
|
|
||||||
stages {
|
|
||||||
stage('approbation') {
|
|
||||||
steps {
|
|
||||||
sh 'printenv'
|
|
||||||
checkout scm
|
|
||||||
runApprobation ignoreFailure: false, frontendBranch: env.BRANCH_NAME, type: 'frontend'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
.PHONY: latest-release
|
||||||
|
latest-release:
|
||||||
|
gh release list | head -n 1 | awk '{print $1}'
|
||||||
|
|
||||||
|
.PHONY: show-latest-release
|
||||||
|
show-latest-release:
|
||||||
|
gh release view `gh release list | head -n 1 | awk '{print $1}'`
|
||||||
|
|
||||||
|
.PHONY: recalculate-ipfs
|
||||||
|
recalculate-ipfs:
|
||||||
|
echo "ipfs hash inside the image"
|
||||||
|
docker run --rm ${TAG} cat /ipfs-hash
|
||||||
|
echo "recalculating ipfs hash"
|
||||||
|
docker run --rm ${TAG} ipfs add -r /usr/share/nginx/html
|
||||||
|
|
||||||
|
.PHONY: eject-ipfs-hash
|
||||||
|
unpack:
|
||||||
|
docker create --name=dist ${TAG}
|
||||||
|
docker cp dist:/usr/share/nginx/html dist
|
||||||
|
docker rm dist
|
||||||
@@ -72,7 +72,7 @@ Run `nx serve my-app` for a dev server. Navigate to the port specified in `app/<
|
|||||||
In order to generate the schemas for your GraphQL queries, you can run `GRAPHQL_SCHEMA_PATH=[YOUR SCHEMA FILE / API URL HERE] nx run types:generate`.
|
In order to generate the schemas for your GraphQL queries, you can run `GRAPHQL_SCHEMA_PATH=[YOUR SCHEMA FILE / API URL HERE] nx run types:generate`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export GRAPHQL_SCHEMA_PATH=https://api.n11.testnet.vega.xyz/graphql
|
export GRAPHQL_SCHEMA_PATH=https://api.n07.testnet.vega.xyz/graphql
|
||||||
yarn nx run types:generate
|
yarn nx run types:generate
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -103,25 +103,68 @@ In CI linting, formatting and also run. These checks can be seen in the [CI work
|
|||||||
|
|
||||||
Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more.
|
Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more.
|
||||||
|
|
||||||
# Docker & Vegacapsule
|
# 🐋 Hosting a console
|
||||||
|
|
||||||
## Docker
|
To host a console there are two possible build scenarios for running the frontends: nx performed **outside** or **inside** docker build. For specific build instructions follow [build instructions](#build-instructions).
|
||||||
|
|
||||||
The [Dockerfile](./dockerfiles) for running the frontends is pretty basic, merely building the application with the APP arg that is passed in and serving the application from [nginx](./nginx/nginx.conf). The only complexity that exists is that there is a script which allows the passing of run time environment variables to the containers. See configuration below for how to do this.
|
In order to run a container on port 3000:
|
||||||
|
|
||||||
You can build any of the containers locally with the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker build --dockerfile dockerfiles/Dockerfile.cra . --build-arg APP=[YOUR APP] --tag=[TAG]
|
|
||||||
```
|
|
||||||
|
|
||||||
In order to run a container:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -p 3000:80 [TAG]
|
docker run -p 3000:80 [TAG]
|
||||||
```
|
```
|
||||||
|
|
||||||
Images ending with `.dist` are to pack locally created transpiled HTML files into nginx container for non-compatible with yarn architectures like M1 Mac
|
## Build instructions
|
||||||
|
|
||||||
|
The [`docker`](./docker) subfolder has some docker configurations for easily setting up your own hosted version of console either for the web, or ready for pinning on IPFS
|
||||||
|
|
||||||
|
### nx build outside the docker
|
||||||
|
|
||||||
|
Packaging prepared dist into [`nginx`](https://hub.docker.com/_/nginx)([server configuration](./nginx/nginx.conf)) docker image involves building the application on docker host machine from source.
|
||||||
|
|
||||||
|
As a prerequisite you need to perform build of `dist` directory and move its content for specific application to `dist-result` directory. Use following script to do it with a single command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./docker/prepare-dist.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
You can build any of the containers locally with the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build --dockerfile docker/node-outside-docker.Dockerfile . --tag=[TAG]
|
||||||
|
```
|
||||||
|
|
||||||
|
### nx build inside the docker
|
||||||
|
|
||||||
|
Using multistage dockerfile dist is compiled using [node](https://hub.docker.com/_/node) image and later packed to nginx as in [dist build](#dist-build) example.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=$(cat .nvmrc) --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Computing ipfs-hash of the build
|
||||||
|
|
||||||
|
At the moment this feature is important only for `trading` (console) releases.
|
||||||
|
|
||||||
|
Each docker build finishes with hash calculation for dist directory. Resulting hash is added to file named as `/ipfs-hash`. Once docker image is produced you can run following commad to display ipfs-hash:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make recalculate-ipfs TAG=vegaprotocol/trading:{YOUR_VERSION}
|
||||||
|
```
|
||||||
|
|
||||||
|
**updating hash:** recompiling dist directory (even if there are no changed to source code) results in different hash computed by ipfs command.
|
||||||
|
|
||||||
|
### Verifying ipfs-hash of existing current application version
|
||||||
|
|
||||||
|
An IPFS CID will be attached to every [release](https://github.com/vegaprotocol/frontend-monorepo/releases). If you are intending to pin an application on IPFS, you can check that your build matches by running the following steps:
|
||||||
|
|
||||||
|
1. Show latest release by runnning: `make latest-release`. You need to configure [`gh`](https://cli.github.com/) for this step to work, otherwise please provide release manually from [github](https://github.com/vegaprotocol/frontend-monorepo/releases) or [dockerhub](https://hub.docker.com/r/vegaprotocol/trading)
|
||||||
|
2. Set RELEASE environment variable to value that you want to validate: `export RELEASE=$(make latest-release)` or `export RELEASE=vXX.XX.XX`
|
||||||
|
3. Set TAG environment variable to image that you want to validate: `export TAG=vegaprotocol/trading:$RELEASE`
|
||||||
|
4. Download docker image with the desired release `docker pull $TAG`.
|
||||||
|
5. Recalculate hash: `make recalculate-ipfs`
|
||||||
|
6. You should see exactly same hash produced by ipfs command as one placed with the release notes: `make show-latest-release`
|
||||||
|
7. If you want to extract dist from docker image to your local filesystem you can run following command: `make unpack`
|
||||||
|
8. Now `dist` directory contains valid application build. **it is not possible to calculate same ipfs hash on files that are result of copy operation**
|
||||||
|
|
||||||
## Config
|
## Config
|
||||||
|
|
||||||
@@ -161,7 +204,7 @@ In order to setup and run vegawallet for e2e capsule tests, in a separate termin
|
|||||||
bash setup-vegawallet.sh
|
bash setup-vegawallet.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
3. copy generated `api-token` and paste the token into `CYPRESS_VEGA_WALLET_API_TOKEN` environment variable in either `apps/token-e2e/.env` or `apps/explorer-e2e/.env` depending on which project needs testing.
|
3. copy generated `api-token` and paste the token into `CYPRESS_VEGA_WALLET_API_TOKEN` environment variable in either `apps/governance-e2e/.env` or `apps/explorer-e2e/.env` depending on which project needs testing.
|
||||||
|
|
||||||
Note: The script is only needed if capsule was built for first time or fresh. To run existing wallet service for capsule:
|
Note: The script is only needed if capsule was built for first time or fresh. To run existing wallet service for capsule:
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
|
|
||||||
NX_TENDERMINT_URL=http://localhost:26617
|
NX_TENDERMINT_URL=http://localhost:26617
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||||
NX_VEGA_URL=http://localhost:3028/query
|
NX_VEGA_URL=http://localhost:3008/graphql
|
||||||
NX_VEGA_ENV=CUSTOM
|
NX_VEGA_ENV=CUSTOM
|
||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/capsule-network.json
|
NX_VEGA_CONFIG_URL=
|
||||||
|
|
||||||
CYPRESS_VEGA_TENDERMINT_URL=http://localhost:26617
|
CYPRESS_VEGA_TENDERMINT_URL=http://localhost:26617
|
||||||
|
|
||||||
@@ -17,5 +16,10 @@ NX_EXPLORER_NETWORK_PARAMETERS=1
|
|||||||
NX_EXPLORER_PARTIES=1
|
NX_EXPLORER_PARTIES=1
|
||||||
NX_EXPLORER_VALIDATORS=1
|
NX_EXPLORER_VALIDATORS=1
|
||||||
|
|
||||||
|
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
||||||
|
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
||||||
|
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
||||||
|
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
|
||||||
|
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
|
||||||
|
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
||||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||||
CYPRESS_VEGA_URL=http://localhost:3028/query
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# App configuration variables
|
# App configuration variables
|
||||||
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
|
|
||||||
NX_TENDERMINT_URL=https://n04.d.vega.xyz/tm
|
NX_TENDERMINT_URL=https://n04.d.vega.xyz/tm
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
|
||||||
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
|
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
# App configuration variables
|
# App configuration variables
|
||||||
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
|
|
||||||
NX_TENDERMINT_URL=https://mainnet-observer-proxy01.ops.vega.xyz/
|
NX_TENDERMINT_URL=https://mainnet-observer-proxy01.ops.vega.xyz/
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket
|
||||||
NX_VEGA_URL=https://api.vega.xyz/query
|
NX_VEGA_URL=https://api.vega.community/graphql
|
||||||
NX_VEGA_ENV=MAINNET
|
NX_VEGA_ENV=MAINNET
|
||||||
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest
|
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
# App configuration variables
|
|
||||||
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
|
|
||||||
NX_TENDERMINT_URL=https://tm.n00.stagnet3.vega.xyz
|
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n00.stagnet3.vega.xyz/websocket
|
|
||||||
NX_VEGA_URL=https://api.stagnet3.vega.xyz/graphql
|
|
||||||
NX_VEGA_ENV=STAGNET3
|
|
||||||
NX_BLOCK_EXPLORER=https://be.stagnet3.vega.xyz/rest
|
|
||||||
|
|
||||||
# App flags
|
|
||||||
NX_EXPLORER_ASSETS=1
|
|
||||||
NX_EXPLORER_GENESIS=1
|
|
||||||
NX_EXPLORER_GOVERNANCE=1
|
|
||||||
NX_EXPLORER_MARKETS=1
|
|
||||||
NX_EXPLORER_ORACLES=1
|
|
||||||
NX_EXPLORER_TXS_LIST=0
|
|
||||||
NX_EXPLORER_NETWORK_PARAMETERS=1
|
|
||||||
NX_EXPLORER_PARTIES=1
|
|
||||||
NX_EXPLORER_VALIDATORS=1
|
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
# App configuration variables
|
# App configuration variables
|
||||||
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
|
|
||||||
NX_TENDERMINT_URL=https://tm.n07.testnet.vega.xyz/tm
|
NX_TENDERMINT_URL=https://tm.n07.testnet.vega.xyz/tm
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
|
||||||
NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
|
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||||
NX_VEGA_ENV=TESTNET
|
NX_VEGA_ENV=TESTNET
|
||||||
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
|
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
const { defineConfig } = require('cypress');
|
const { defineConfig } = require('cypress');
|
||||||
|
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
projectId: 'et4snf',
|
reporter: '../../node_modules/cypress-mochawesome-reporter',
|
||||||
|
|
||||||
e2e: {
|
e2e: {
|
||||||
setupNodeEvents(on, config) {
|
setupNodeEvents(on, config) {
|
||||||
|
require('cypress-mochawesome-reporter/plugin')(on);
|
||||||
require('@cypress/grep/src/plugin')(config);
|
require('@cypress/grep/src/plugin')(config);
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
@@ -21,10 +22,11 @@ module.exports = defineConfig({
|
|||||||
chromeWebSecurity: false,
|
chromeWebSecurity: false,
|
||||||
viewportWidth: 1440,
|
viewportWidth: 1440,
|
||||||
viewportHeight: 900,
|
viewportHeight: 900,
|
||||||
|
testIsolation: false,
|
||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
environment: 'CUSTOM',
|
environment: 'CUSTOM',
|
||||||
networkQueryUrl: 'http://localhost:3028/query',
|
networkQueryUrl: 'http://localhost:3008/graphql',
|
||||||
ethUrl: 'https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
|
ethUrl: 'https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
|
||||||
commitHash: 'dev',
|
commitHash: 'dev',
|
||||||
tsConfig: 'tsconfig.json',
|
tsConfig: 'tsconfig.json',
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
|
"name": "explorer-e2e",
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
"sourceRoot": "apps/explorer-e2e/src",
|
"sourceRoot": "apps/explorer-e2e/src",
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"targets": {
|
"targets": {
|
||||||
"e2e": {
|
"e2e": {
|
||||||
"executor": "@nrwl/cypress:cypress",
|
"executor": "@nx/cypress:cypress",
|
||||||
"options": {
|
"options": {
|
||||||
"cypressConfig": "apps/explorer-e2e/cypress.config.js",
|
"cypressConfig": "apps/explorer-e2e/cypress.config.js",
|
||||||
"devServerTarget": "explorer:serve"
|
"devServerTarget": "explorer:serve"
|
||||||
@@ -16,14 +17,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"executor": "@nrwl/linter:eslint",
|
"executor": "@nx/linter:eslint",
|
||||||
"outputs": ["{options.outputFile}"],
|
"outputs": ["{options.outputFile}"],
|
||||||
"options": {
|
"options": {
|
||||||
"lintFilePatterns": ["apps/explorer-e2e/**/*.{js,ts}"]
|
"lintFilePatterns": ["apps/explorer-e2e/**/*.{js,ts}"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"executor": "@nrwl/workspace:run-commands",
|
"executor": "nx:run-commands",
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"options": {
|
"options": {
|
||||||
"command": "yarn tsc --project ./apps/explorer-e2e/"
|
"command": "yarn tsc --project ./apps/explorer-e2e/"
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
"governance.proposal.updateMarket.minVoterBalance",
|
"governance.proposal.updateMarket.minVoterBalance",
|
||||||
"governance.proposal.updateNetParam.minProposerBalance",
|
"governance.proposal.updateNetParam.minProposerBalance",
|
||||||
"governance.proposal.updateNetParam.minVoterBalance",
|
"governance.proposal.updateNetParam.minVoterBalance",
|
||||||
|
"governance.proposal.updateAsset.minProposerBalance",
|
||||||
|
"governance.proposal.updateAsset.minVoterBalance",
|
||||||
"reward.staking.delegation.maxPayoutPerEpoch",
|
"reward.staking.delegation.maxPayoutPerEpoch",
|
||||||
"reward.staking.delegation.maxPayoutPerParticipant",
|
"reward.staking.delegation.maxPayoutPerParticipant",
|
||||||
"reward.staking.delegation.minimumValidatorStake",
|
"reward.staking.delegation.minimumValidatorStake",
|
||||||
@@ -19,10 +21,6 @@
|
|||||||
"validators.delegation.minAmount"
|
"validators.delegation.minAmount"
|
||||||
],
|
],
|
||||||
"fiveDecimal": [
|
"fiveDecimal": [
|
||||||
"governance.proposal.updateAsset.minProposerBalance",
|
|
||||||
"governance.proposal.updateAsset.minVoterBalance",
|
|
||||||
"governance.proposal.updateAsset.requiredParticipation",
|
|
||||||
"governance.proposal.updateMarket.minProposerEquityLikeShare",
|
|
||||||
"market.fee.factors.infrastructureFee",
|
"market.fee.factors.infrastructureFee",
|
||||||
"market.fee.factors.makerFee",
|
"market.fee.factors.makerFee",
|
||||||
"market.liquidity.bondPenaltyParameter",
|
"market.liquidity.bondPenaltyParameter",
|
||||||
@@ -77,6 +75,8 @@
|
|||||||
"governance.proposal.updateMarket.requiredParticipationLP",
|
"governance.proposal.updateMarket.requiredParticipationLP",
|
||||||
"governance.proposal.updateNetParam.requiredMajority",
|
"governance.proposal.updateNetParam.requiredMajority",
|
||||||
"governance.proposal.updateNetParam.requiredParticipation",
|
"governance.proposal.updateNetParam.requiredParticipation",
|
||||||
|
"governance.proposal.updateMarket.minProposerEquityLikeShare",
|
||||||
|
"governance.proposal.updateAsset.requiredParticipation",
|
||||||
"validators.vote.required"
|
"validators.vote.required"
|
||||||
],
|
],
|
||||||
"duration": [
|
"duration": [
|
||||||
|
|||||||
@@ -1,150 +1,48 @@
|
|||||||
context('Asset page', { tags: '@regression' }, function () {
|
context('Asset page', { tags: '@regression' }, () => {
|
||||||
before('gather system asset information', function () {
|
const columns = ['symbol', 'name', 'id', 'type', 'status', 'actions'];
|
||||||
cy.get_asset_information().as('assetsInfo');
|
const hiddenOnMobile = ['id', 'type', 'status'];
|
||||||
});
|
describe('Verify elements on page', () => {
|
||||||
|
before('Navigate to assets page', () => {
|
||||||
describe('Verify elements on page', function () {
|
cy.visit('/assets');
|
||||||
const assetsNavigation = 'a[href="/assets"]';
|
|
||||||
const assetHeader = '[data-testid="asset-header"]';
|
|
||||||
const jsonSection = '.language-json';
|
|
||||||
|
|
||||||
before('Navigate to assets page', function () {
|
|
||||||
cy.visit('/');
|
|
||||||
cy.get(assetsNavigation).click();
|
|
||||||
|
|
||||||
// Check we have enough enough assets
|
|
||||||
const assetNames = Object.keys(this.assetsInfo);
|
|
||||||
assert.isAtLeast(
|
|
||||||
assetNames.length,
|
|
||||||
5,
|
|
||||||
'Ensuring we have at least 5 assets to test'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to see assets page sections', function () {
|
it('should be able to see full assets list', () => {
|
||||||
const assetNames = Object.keys(this.assetsInfo);
|
cy.getAssets().then((assets) => {
|
||||||
assetNames.forEach((assetName) => {
|
assets.forEach((asset) => {
|
||||||
cy.get(assetHeader)
|
cy.get(`[row-id="${asset.id}"]`).should('be.visible');
|
||||||
.contains(assetName)
|
|
||||||
.should('be.visible')
|
|
||||||
.next()
|
|
||||||
.within(() => {
|
|
||||||
cy.get(jsonSection).should('not.be.empty');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to see all asset details displayed in JSON', function () {
|
|
||||||
const assetNames = Object.keys(this.assetsInfo);
|
|
||||||
assetNames.forEach((assetName) => {
|
|
||||||
cy.get(assetHeader)
|
|
||||||
.contains(assetName)
|
|
||||||
.next()
|
|
||||||
.within(() => {
|
|
||||||
cy.get(jsonSection)
|
|
||||||
.invoke('text')
|
|
||||||
.convert_string_json_to_js_object()
|
|
||||||
.then((assetsListedInJson) => {
|
|
||||||
const assetInfo = this.assetsInfo[assetName];
|
|
||||||
|
|
||||||
assert.equal(assetsListedInJson.name, assetInfo.node.name);
|
|
||||||
assert.equal(assetsListedInJson.id, assetInfo.node.id);
|
|
||||||
assert.equal(
|
|
||||||
assetsListedInJson.decimals,
|
|
||||||
assetInfo.node.decimals
|
|
||||||
);
|
|
||||||
assert.equal(assetsListedInJson.symbol, assetInfo.node.symbol);
|
|
||||||
assert.equal(
|
|
||||||
assetsListedInJson.source.__typename,
|
|
||||||
assetInfo.node.source.__typename
|
|
||||||
);
|
|
||||||
|
|
||||||
if (assetInfo.node.source.__typename == 'ERC20') {
|
|
||||||
assert.equal(
|
|
||||||
assetsListedInJson.source.contractAddress,
|
|
||||||
assetInfo.node.source.contractAddress
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (assetInfo.node.source.__typename == 'BuiltinAsset') {
|
|
||||||
assert.equal(
|
|
||||||
assetsListedInJson.source.maxFaucetAmountMint,
|
|
||||||
assetInfo.node.source.maxFaucetAmountMint
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let knownAssetTypes = ['BuiltinAsset', 'ERC20'];
|
|
||||||
assert.include(
|
|
||||||
knownAssetTypes,
|
|
||||||
assetInfo.node.source.__typename,
|
|
||||||
`Checking that current asset type of ${assetInfo.node.source.__typename} /
|
|
||||||
is one of: ${knownAssetTypes}: /
|
|
||||||
If fail then we need to add extra tests for un-encountered asset types`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to switch assets between light and dark mode', function () {
|
|
||||||
const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)';
|
|
||||||
const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)';
|
|
||||||
const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)';
|
|
||||||
const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)';
|
|
||||||
const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)';
|
|
||||||
const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)';
|
|
||||||
const themeSwitcher = '[data-testid="theme-switcher"]';
|
|
||||||
const jsonFields = '.hljs';
|
|
||||||
const sideMenuBackground = '.absolute';
|
|
||||||
|
|
||||||
// Engage dark mode if not allready set
|
|
||||||
cy.get(sideMenuBackground)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.then((background_color) => {
|
|
||||||
if (background_color.includes(whiteThemeSideMenuBackgroundColor))
|
|
||||||
cy.get(themeSwitcher).click();
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
// Engage white mode
|
columns.forEach((col) => {
|
||||||
cy.get(themeSwitcher).click();
|
cy.get(`[col-id="${col}"]`).should('be.visible');
|
||||||
|
});
|
||||||
// White Mode
|
|
||||||
cy.get(assetsNavigation)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', whiteThemeSelectedMenuOptionColor);
|
|
||||||
cy.get(jsonFields)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', whiteThemeJsonFieldBackColor);
|
|
||||||
cy.get(sideMenuBackground)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', whiteThemeSideMenuBackgroundColor);
|
|
||||||
|
|
||||||
// Dark Mode
|
|
||||||
cy.get(themeSwitcher).click();
|
|
||||||
cy.get(assetsNavigation)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', darkThemeSelectedMenuOptionColor);
|
|
||||||
cy.get(jsonFields)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', darkThemeJsonFieldBackColor);
|
|
||||||
cy.get(sideMenuBackground)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', darkThemeSideMenuBackgroundColor);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to see assets page displayed in mobile', function () {
|
it('should be able to see assets page displayed in mobile', () => {
|
||||||
cy.common_switch_to_mobile_and_click_toggle();
|
cy.switchToMobile();
|
||||||
cy.get(assetsNavigation).click();
|
|
||||||
|
|
||||||
const assetNames = Object.keys(this.assetsInfo);
|
hiddenOnMobile.forEach((col) => {
|
||||||
assetNames.forEach((assetName) => {
|
cy.get(`[col-id="${col}"]`).should('have.length', 0);
|
||||||
cy.get(assetHeader)
|
});
|
||||||
.contains(assetName)
|
|
||||||
.should('be.visible')
|
cy.getAssets().then((assets) => {
|
||||||
.next()
|
assets.forEach((asset) => {
|
||||||
.within(() => {
|
cy.get(`[row-id="${asset.id}"]`).should('be.visible');
|
||||||
cy.get(jsonSection).should('not.be.empty');
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('should open details page when clicked on "View details"', () => {
|
||||||
|
cy.getAssets().then((assets) => {
|
||||||
|
assets.forEach((asset) => {
|
||||||
|
cy.get(`[row-id="${asset.id}"] [col-id="actions"] button`)
|
||||||
|
.eq(0)
|
||||||
|
.should('contain.text', 'View details');
|
||||||
|
cy.get(`[row-id="${asset.id}"] [col-id="actions"] button`)
|
||||||
|
.eq(0)
|
||||||
|
.click();
|
||||||
|
cy.getByTestId('asset-header').should('have.text', asset.name);
|
||||||
|
cy.go('back');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,33 +1,27 @@
|
|||||||
context('Blocks page', { tags: '@regression' }, function () {
|
context('Blocks page', { tags: '@regression' }, function () {
|
||||||
before('visit token home page', function () {
|
|
||||||
cy.visit('/');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Verify elements on page', function () {
|
describe('Verify elements on page', function () {
|
||||||
const blockNavigation = 'a[href="/blocks"]';
|
beforeEach(() => {
|
||||||
|
cy.visit('/blocks');
|
||||||
|
});
|
||||||
const blockHeight = '[data-testid="block-height"]';
|
const blockHeight = '[data-testid="block-height"]';
|
||||||
const blockTime = '[data-testid="block-time"]';
|
const blockTime = '[data-testid="block-time"]';
|
||||||
const blockHeader = '[data-testid="block-header"]';
|
const blockHeader = '[data-testid="block-header"]';
|
||||||
const previousBlockBtn = '[data-testid="previous-block"]';
|
const previousBlockBtn = '[data-testid="previous-block"]';
|
||||||
const infiniteScrollWrapper = '[data-testid="infinite-scroll-wrapper"]';
|
const infiniteScrollWrapper = '[data-testid="infinite-scroll-wrapper"]';
|
||||||
|
|
||||||
beforeEach('navigate to blocks page', function () {
|
|
||||||
cy.get(blockNavigation).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Blocks page is displayed', function () {
|
it('Blocks page is displayed', function () {
|
||||||
validateBlocksDisplayed();
|
validateBlocksDisplayed();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Blocks page is displayed on mobile', function () {
|
it('Blocks page is displayed on mobile', function () {
|
||||||
cy.common_switch_to_mobile_and_click_toggle();
|
cy.switchToMobile();
|
||||||
cy.get(blockNavigation).click();
|
|
||||||
validateBlocksDisplayed();
|
validateBlocksDisplayed();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Block validator page is displayed', function () {
|
it('Block validator page is displayed', function () {
|
||||||
waitForBlocksResponse();
|
waitForBlocksResponse();
|
||||||
cy.get(blockHeight).eq(0).click();
|
cy.get(blockHeight).eq(0).find('a').click({ force: true });
|
||||||
|
|
||||||
cy.get('[data-testid="block-validator"]').should('not.be.empty');
|
cy.get('[data-testid="block-validator"]').should('not.be.empty');
|
||||||
cy.get(blockTime).should('not.be.empty');
|
cy.get(blockTime).should('not.be.empty');
|
||||||
//TODO: Add assertion for transactions when txs are added
|
//TODO: Add assertion for transactions when txs are added
|
||||||
@@ -35,7 +29,7 @@ context('Blocks page', { tags: '@regression' }, function () {
|
|||||||
|
|
||||||
it('Navigate to previous block', function () {
|
it('Navigate to previous block', function () {
|
||||||
waitForBlocksResponse();
|
waitForBlocksResponse();
|
||||||
cy.get(blockHeight).eq(0).click();
|
cy.get(blockHeight).eq(0).find('a').click({ force: true });
|
||||||
cy.get(blockHeader)
|
cy.get(blockHeader)
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.then(($blockHeaderTxt) => {
|
.then(($blockHeaderTxt) => {
|
||||||
|
|||||||
@@ -6,75 +6,39 @@ context('Home Page', function () {
|
|||||||
describe('Stats page', { tags: '@smoke' }, function () {
|
describe('Stats page', { tags: '@smoke' }, function () {
|
||||||
const statsValue = '[data-testid="stats-value"]';
|
const statsValue = '[data-testid="stats-value"]';
|
||||||
|
|
||||||
it('Should show connected environment', function () {
|
|
||||||
const deployedEnv = Cypress.env('environment').toUpperCase();
|
|
||||||
cy.get('[data-testid="stats-environment"]').should(
|
|
||||||
'have.text',
|
|
||||||
`/ ${deployedEnv}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show connected environment stats', function () {
|
it('should show connected environment stats', function () {
|
||||||
const statTitles = {
|
const statTitles = {
|
||||||
0: 'Status',
|
0: 'Status',
|
||||||
1: 'Height',
|
1: 'Epoch',
|
||||||
2: 'Uptime',
|
2: 'Block height',
|
||||||
3: 'Total nodes',
|
3: 'Uptime',
|
||||||
4: 'Total staked',
|
4: 'Total nodes',
|
||||||
5: 'Backlog',
|
5: 'Total staked',
|
||||||
6: 'Trades / second',
|
6: 'Backlog',
|
||||||
7: 'Orders / block',
|
7: 'Trades / second',
|
||||||
8: 'Orders / second',
|
8: 'Orders / block',
|
||||||
9: 'Transactions / block',
|
9: 'Orders / second',
|
||||||
10: 'Block time',
|
10: 'Transactions / block',
|
||||||
11: 'Time',
|
11: 'Block time',
|
||||||
12: 'App',
|
12: 'Time',
|
||||||
13: 'Tendermint',
|
13: 'App',
|
||||||
14: 'Up since',
|
14: 'Tendermint',
|
||||||
15: 'Chain ID',
|
15: 'Up since',
|
||||||
|
16: 'Chain ID',
|
||||||
};
|
};
|
||||||
|
|
||||||
cy.get('[data-testid="stats-title"]')
|
cy.get('[data-testid="stats-title"]')
|
||||||
.each(($list, index) => {
|
.each(($list, index) => {
|
||||||
cy.wrap($list).should('have.text', statTitles[index]);
|
cy.wrap($list).should('contain.text', statTitles[index]);
|
||||||
})
|
})
|
||||||
.then(($list) => {
|
.then(($list) => {
|
||||||
cy.wrap($list).should('have.length', 16);
|
cy.wrap($list).should('have.length', 17);
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.get(statsValue).eq(0).should('have.text', 'CONNECTED');
|
|
||||||
cy.get(statsValue).eq(1).should('not.be.empty');
|
|
||||||
cy.get(statsValue)
|
|
||||||
.eq(2)
|
|
||||||
.invoke('text')
|
|
||||||
.should('match', /\d+d \d+h \d+m \d+s/i);
|
|
||||||
cy.get(statsValue).eq(3).should('have.text', '2');
|
|
||||||
cy.get(statsValue)
|
|
||||||
.eq(4)
|
|
||||||
.invoke('text')
|
|
||||||
.should('match', /\d+\.\d\d(?!\d)/i);
|
|
||||||
cy.get(statsValue).eq(5).should('have.text', '0');
|
|
||||||
cy.get(statsValue).eq(6).should('have.text', '0');
|
|
||||||
cy.get(statsValue).eq(7).should('have.text', '0');
|
|
||||||
cy.get(statsValue).eq(8).should('have.text', '0');
|
|
||||||
cy.get(statsValue).eq(9).should('not.be.empty');
|
|
||||||
cy.get(statsValue).eq(10).should('not.be.empty');
|
|
||||||
cy.get(statsValue).eq(11).should('not.be.empty');
|
|
||||||
cy.get(statsValue)
|
|
||||||
.eq(12)
|
|
||||||
.invoke('text')
|
|
||||||
.should('match', /v\d+\.\d+\.\d+/i);
|
|
||||||
cy.get(statsValue)
|
|
||||||
.eq(13)
|
|
||||||
.invoke('text')
|
|
||||||
.should('match', /\d+\.\d+\.\d+/i);
|
|
||||||
cy.get(statsValue).eq(14).should('not.be.empty');
|
|
||||||
cy.get(statsValue).eq(15).should('not.be.empty');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Block height should be updating', function () {
|
it('Block height should be updating', function () {
|
||||||
cy.get(statsValue)
|
cy.get(statsValue)
|
||||||
.eq(1)
|
.eq(2)
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.then((blockHeightTxt) => {
|
.then((blockHeightTxt) => {
|
||||||
cy.get(statsValue)
|
cy.get(statsValue)
|
||||||
@@ -86,75 +50,4 @@ context('Home Page', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Git info', function () {
|
|
||||||
it('git info is rendered on the footer of the page', function () {
|
|
||||||
cy.getByTestId('git-info').within(() => {
|
|
||||||
cy.getByTestId('git-network-data').within(() => {
|
|
||||||
cy.contains('Reading network data from').should('be.visible');
|
|
||||||
cy.get('span').should('have.text', Cypress.env('networkQueryUrl'));
|
|
||||||
cy.getByTestId('link').should('be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.getByTestId('git-eth-data').within(() => {
|
|
||||||
cy.contains('Reading Ethereum data from').should('be.visible');
|
|
||||||
cy.get('span').should('have.text', Cypress.env('ethUrl'));
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.getByTestId('git-commit-hash').within(() => {
|
|
||||||
cy.contains('Version/commit hash:').should('be.visible');
|
|
||||||
cy.getByTestId('link').should('have.text', Cypress.env('commitHash'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Search bar', function () {
|
|
||||||
it('Successful search for specific id by block id', function () {
|
|
||||||
const blockId = '973624';
|
|
||||||
search(blockId);
|
|
||||||
cy.url().should('include', blockId);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Successful search for specific id by tx hash', function () {
|
|
||||||
const txHash =
|
|
||||||
'9ED3718AA8308E7E08EC588EE7AADAF49711D2138860D8914B4D81A2054D9FB8';
|
|
||||||
search(txHash);
|
|
||||||
cy.url().should('include', txHash);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Successful search for specific id by tx id', function () {
|
|
||||||
const txId =
|
|
||||||
'0x61DCCEBB955087F50D0B85382DAE138EDA9631BF1A4F92E563D528904AA38898';
|
|
||||||
search(txId);
|
|
||||||
cy.url().should('include', txId);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Error message displayed when invalid search by wrong string length', function () {
|
|
||||||
search('9ED3718AA8308E7E08EC588EE7AADAF497D2138860D8914B4D81A2054D9FB8');
|
|
||||||
validateSearchError("Something doesn't look right");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Error message displayed when invalid search by invalid hash', function () {
|
|
||||||
search(
|
|
||||||
'9ED3718AA8308E7E08ECht8EE753DAF49711D2138860D8914B4D81A2054D9FB8'
|
|
||||||
);
|
|
||||||
validateSearchError('Transaction is not hexadecimal');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Error message displayed when searching empty field', function () {
|
|
||||||
cy.get('[data-testid="search"]').clear();
|
|
||||||
cy.get('[data-testid="search-button"]').click();
|
|
||||||
validateSearchError('Search required');
|
|
||||||
});
|
|
||||||
|
|
||||||
function search(searchTxt) {
|
|
||||||
cy.get('[data-testid="search"]').clear().type(searchTxt);
|
|
||||||
cy.get('[data-testid="search-button"]').click();
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateSearchError(expectedError) {
|
|
||||||
cy.get('[data-testid="search-error"]').should('have.text', expectedError);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,19 +1,130 @@
|
|||||||
//Tests set to skip until market bug for capsule checkpoint is fixed
|
context('Market page', { tags: '@regression' }, function () {
|
||||||
context.skip('Market page', { tags: '@regression' }, function () {
|
|
||||||
describe('Verify elements on page', function () {
|
describe('Verify elements on page', function () {
|
||||||
const marketHeaders = '[data-testid="markets-header"]';
|
const marketHeaders = 'markets-heading';
|
||||||
const marketNavigation = 'a[href="/markets"]';
|
const createdMarketId =
|
||||||
|
'2eab0e66545a789047561bc5a2e5cbc3b19eb708da41104e3cac2474ee36c4d4';
|
||||||
|
|
||||||
it('Markets page is displayed', function () {
|
before('Create market', function () {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
cy.get(marketNavigation).click();
|
cy.createMarket();
|
||||||
cy.common_validate_blocks_data_displayed(marketHeaders);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Markets page displayed on mobile', function () {
|
beforeEach('Get market id', function () {
|
||||||
|
cy.navigate_to('markets');
|
||||||
|
cy.get('[col-id="id"]').eq(1).invoke('text').as('createdMarketId');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Market displayed on market page', function () {
|
||||||
|
cy.navigate_to('markets');
|
||||||
|
cy.getByTestId(marketHeaders).should('be.visible');
|
||||||
|
cy.get(`[row-id="${this.createdMarketId}"]`)
|
||||||
|
.should('be.visible')
|
||||||
|
.within(() => {
|
||||||
|
cy.get_element_by_col_id('code').should('have.text', 'TEST.24h');
|
||||||
|
cy.get_element_by_col_id('name').should('have.text', 'Test market 1');
|
||||||
|
cy.get_element_by_col_id('state').should('have.text', 'Pending');
|
||||||
|
cy.get_element_by_col_id('asset').should('have.text', 'fUSDC');
|
||||||
|
cy.get_element_by_col_id('id').should(
|
||||||
|
'have.text',
|
||||||
|
this.createdMarketId
|
||||||
|
);
|
||||||
|
cy.get_element_by_col_id('actions')
|
||||||
|
.find('a')
|
||||||
|
.should('have.attr', 'href', `/markets/${this.createdMarketId}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Able to go to market details page', function () {
|
||||||
|
cy.navigate_to('markets');
|
||||||
|
cy.get_element_by_col_id('actions').eq(1).click();
|
||||||
|
cy.getByTestId(marketHeaders).should('have.text', 'Test market 1');
|
||||||
|
cy.validate_element_from_table('Name', 'Test market 1');
|
||||||
|
cy.validate_element_from_table('Market ID', this.createdMarketId);
|
||||||
|
cy.validate_element_from_table('Trading Mode', 'Opening auction');
|
||||||
|
cy.validate_element_from_table('Market Decimal Places', '5');
|
||||||
|
cy.validate_element_from_table('Position Decimal Places', '5');
|
||||||
|
cy.validate_element_from_table('Settlement Asset Decimal Places', '5');
|
||||||
|
// Instrument
|
||||||
|
cy.validate_element_from_table('Market Name', 'Test market 1');
|
||||||
|
cy.validate_element_from_table('Code', 'TEST.24h');
|
||||||
|
cy.validate_element_from_table('Product Type', 'Future');
|
||||||
|
cy.validate_element_from_table('Quote Name', 'fUSDC');
|
||||||
|
// Settlement Asset
|
||||||
|
cy.validate_element_from_table('Quote Name', 'fUSDC');
|
||||||
|
cy.validate_element_from_table('Symbol', 'fUSDC');
|
||||||
|
cy.validate_element_from_table('Decimals', '5');
|
||||||
|
cy.validate_element_from_table('Quantum', '0.00');
|
||||||
|
cy.validate_element_from_table('Status', 'Enabled');
|
||||||
|
cy.validate_element_from_table('Max faucet amount', '10,000,000.00');
|
||||||
|
cy.validate_element_from_table(
|
||||||
|
'Infrastructure fee account balance',
|
||||||
|
'0.00'
|
||||||
|
);
|
||||||
|
cy.validate_element_from_table(
|
||||||
|
'Global reward pool account balance',
|
||||||
|
'0.00'
|
||||||
|
);
|
||||||
|
// Metadata
|
||||||
|
cy.validate_element_from_table('Sector', 'tech');
|
||||||
|
cy.validate_element_from_table('Source', 'docs.vega.xyz');
|
||||||
|
// Risk model
|
||||||
|
cy.validate_element_from_table('Tau', '0.0001140771161');
|
||||||
|
cy.validate_element_from_table('Risk Aversion Parameter', '0.01');
|
||||||
|
// Risk parameters
|
||||||
|
// cy.validate_element_from_table('R', '0.016')
|
||||||
|
cy.validate_element_from_table('Sigma', '0.5');
|
||||||
|
// Risk factors
|
||||||
|
cy.validate_element_from_table('Short', '0.0143218738374871');
|
||||||
|
cy.validate_element_from_table('Long', '0.0141450471498822');
|
||||||
|
// Price monitoring settings 1
|
||||||
|
cy.validate_element_from_table('Horizon Secs', '43,200');
|
||||||
|
cy.validate_element_from_table('Probability', '1');
|
||||||
|
cy.validate_element_from_table('Auction Extension Secs', '600');
|
||||||
|
// Liquidity Monitoring
|
||||||
|
cy.validate_element_from_table('Triggering Ratio', '0.7');
|
||||||
|
cy.validate_element_from_table('Time Window', '3,600');
|
||||||
|
cy.validate_element_from_table('Scaling Factor', '10');
|
||||||
|
// Liquidity
|
||||||
|
cy.validate_element_from_table('Target Stake', '0.00 fUSDC');
|
||||||
|
cy.validate_element_from_table('Supplied Stake', '0.00 fUSDC');
|
||||||
|
cy.validate_element_from_table('Market Value Proxy', '0.00 fUSDC');
|
||||||
|
// Liquidity price range
|
||||||
|
cy.validate_element_from_table(
|
||||||
|
'Liquidity Price Range',
|
||||||
|
'1,000.00% of mid price'
|
||||||
|
);
|
||||||
|
cy.validate_element_from_table('Lowest Price', '0.00 fUSDC');
|
||||||
|
cy.validate_element_from_table('Highest Price', '0.00 fUSDC');
|
||||||
|
|
||||||
|
cy.getByTestId('oracle-spec-links')
|
||||||
|
.should('have.attr', 'href')
|
||||||
|
.and(
|
||||||
|
'contain',
|
||||||
|
'/oracles/bf242aa5c9f64fcbb77808aa8582e73711519f4b35264eb797a80f1803590a24'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Able to view Json
|
||||||
|
cy.contains('View JSON').click();
|
||||||
|
cy.get('.language-json').should('exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Skipping due to resize observer loop limit error
|
||||||
|
it.skip('Markets page displayed on mobile', function () {
|
||||||
cy.common_switch_to_mobile_and_click_toggle();
|
cy.common_switch_to_mobile_and_click_toggle();
|
||||||
cy.get(marketNavigation).click();
|
cy.navigate_to('markets', true);
|
||||||
cy.common_validate_blocks_data_displayed(marketHeaders);
|
cy.getByTestId(marketHeaders).should('be.visible');
|
||||||
|
cy.get(`[row-id="${createdMarketId}"]`)
|
||||||
|
.should('be.visible')
|
||||||
|
.within(() => {
|
||||||
|
cy.get_element_by_col_id('code').should('have.text', 'TEST.24h');
|
||||||
|
cy.get_element_by_col_id('name').should('have.text', 'Test market 1');
|
||||||
|
cy.get_element_by_col_id('state').should('have.text', 'Pending');
|
||||||
|
cy.get_element_by_col_id('asset').should('have.text', 'fUSDC');
|
||||||
|
cy.get_element_by_col_id('id').should('have.text', createdMarketId);
|
||||||
|
cy.get_element_by_col_id('actions')
|
||||||
|
.find('a')
|
||||||
|
.should('have.attr', 'href', `/markets/${createdMarketId}`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,17 +2,14 @@ context('Network parameters page', { tags: '@smoke' }, function () {
|
|||||||
before('navigate to network parameter page', function () {
|
before('navigate to network parameter page', function () {
|
||||||
cy.fixture('net_parameter_format_lookup').as('networkParameterFormat');
|
cy.fixture('net_parameter_format_lookup').as('networkParameterFormat');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Verify elements on page', function () {
|
describe('Verify elements on page', function () {
|
||||||
const networkParametersNavigation = 'a[href="/network-parameters"]';
|
beforeEach(() => {
|
||||||
|
cy.visit('/network-parameters');
|
||||||
|
});
|
||||||
|
|
||||||
const networkParametersHeader = '[data-testid="network-param-header"]';
|
const networkParametersHeader = '[data-testid="network-param-header"]';
|
||||||
const tableRows = '[data-testid="key-value-table-row"]';
|
const tableRows = '[data-testid="key-value-table-row"]';
|
||||||
|
|
||||||
before('navigate to network parameter page', function () {
|
|
||||||
cy.visit('/');
|
|
||||||
cy.get(networkParametersNavigation).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show network parameter heading at top of page', function () {
|
it('should show network parameter heading at top of page', function () {
|
||||||
cy.get(networkParametersHeader)
|
cy.get(networkParametersHeader)
|
||||||
.should('have.text', 'Network Parameters')
|
.should('have.text', 'Network Parameters')
|
||||||
@@ -141,7 +138,7 @@ context('Network parameters page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should list each network parameter displayed as a currency value with four decimals - in the correct format', function () {
|
it.skip('should list each network parameter displayed as a currency value with four decimals - in the correct format', function () {
|
||||||
cy.get_network_parameters().then((network_parameters) => {
|
cy.get_network_parameters().then((network_parameters) => {
|
||||||
network_parameters = Object.entries(network_parameters);
|
network_parameters = Object.entries(network_parameters);
|
||||||
network_parameters.forEach((network_parameter) => {
|
network_parameters.forEach((network_parameter) => {
|
||||||
@@ -201,55 +198,8 @@ context('Network parameters page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to switch network parameter page - between light and dark mode', function () {
|
|
||||||
const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)';
|
|
||||||
const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)';
|
|
||||||
const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)';
|
|
||||||
const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)';
|
|
||||||
const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)';
|
|
||||||
const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)';
|
|
||||||
const themeSwitcher = '[data-testid="theme-switcher"]';
|
|
||||||
const jsonFields = '.hljs';
|
|
||||||
const sideMenuBackground = '.absolute';
|
|
||||||
|
|
||||||
// Engage dark mode if not already set
|
|
||||||
cy.get(sideMenuBackground)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.then((background_color) => {
|
|
||||||
if (background_color.includes(whiteThemeSideMenuBackgroundColor))
|
|
||||||
cy.get(themeSwitcher).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Engage white mode
|
|
||||||
cy.get(themeSwitcher).click();
|
|
||||||
|
|
||||||
// White Mode
|
|
||||||
cy.get(networkParametersNavigation)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', whiteThemeSelectedMenuOptionColor);
|
|
||||||
cy.get(jsonFields)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', whiteThemeJsonFieldBackColor);
|
|
||||||
cy.get(sideMenuBackground)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', whiteThemeSideMenuBackgroundColor);
|
|
||||||
|
|
||||||
// Dark Mode
|
|
||||||
cy.get(themeSwitcher).click();
|
|
||||||
cy.get(networkParametersNavigation)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', darkThemeSelectedMenuOptionColor);
|
|
||||||
cy.get(jsonFields)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', darkThemeJsonFieldBackColor);
|
|
||||||
cy.get(sideMenuBackground)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', darkThemeSideMenuBackgroundColor);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to see network parameters - on mobile', function () {
|
it('should be able to see network parameters - on mobile', function () {
|
||||||
cy.common_switch_to_mobile_and_click_toggle();
|
cy.switchToMobile();
|
||||||
cy.get(networkParametersNavigation).click();
|
|
||||||
cy.get_network_parameters().then((network_parameters) => {
|
cy.get_network_parameters().then((network_parameters) => {
|
||||||
network_parameters = Object.entries(network_parameters);
|
network_parameters = Object.entries(network_parameters);
|
||||||
network_parameters.forEach((network_parameter) => {
|
network_parameters.forEach((network_parameter) => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const customNodeBtn = 'custom-node';
|
|||||||
context.skip('Node switcher', { tags: '@regression' }, function () {
|
context.skip('Node switcher', { tags: '@regression' }, function () {
|
||||||
beforeEach('visit home page', function () {
|
beforeEach('visit home page', function () {
|
||||||
cy.intercept('GET', 'https://static.vega.xyz/assets/capsule-network.json', {
|
cy.intercept('GET', 'https://static.vega.xyz/assets/capsule-network.json', {
|
||||||
hosts: ['http://localhost:3028/query'],
|
hosts: ['http://localhost:3008/graphql'],
|
||||||
}).as('nodeData');
|
}).as('nodeData');
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
cy.wait('@nodeData');
|
cy.wait('@nodeData');
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ context.skip('Parties page', { tags: '@regression' }, function () {
|
|||||||
.convert_string_json_to_js_object()
|
.convert_string_json_to_js_object()
|
||||||
.get_party_accounts_data_from_js_object()
|
.get_party_accounts_data_from_js_object()
|
||||||
.then((accountsListedInJson) => {
|
.then((accountsListedInJson) => {
|
||||||
cy.get_asset_information().then((assetsInfo) => {
|
cy.getAssets().then((assetsInfo) => {
|
||||||
const assetInfo =
|
const assetInfo =
|
||||||
assetsInfo[accountsListedInJson[assetInTest].asset.name];
|
assetsInfo[accountsListedInJson[assetInTest].asset.name];
|
||||||
|
|
||||||
@@ -169,7 +169,6 @@ context.skip('Parties page', { tags: '@regression' }, function () {
|
|||||||
const jsonFields = '.hljs';
|
const jsonFields = '.hljs';
|
||||||
const sideMenuBackground = '.absolute';
|
const sideMenuBackground = '.absolute';
|
||||||
|
|
||||||
// Engage dark mode if not allready set
|
|
||||||
cy.get(sideMenuBackground)
|
cy.get(sideMenuBackground)
|
||||||
.should('have.css', 'background-color')
|
.should('have.css', 'background-color')
|
||||||
.then((background_color) => {
|
.then((background_color) => {
|
||||||
@@ -205,7 +204,7 @@ context.skip('Parties page', { tags: '@regression' }, function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('get_asset_decimals', (assetID) => {
|
Cypress.Commands.add('get_asset_decimals', (assetID) => {
|
||||||
cy.get_asset_information().then((assetsInfo) => {
|
cy.getAssets().then((assetsInfo) => {
|
||||||
const assetDecimals = assetsInfo[assetData[assetID].name].decimals;
|
const assetDecimals = assetsInfo[assetData[assetID].name].decimals;
|
||||||
let decimals = '';
|
let decimals = '';
|
||||||
for (let i = 0; i < assetDecimals; i++) decimals += '0';
|
for (let i = 0; i < assetDecimals; i++) decimals += '0';
|
||||||
@@ -219,7 +218,7 @@ context.skip('Parties page', { tags: '@regression' }, function () {
|
|||||||
balance type asset {id symbol decimals}}}}}}}}';
|
balance type asset {id symbol decimals}}}}}}}}';
|
||||||
cy.request({
|
cy.request({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `http://localhost:3028/query`,
|
url: `http://localhost:3008/graphql`,
|
||||||
body: {
|
body: {
|
||||||
query: mutation,
|
query: mutation,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ context.skip('Transactions page', function () {
|
|||||||
.first()
|
.first()
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
} else {
|
} else {
|
||||||
cy.slack('Unable to find any transactions on page');
|
cy.log('Unable to find any transactions on page');
|
||||||
cy.screenshot();
|
cy.screenshot();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -78,7 +78,7 @@ context.skip('Transactions page', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
cy.slack('Unable to find any transactions on page');
|
cy.log('Unable to find any transactions on page');
|
||||||
cy.screenshot();
|
cy.screenshot();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,303 +1,19 @@
|
|||||||
context('Validator page', { tags: '@smoke' }, function () {
|
context('Validator page', { tags: '@smoke' }, function () {
|
||||||
const validatorMenuHeading = 'a[href="/validators"]';
|
|
||||||
const tendermintDataHeader = '[data-testid="tendermint-header"]';
|
|
||||||
const vegaDataHeader = '[data-testid="vega-header"]';
|
|
||||||
const jsonSection = '.language-json';
|
|
||||||
|
|
||||||
before('Visit validators page and obtain data', function () {
|
before('Visit validators page and obtain data', function () {
|
||||||
cy.visit('/');
|
cy.visit('/validators');
|
||||||
cy.get(validatorMenuHeading).click();
|
|
||||||
cy.get_validators().as('validators');
|
|
||||||
cy.get_nodes().as('nodes');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Verify elements on page', function () {
|
describe('Verify elements on page', function () {
|
||||||
before('Ensure at least two validators are present', function () {
|
it('should be able to see validator tiles', function () {
|
||||||
assert.isAtLeast(
|
cy.getNodes().then((nodes) => {
|
||||||
this.validators.length,
|
nodes.forEach((node) => {
|
||||||
2,
|
if (node.rankingScore.performanceScore > 0) {
|
||||||
'Ensuring at least two validators exist'
|
cy.get(`[validator-id="${node.id}"]`).should('be.visible');
|
||||||
);
|
} else {
|
||||||
});
|
cy.get(`[validator-id="${node.id}"]`).should('not.exist');
|
||||||
|
}
|
||||||
it('should be able to see validator page sections', function () {
|
|
||||||
cy.get(vegaDataHeader)
|
|
||||||
.contains('Vega data')
|
|
||||||
.and('is.visible')
|
|
||||||
.next()
|
|
||||||
.within(() => {
|
|
||||||
cy.get(jsonSection).should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(tendermintDataHeader)
|
|
||||||
.contains('Tendermint data')
|
|
||||||
.and('is.visible')
|
|
||||||
.next()
|
|
||||||
.within(() => {
|
|
||||||
cy.get(jsonSection).should('not.be.empty');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to see relevant validator information in tendermint section', function () {
|
|
||||||
cy.get(tendermintDataHeader)
|
|
||||||
.contains('Tendermint data')
|
|
||||||
.next()
|
|
||||||
.within(() => {
|
|
||||||
cy.get(jsonSection)
|
|
||||||
.invoke('text')
|
|
||||||
.convert_string_json_to_js_object()
|
|
||||||
.then((validatorsInJson) => {
|
|
||||||
this.validators.forEach((validator, index) => {
|
|
||||||
const validatorInJson =
|
|
||||||
validatorsInJson.result.validators[index];
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
validatorInJson.address,
|
|
||||||
validator.address,
|
|
||||||
`Checking that validator address shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(validator.address).should('be.visible');
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
validatorInJson.pub_key.type,
|
|
||||||
validator.pub_key.type,
|
|
||||||
`Checking that validator public key type shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(validator.pub_key.type).should('be.visible');
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
validatorInJson.pub_key.value,
|
|
||||||
validator.pub_key.value,
|
|
||||||
`Checking that validator public key value shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(validator.pub_key.value).should('be.visible');
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
validatorInJson.voting_power,
|
|
||||||
validator.voting_power,
|
|
||||||
`Checking that validator voting power in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(validator.voting_power).should('be.visible');
|
|
||||||
|
|
||||||
// Proposer priority can change frequently mid test
|
|
||||||
// Therefore only checking the field name is present.
|
|
||||||
cy.contains('proposer_priority').should('be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test disabled 2022/11/15 during the 0.62.1 upgrade. The JSON structure changed, and
|
|
||||||
// this test failed. Rather than fix it, it will be replaced when the validator view displays
|
|
||||||
// something useful rather than just dumping out the JSON.
|
|
||||||
xit('should be able to see relevant node information in vega data section', function () {
|
|
||||||
cy.get(vegaDataHeader)
|
|
||||||
.contains('Vega data')
|
|
||||||
.next()
|
|
||||||
.within(() => {
|
|
||||||
cy.get(jsonSection)
|
|
||||||
.invoke('text')
|
|
||||||
.convert_string_json_to_js_object()
|
|
||||||
.then((nodesInJson) => {
|
|
||||||
this.nodes.forEach((node, index) => {
|
|
||||||
const nodeInJson = nodesInJson.edges[index].node;
|
|
||||||
|
|
||||||
// Vegacapsule shows no info or null for following fields:
|
|
||||||
// name, infoURL, avatarUrl, location, epoch data
|
|
||||||
// Therefore, these values remain unchecked.
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
nodeInJson.__typename,
|
|
||||||
node.__typename,
|
|
||||||
`Checking that node __typename shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(node.__typename).should('be.visible');
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
nodeInJson.id,
|
|
||||||
node.id,
|
|
||||||
`Checking that node id shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(node.id).should('be.visible');
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
nodeInJson.pubkey,
|
|
||||||
node.pubkey,
|
|
||||||
`Checking that node pubkey shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(node.pubkey).should('be.visible');
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
nodeInJson.tmPubkey,
|
|
||||||
node.tmPubkey,
|
|
||||||
`Checking that node tmPubkey shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(node.tmPubkey).should('be.visible');
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
nodeInJson.ethereumAddress,
|
|
||||||
node.ethereumAddress,
|
|
||||||
`Checking that node ethereumAddress shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(node.ethereumAddress).should('be.visible');
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
nodeInJson.stakedByOperator,
|
|
||||||
node.stakedByOperator,
|
|
||||||
`Checking that node stakedByOperator value shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(node.stakedByOperator).should('be.visible');
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
nodeInJson.stakedByDelegates,
|
|
||||||
node.stakedByDelegates,
|
|
||||||
`Checking that node stakedByDelegates value shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(node.stakedByDelegates).should('be.visible');
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
nodeInJson.stakedTotal,
|
|
||||||
node.stakedTotal,
|
|
||||||
`Checking that node stakedTotal shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(node.stakedTotal).should('be.visible');
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
nodeInJson.pendingStake,
|
|
||||||
node.pendingStake,
|
|
||||||
`Checking that node pendingStake shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(node.pendingStake).should('be.visible');
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
nodeInJson.status,
|
|
||||||
node.status,
|
|
||||||
`Checking that node status shown in json matches system data`
|
|
||||||
);
|
|
||||||
cy.contains(node.status).should('be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to see validator page displayed on mobile', function () {
|
|
||||||
cy.common_switch_to_mobile_and_click_toggle();
|
|
||||||
cy.get(validatorMenuHeading).click();
|
|
||||||
cy.get(vegaDataHeader)
|
|
||||||
.contains('Vega data')
|
|
||||||
.and('is.visible')
|
|
||||||
.next()
|
|
||||||
.within(() => {
|
|
||||||
cy.get(jsonSection).should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(tendermintDataHeader)
|
|
||||||
.contains('Tendermint data')
|
|
||||||
.and('is.visible')
|
|
||||||
.next()
|
|
||||||
.within(() => {
|
|
||||||
cy.get(jsonSection).should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(tendermintDataHeader)
|
|
||||||
.contains('Tendermint data')
|
|
||||||
.next()
|
|
||||||
.within(() => {
|
|
||||||
this.validators.forEach((validator) => {
|
|
||||||
cy.contains(validator.address).should('be.visible');
|
|
||||||
cy.contains(validator.pub_key.type).should('be.visible');
|
|
||||||
cy.contains(validator.pub_key.value).should('be.visible');
|
|
||||||
cy.contains(validator.voting_power).should('be.visible');
|
|
||||||
// Proposer priority can change frequently mid test
|
|
||||||
// Therefore only checking the field name is present.
|
|
||||||
cy.contains('proposer_priority').should('be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to switch validator page between light and dark mode', function () {
|
|
||||||
const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)';
|
|
||||||
const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)';
|
|
||||||
const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)';
|
|
||||||
const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)';
|
|
||||||
const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)';
|
|
||||||
const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)';
|
|
||||||
const themeSwitcher = '[data-testid="theme-switcher"]';
|
|
||||||
const jsonFields = '.hljs';
|
|
||||||
const sideMenuBackground = '.absolute';
|
|
||||||
|
|
||||||
// Engage dark mode if not allready set
|
|
||||||
cy.get(sideMenuBackground)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.then((background_color) => {
|
|
||||||
if (background_color.includes(whiteThemeSideMenuBackgroundColor))
|
|
||||||
cy.get(themeSwitcher).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Engage white mode
|
|
||||||
cy.get(themeSwitcher).click();
|
|
||||||
|
|
||||||
// White Mode
|
|
||||||
cy.get(validatorMenuHeading)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', whiteThemeSelectedMenuOptionColor);
|
|
||||||
cy.get(jsonFields)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', whiteThemeJsonFieldBackColor);
|
|
||||||
cy.get(sideMenuBackground)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', whiteThemeSideMenuBackgroundColor);
|
|
||||||
|
|
||||||
// Dark Mode
|
|
||||||
cy.get(themeSwitcher).click();
|
|
||||||
cy.get(validatorMenuHeading)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', darkThemeSelectedMenuOptionColor);
|
|
||||||
cy.get(jsonFields)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', darkThemeJsonFieldBackColor);
|
|
||||||
cy.get(sideMenuBackground)
|
|
||||||
.should('have.css', 'background-color')
|
|
||||||
.and('include', darkThemeSideMenuBackgroundColor);
|
|
||||||
});
|
|
||||||
|
|
||||||
Cypress.Commands.add('get_validators', () => {
|
|
||||||
cy.request({
|
|
||||||
method: 'GET',
|
|
||||||
url: `http://localhost:26617/validators`,
|
|
||||||
headers: { 'content-type': 'application/json' },
|
|
||||||
})
|
|
||||||
.its(`body.result.validators`)
|
|
||||||
.then(function (response) {
|
|
||||||
let validators = [];
|
|
||||||
response.forEach((account, index) => {
|
|
||||||
validators[index] = account;
|
|
||||||
});
|
|
||||||
return validators;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Cypress.Commands.add('get_nodes', () => {
|
|
||||||
const mutation =
|
|
||||||
'{nodesConnection { edges { node { id name infoUrl avatarUrl pubkey tmPubkey ethereumAddress \
|
|
||||||
location stakedByOperator stakedByDelegates stakedTotal pendingStake \
|
|
||||||
epochData { total offline online __typename } status name __typename}}}}';
|
|
||||||
cy.request({
|
|
||||||
method: 'POST',
|
|
||||||
url: `http://localhost:3028/query`,
|
|
||||||
body: {
|
|
||||||
query: mutation,
|
|
||||||
},
|
|
||||||
headers: { 'content-type': 'application/json' },
|
|
||||||
})
|
|
||||||
.its(`body.data.nodesConnection.edges`)
|
|
||||||
.then(function (response) {
|
|
||||||
let nodes = [];
|
|
||||||
response.forEach((node) => {
|
|
||||||
nodes.push(node);
|
|
||||||
});
|
|
||||||
return nodes;
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,10 +21,14 @@ Cypress.Commands.add(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Cypress.Commands.add('switchToMobile', () => {
|
||||||
|
cy.viewport('iphone-x');
|
||||||
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('common_switch_to_mobile_and_click_toggle', function () {
|
Cypress.Commands.add('common_switch_to_mobile_and_click_toggle', function () {
|
||||||
cy.viewport('iphone-x');
|
cy.viewport('iphone-x');
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
cy.get('[data-testid="open-menu"]').click();
|
cy.get('[data-testid="button-menu-drawer"]').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('monitor_clipboard', () => {
|
Cypress.Commands.add('monitor_clipboard', () => {
|
||||||
@@ -84,3 +88,42 @@ Cypress.Commands.add('convert_number_to_max_four_decimal', (number) => {
|
|||||||
maximumFractionDigits: 4,
|
maximumFractionDigits: 4,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add('navigate_to', (page, mobileView = false) => {
|
||||||
|
const navigation = {
|
||||||
|
transactions: '/txs',
|
||||||
|
blocks: '/blocks',
|
||||||
|
oracles: '/oracles',
|
||||||
|
validators: '/validators',
|
||||||
|
parties: '/parties',
|
||||||
|
assets: '/assets',
|
||||||
|
markets: '/markets',
|
||||||
|
governanceProposals: '/governance',
|
||||||
|
networkParameters: '/network-parameters',
|
||||||
|
genesisParameters: '/genesis',
|
||||||
|
};
|
||||||
|
|
||||||
|
const topLevelRoutes = ['transactions', 'blocks', 'oracles', 'validators'];
|
||||||
|
|
||||||
|
cy.getByTestId('navigation').within(() => {
|
||||||
|
if (!topLevelRoutes.includes(page) && mobileView === false) {
|
||||||
|
cy.getByTestId('other').first().click();
|
||||||
|
}
|
||||||
|
cy.get(`[href="${navigation[page]}"]`).first().click();
|
||||||
|
});
|
||||||
|
cy.url().should('include', navigation[page]);
|
||||||
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add('get_element_by_col_id', (elementName) => {
|
||||||
|
cy.get(`[col-id="${elementName}"]`);
|
||||||
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add(
|
||||||
|
'validate_element_from_table',
|
||||||
|
(tableRowName, tableRowValue) => {
|
||||||
|
cy.contains(tableRowName)
|
||||||
|
.parentsUntil("[data-testid='key-value-table-row']")
|
||||||
|
.siblings()
|
||||||
|
.should('have.text', tableRowValue);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
@@ -15,5 +15,6 @@
|
|||||||
|
|
||||||
import '@vegaprotocol/cypress';
|
import '@vegaprotocol/cypress';
|
||||||
import './common.functions.js';
|
import './common.functions.js';
|
||||||
|
import 'cypress-mochawesome-reporter/register';
|
||||||
import registerCypressGrep from '@cypress/grep';
|
import registerCypressGrep from '@cypress/grep';
|
||||||
registerCypressGrep();
|
registerCypressGrep();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"presets": [
|
"presets": [
|
||||||
[
|
[
|
||||||
"@nrwl/react/babel",
|
"@nx/react/babel",
|
||||||
{
|
{
|
||||||
"runtime": "automatic"
|
"runtime": "automatic"
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-9
@@ -1,13 +1,21 @@
|
|||||||
NX_TENDERMINT_URL=https://tm.n00.stagnet3.vega.xyz
|
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n00.stagnet3.vega.xyz/websocket
|
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
|
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.rocks
|
||||||
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
|
||||||
NX_VEGA_ENV=STAGNET3
|
NX_VEGA_ENV=STAGNET1
|
||||||
|
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
||||||
|
NX_VEGA_TOKEN_URL=https://governance.stagnet1.vega.rocks
|
||||||
|
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||||
|
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||||
|
NX_BLOCK_EXPLORER=https://be.stagnet1.vega.rocks/rest
|
||||||
|
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||||
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
|
NX_VEGA_GOVERNANCE_URL=https://governance.stagnet1.vega.rocks
|
||||||
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
|
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
|
||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases/tag/
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_BLOCK_EXPLORER=https://be.stagnet3.vega.xyz/rest
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
|
|
||||||
# App flags
|
# App flags
|
||||||
NX_EXPLORER_ASSETS=1
|
NX_EXPLORER_ASSETS=1
|
||||||
|
|||||||
@@ -1,18 +1,10 @@
|
|||||||
# App configuration variables
|
# App configuration variables
|
||||||
NX_TENDERMINT_URL=http://localhost:26617
|
NX_TENDERMINT_URL=http://localhost:26617
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||||
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
|
|
||||||
NX_VEGA_ENV=CUSTOM
|
NX_VEGA_ENV=CUSTOM
|
||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||||
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||||
|
NX_VEGA_EXPLORER_URL=/
|
||||||
|
|
||||||
# App flags
|
# App flags
|
||||||
NX_EXPLORER_ASSETS=1
|
|
||||||
NX_EXPLORER_GENESIS=1
|
|
||||||
NX_EXPLORER_GOVERNANCE=1
|
|
||||||
NX_EXPLORER_MARKETS=1
|
|
||||||
NX_EXPLORER_ORACLES=1
|
|
||||||
NX_EXPLORER_TXS_LIST=0
|
NX_EXPLORER_TXS_LIST=0
|
||||||
NX_EXPLORER_NETWORK_PARAMETERS=1
|
|
||||||
NX_EXPLORER_PARTIES=1
|
|
||||||
NX_EXPLORER_VALIDATORS=1
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
# App configuration variables
|
# App configuration variables
|
||||||
NX_TENDERMINT_URL=https://n04.d.vega.xyz/tm
|
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
|
||||||
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
|
|
||||||
NX_VEGA_ENV=DEVNET
|
NX_VEGA_ENV=DEVNET
|
||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|
||||||
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
|
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||||
NX_VEGA_GOVERNANCE_URL=https://dev.token.vega.xyz
|
NX_VEGA_GOVERNANCE_URL=https://dev.governance.vega.xyz
|
||||||
|
NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql
|
||||||
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
|
||||||
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
|
NX_VEGA_EXPLORER_URL=/
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
# App configuration variables
|
# App configuration variables
|
||||||
NX_TENDERMINT_URL=https://be.explorer.vega.xyz
|
NX_TENDERMINT_URL=https://be.vega.community
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.explorer.vega.xyz
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mainnet-network.json
|
NX_SENTRY_DSN=https://b3a56b03eda842faad731f3ea9dfd1bc@o286262.ingest.sentry.io/6242427
|
||||||
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||||
|
NX_VEGA_URL=https://api.vega.community/graphql
|
||||||
NX_VEGA_ENV=MAINNET
|
NX_VEGA_ENV=MAINNET
|
||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
NX_BLOCK_EXPLORER=https://be.vega.community/rest
|
||||||
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest/
|
|
||||||
NX_ETHERSCAN_URL=https://etherscan.io
|
NX_ETHERSCAN_URL=https://etherscan.io
|
||||||
NX_VEGA_GOVERNANCE_URL=https://token.vega.xyz
|
NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
|
||||||
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||||
|
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
|
||||||
|
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# App configuration variables
|
||||||
|
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||||
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||||
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
|
||||||
|
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
|
||||||
|
NX_VEGA_ENV=MAINNET_MIRROR
|
||||||
|
NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.rocks/rest
|
||||||
|
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||||
|
NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
|
||||||
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||||
|
NX_VEGA_EXPLORER_URL=https://explorer.mainnet-mirror.vega.rocks/
|
||||||
|
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# App configuration variables
|
|
||||||
NX_TENDERMINT_URL=https://tm.be.mainnet-mirror.vega.xyz
|
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=https://tm.be.mainnet-mirror.vega.xyz
|
|
||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mirror-network.json
|
|
||||||
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET3":"https://stagnet3.token.vega.xyz","TESTNET":"https://token.fairground.wtf","MAINNET":"https://token.vega.xyz"}'
|
|
||||||
NX_VEGA_ENV=MIRROR
|
|
||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|
||||||
NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.xyz/rest/
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_VEGA_GOVERNANCE_URL=https://mainnet-mirror.token.vega.xyz
|
|
||||||
|
|
||||||
# App flags
|
|
||||||
NX_EXPLORER_ASSETS=1
|
|
||||||
NX_EXPLORER_GENESIS=1
|
|
||||||
NX_EXPLORER_GOVERNANCE=1
|
|
||||||
NX_EXPLORER_NETWORK_PARAMETERS=1
|
|
||||||
NX_EXPLORER_PARTIES=1
|
|
||||||
NX_EXPLORER_VALIDATORS=1
|
|
||||||
NX_EXPLORER_MARKETS=0
|
|
||||||
NX_EXPLORER_ORACLES=0
|
|
||||||
NX_EXPLORER_TXS_LIST=1
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# App configuration variables
|
|
||||||
NX_VEGA_ENV=SANDBOX
|
|
||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/sandbox-network.json
|
|
||||||
NX_VEGA_EXPLORER_URL=https://sandbox.explorer.vega.xyz
|
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
|
||||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
|
|
||||||
NX_TENDERMINT_URL=https://tm.n01.sandbox.vega.xyz
|
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.sandbox.vega.xyz/websocket
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_VEGA_GOVERNANCE_URL=https://sandbox.token.vega.xyz
|
|
||||||
@@ -1,14 +1,2 @@
|
|||||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
# .env is stagnet1, so there are no overrides required
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz","STAGNET1":"https://stagnet1.explorer.vega.xyz"}'
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
|
||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet1-network.json
|
|
||||||
NX_VEGA_ENV=STAGNET1
|
|
||||||
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
|
|
||||||
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
|
|
||||||
NX_VEGA_TOKEN_URL=https://stagnet1.token.vega.xyz
|
|
||||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
|
||||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.xyz
|
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
|
||||||
NX_BLOCK_EXPLORER=https://be.stagnet1.vega.xyz/rest
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_VEGA_GOVERNANCE_URL=https://stagnet1.token.vega.xyz
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
NX_TENDERMINT_URL=https://tm.n00.stagnet3.vega.xyz
|
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n00.stagnet3.vega.xyz/websocket
|
|
||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
|
|
||||||
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
|
|
||||||
NX_VEGA_ENV=STAGNET3
|
|
||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|
||||||
NX_BLOCK_EXPLORER=https://be.stagnet3.vega.xyz/rest
|
|
||||||
NX_VEGA_GOVERNANCE_URL=https://stagnet3.token.vega.xyz
|
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
# App configuration variables
|
# App configuration variables
|
||||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||||
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
|
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.be.testnet.vega.xyz/websocket/
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
||||||
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
|
|
||||||
NX_VEGA_ENV=TESTNET
|
NX_VEGA_ENV=TESTNET
|
||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|
||||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||||
NX_VEGA_NETWORKS={}
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||||
NX_VEGA_GOVERNANCE_URL=https://token.fairground.wtf
|
NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
|
||||||
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
|
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||||
|
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# App configuration variables
|
||||||
|
NX_VEGA_ENV=VALIDATOR_TESTNET
|
||||||
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/testnet2/testnet2.toml
|
||||||
|
NX_VEGA_URL=https://api-validators-testnet.vega.rocks/graphql
|
||||||
|
NX_VEGA_REST=https://api-validators-testnet.vega.rocks/
|
||||||
|
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||||
|
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||||
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
|
|
||||||
|
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||||
|
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
|
||||||
|
NX_VEGA_GOVERNANCE_URL=https://governance.validators-testnet.vega.rocks
|
||||||
|
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
|
||||||
|
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
NX_TENDERMINT_URL=http://localhost:26607/
|
NX_TENDERMINT_URL=http://localhost:26607/
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26607/websocket
|
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26607/websocket
|
||||||
NX_VEGA_ENV=CUSTOM
|
NX_VEGA_ENV=CUSTOM
|
||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|
||||||
NX_BLOCK_EXPLORER=
|
NX_BLOCK_EXPLORER=
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||||
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||||
|
NX_VEGA_EXPLORER_URL=/
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
|
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
|
||||||
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
|
"ignorePatterns": ["!**/*", "__generated__"],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
|
|||||||
@@ -32,15 +32,15 @@ yarn nx serve explorer
|
|||||||
Example configurations are provided here:
|
Example configurations are provided here:
|
||||||
|
|
||||||
- [Mainnet](./.env.mainnet)
|
- [Mainnet](./.env.mainnet)
|
||||||
|
- [Mainnet-mirror](./.env.mainnet-mirror)
|
||||||
- [Devnet](./.env.devnet)
|
- [Devnet](./.env.devnet)
|
||||||
- [Capsule](./.env.capsule)
|
- [Capsule](./.env.capsule)
|
||||||
- [Testnet](./.env.testnet)
|
- [Testnet](./.env.testnet)
|
||||||
- [Stagnet3](./.env.stagnet3)
|
|
||||||
|
|
||||||
For convenience, you can boot the app injecting one of the configurations above by running:
|
For convenience, you can boot the app injecting one of the configurations above by running:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yarn nx run explorer:serve --env={env} # e.g. stagnet3
|
yarn env-cmd -f .\apps\explorer\.env.{env} yarn nx run explorer:serve # e.g. stagnet1
|
||||||
```
|
```
|
||||||
|
|
||||||
There are a few different configuration options offered for this app:
|
There are a few different configuration options offered for this app:
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ export default {
|
|||||||
displayName: 'explorer',
|
displayName: 'explorer',
|
||||||
preset: '../../jest.preset.js',
|
preset: '../../jest.preset.js',
|
||||||
transform: {
|
transform: {
|
||||||
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
|
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
|
||||||
'^.+\\.[tj]sx?$': 'babel-jest',
|
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }],
|
||||||
},
|
},
|
||||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||||
coverageDirectory: '../../coverage/apps/explorer',
|
coverageDirectory: '../../coverage/apps/explorer',
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
|
"name": "explorer",
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
"sourceRoot": "apps/explorer/src",
|
"sourceRoot": "apps/explorer/src",
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"targets": {
|
"targets": {
|
||||||
"build": {
|
"build": {
|
||||||
"executor": "./tools/executors/webpack:build",
|
"executor": "@nx/webpack:webpack",
|
||||||
"outputs": ["{options.outputPath}"],
|
"outputs": ["{options.outputPath}"],
|
||||||
"defaultConfiguration": "production",
|
"defaultConfiguration": "production",
|
||||||
"options": {
|
"options": {
|
||||||
@@ -38,7 +39,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
"executor": "./tools/executors/webpack:serve",
|
"executor": "@nx/webpack:dev-server",
|
||||||
"options": {
|
"options": {
|
||||||
"port": 3000,
|
"port": 3000,
|
||||||
"buildTarget": "explorer:build:development",
|
"buildTarget": "explorer:build:development",
|
||||||
@@ -52,30 +53,36 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"executor": "@nrwl/linter:eslint",
|
"executor": "@nx/linter:eslint",
|
||||||
"outputs": ["{options.outputFile}"],
|
"outputs": ["{options.outputFile}"],
|
||||||
"options": {
|
"options": {
|
||||||
"lintFilePatterns": ["apps/explorer/**/*.{ts,tsx,js,jsx}"]
|
"lintFilePatterns": ["apps/explorer/**/*.{ts,tsx,js,jsx}"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"executor": "@nrwl/jest:jest",
|
"executor": "@nx/jest:jest",
|
||||||
"outputs": ["coverage/apps/explorer"],
|
"outputs": ["{workspaceRoot}/coverage/apps/explorer"],
|
||||||
"options": {
|
"options": {
|
||||||
"jestConfig": "apps/explorer/jest.config.ts",
|
"jestConfig": "apps/explorer/jest.config.ts",
|
||||||
"passWithNoTests": true
|
"passWithNoTests": true
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"ci": {
|
||||||
|
"ci": true,
|
||||||
|
"codeCoverage": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"generate-types": {
|
"generate-types": {
|
||||||
"executor": "@nrwl/workspace:run-commands",
|
"executor": "nx:run-commands",
|
||||||
"options": {
|
"options": {
|
||||||
"commands": [
|
"commands": [
|
||||||
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.67.3/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
|
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/spec-update-v0.72.0-preview.2/specs/v0.72.0-preview.2/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"build-netlify": {
|
"build-netlify": {
|
||||||
"executor": "@nrwl/workspace:run-commands",
|
"executor": "nx:run-commands",
|
||||||
"options": {
|
"options": {
|
||||||
"commands": [
|
"commands": [
|
||||||
"cp apps/explorer/netlify.toml netlify.toml",
|
"cp apps/explorer/netlify.toml netlify.toml",
|
||||||
@@ -84,7 +91,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"build-spec": {
|
"build-spec": {
|
||||||
"executor": "@nrwl/workspace:run-commands",
|
"executor": "nx:run-commands",
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"options": {
|
"options": {
|
||||||
"command": "yarn tsc --project ./apps/explorer/tsconfig.spec.json"
|
"command": "yarn tsc --project ./apps/explorer/tsconfig.spec.json"
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
function ReactMarkdown({ children }) {
|
||||||
|
return <div>{children}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReactMarkdown;
|
||||||
@@ -1,62 +1,54 @@
|
|||||||
import classnames from 'classnames';
|
import {
|
||||||
import { useState, useEffect } from 'react';
|
AppFailure,
|
||||||
import { useLocation } from 'react-router-dom';
|
NetworkLoader,
|
||||||
import { EnvironmentProvider, NetworkLoader } from '@vegaprotocol/environment';
|
NodeGuard,
|
||||||
import { Nav } from './components/nav';
|
NodeSwitcherDialog,
|
||||||
import { Header } from './components/header';
|
useEnvironment,
|
||||||
import { Main } from './components/main';
|
useInitializeEnv,
|
||||||
|
useNodeSwitcherStore,
|
||||||
|
} from '@vegaprotocol/environment';
|
||||||
import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider';
|
import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider';
|
||||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Footer } from './components/footer/footer';
|
import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
|
||||||
|
import { RouterProvider } from 'react-router-dom';
|
||||||
|
import { router } from './routes/router-config';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { Suspense } from 'react';
|
||||||
|
|
||||||
|
const splashLoading = (
|
||||||
|
<Splash>
|
||||||
|
<Loader />
|
||||||
|
</Splash>
|
||||||
|
);
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const { VEGA_URL } = useEnvironment();
|
||||||
|
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useNodeSwitcherStore(
|
||||||
const location = useLocation();
|
(store) => [store.dialogOpen, store.setDialogOpen]
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setMenuOpen(false);
|
|
||||||
}, [location]);
|
|
||||||
|
|
||||||
const cacheConfig: InMemoryCacheConfig = {
|
|
||||||
typePolicies: {
|
|
||||||
statistics: {
|
|
||||||
keyFields: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const layoutClasses = classnames(
|
|
||||||
'grid grid-rows-[auto_1fr_auto] grid-cols-[1fr] md:grid-rows-[auto_minmax(700px,_1fr)_auto] md:grid-cols-[300px_1fr]',
|
|
||||||
'min-h-[100vh] mx-auto my-0',
|
|
||||||
'border-neutral-700 dark:border-neutral-300 lg:border-l lg:border-r',
|
|
||||||
'bg-white dark:bg-black',
|
|
||||||
'antialiased text-black dark:text-white',
|
|
||||||
{
|
|
||||||
'h-[100vh] min-h-auto overflow-hidden': menuOpen,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TendermintWebsocketProvider>
|
<TendermintWebsocketProvider>
|
||||||
<NetworkLoader cache={cacheConfig}>
|
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
|
||||||
<div className={layoutClasses}>
|
<NodeGuard
|
||||||
<Header menuOpen={menuOpen} setMenuOpen={setMenuOpen} />
|
skeleton={<div>{t('Loading')}</div>}
|
||||||
<Nav menuOpen={menuOpen} />
|
failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
|
||||||
<Main />
|
>
|
||||||
<Footer />
|
<Suspense fallback={splashLoading}>
|
||||||
</div>
|
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||||
|
</Suspense>
|
||||||
|
</NodeGuard>
|
||||||
|
<NodeSwitcherDialog
|
||||||
|
open={nodeSwitcherOpen}
|
||||||
|
setOpen={setNodeSwitcherOpen}
|
||||||
|
/>
|
||||||
</NetworkLoader>
|
</NetworkLoader>
|
||||||
</TendermintWebsocketProvider>
|
</TendermintWebsocketProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Wrapper = () => {
|
const Wrapper = () => {
|
||||||
return (
|
useInitializeEnv();
|
||||||
<EnvironmentProvider>
|
return <App />;
|
||||||
<App />
|
|
||||||
</EnvironmentProvider>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Wrapper;
|
export default Wrapper;
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { addDecimalsFormatNumber } from '@vegaprotocol/react-helpers';
|
import { useAssetDataProvider } from '@vegaprotocol/assets';
|
||||||
|
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||||
import { AssetLink } from '../links';
|
import { AssetLink } from '../links';
|
||||||
import { useExplorerAssetQuery } from '../links/asset-link/__generated__/Asset';
|
|
||||||
|
|
||||||
export type AssetBalanceProps = {
|
export type AssetBalanceProps = {
|
||||||
assetId: string;
|
assetId: string;
|
||||||
price: string;
|
price: string;
|
||||||
showAssetLink?: boolean;
|
showAssetLink?: boolean;
|
||||||
|
showAssetSymbol?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,21 +17,20 @@ const AssetBalance = ({
|
|||||||
assetId,
|
assetId,
|
||||||
price,
|
price,
|
||||||
showAssetLink = true,
|
showAssetLink = true,
|
||||||
|
showAssetSymbol = false,
|
||||||
}: AssetBalanceProps) => {
|
}: AssetBalanceProps) => {
|
||||||
const { data } = useExplorerAssetQuery({
|
const { data: asset, loading } = useAssetDataProvider(assetId);
|
||||||
variables: { id: assetId },
|
|
||||||
});
|
|
||||||
|
|
||||||
const label =
|
const label =
|
||||||
data && data.asset?.decimals
|
!loading && asset && asset.decimals
|
||||||
? addDecimalsFormatNumber(price, data.asset.decimals)
|
? addDecimalsFormatNumber(price, asset.decimals)
|
||||||
: price;
|
: price;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="inline-block">
|
<div className="inline-block">
|
||||||
<span>{label}</span>{' '}
|
<span>{label}</span>{' '}
|
||||||
{showAssetLink && data?.asset?.id ? (
|
{showAssetLink && asset?.id ? (
|
||||||
<AssetLink id={data.asset.id} />
|
<AssetLink showAssetSymbol={showAssetSymbol} assetId={assetId} />
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { addDecimalsFormatNumber } from '@vegaprotocol/react-helpers';
|
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||||
import { useExplorerGovernanceAssetQuery } from './__generated__/Governance-asset';
|
import { useExplorerGovernanceAssetQuery } from './__generated__/Governance-asset';
|
||||||
import AssetBalance from './asset-balance';
|
import AssetBalance from './asset-balance';
|
||||||
|
|
||||||
@@ -13,11 +13,17 @@ const DEFAULT_DECIMALS = 18;
|
|||||||
* the governance asset first, which is set by a network parameter
|
* the governance asset first, which is set by a network parameter
|
||||||
*/
|
*/
|
||||||
const GovernanceAssetBalance = ({ price }: GovernanceAssetBalanceProps) => {
|
const GovernanceAssetBalance = ({ price }: GovernanceAssetBalanceProps) => {
|
||||||
const { data } = useExplorerGovernanceAssetQuery();
|
const { data, loading } = useExplorerGovernanceAssetQuery();
|
||||||
|
|
||||||
if (data && data.networkParameter?.value) {
|
if (!loading && data && data.networkParameter?.value) {
|
||||||
const governanceAssetId = data.networkParameter.value;
|
const governanceAssetId = data.networkParameter.value;
|
||||||
return <AssetBalance price={price} assetId={governanceAssetId} />;
|
return (
|
||||||
|
<AssetBalance
|
||||||
|
price={price}
|
||||||
|
showAssetSymbol={true}
|
||||||
|
assetId={governanceAssetId}
|
||||||
|
/>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div className="inline-block">
|
<div className="inline-block">
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { render, waitFor } from '@testing-library/react';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
import { assetsList } from '../../mocks/assets';
|
||||||
|
import { AssetsTable } from './assets-table';
|
||||||
|
|
||||||
|
describe('AssetsTable', () => {
|
||||||
|
it('shows loading message on first render', async () => {
|
||||||
|
const res = render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<AssetsTable data={null} />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
expect(await res.findByText('Loading...')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows no data message if no assets found', async () => {
|
||||||
|
const res = render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<AssetsTable data={[]} />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
await res.findByText('This chain has no assets')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows a table/list with all the assets', async () => {
|
||||||
|
const res = render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<AssetsTable data={assetsList} />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
await waitFor(() => {
|
||||||
|
const rowA1 = res.container.querySelector('[row-id="123"]');
|
||||||
|
expect(rowA1).toBeInTheDocument();
|
||||||
|
|
||||||
|
const rowA2 = res.container.querySelector('[row-id="456"]');
|
||||||
|
expect(rowA2).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||||
|
import { AssetTypeMapping, AssetStatusMapping } from '@vegaprotocol/assets';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||||
|
import type { VegaICellRendererParams } from '@vegaprotocol/datagrid';
|
||||||
|
import { useRef, useLayoutEffect } from 'react';
|
||||||
|
import { BREAKPOINT_MD } from '../../config/breakpoints';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import type { RowClickedEvent, ColDef } from 'ag-grid-community';
|
||||||
|
|
||||||
|
type AssetsTableProps = {
|
||||||
|
data: AssetFieldsFragment[] | null;
|
||||||
|
};
|
||||||
|
export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const ref = useRef<AgGridReact>(null);
|
||||||
|
const showColumnsOnDesktop = () => {
|
||||||
|
ref.current?.columnApi.setColumnsVisible(
|
||||||
|
['id', 'type', 'status'],
|
||||||
|
window.innerWidth > BREAKPOINT_MD
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
window.addEventListener('resize', showColumnsOnDesktop);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', showColumnsOnDesktop);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const columnDefs = useMemo<ColDef[]>(
|
||||||
|
() => [
|
||||||
|
{ headerName: t('Symbol'), field: 'symbol' },
|
||||||
|
{ headerName: t('Name'), field: 'name' },
|
||||||
|
{
|
||||||
|
flex: 2,
|
||||||
|
headerName: t('ID'),
|
||||||
|
field: 'id',
|
||||||
|
hide: window.innerWidth < BREAKPOINT_MD,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'type',
|
||||||
|
headerName: t('Type'),
|
||||||
|
field: 'source.__typename',
|
||||||
|
hide: window.innerWidth < BREAKPOINT_MD,
|
||||||
|
valueFormatter: ({ value }: { value?: string }) =>
|
||||||
|
value ? AssetTypeMapping[value].value : '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headerName: t('Status'),
|
||||||
|
field: 'status',
|
||||||
|
hide: window.innerWidth < BREAKPOINT_MD,
|
||||||
|
valueFormatter: ({ value }: { value?: string }) =>
|
||||||
|
value ? AssetStatusMapping[value].value : '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colId: 'actions',
|
||||||
|
headerName: '',
|
||||||
|
sortable: false,
|
||||||
|
filter: false,
|
||||||
|
resizable: false,
|
||||||
|
wrapText: true,
|
||||||
|
field: 'id',
|
||||||
|
cellRenderer: ({
|
||||||
|
value,
|
||||||
|
}: VegaICellRendererParams<AssetFieldsFragment, 'id'>) =>
|
||||||
|
value ? (
|
||||||
|
<ButtonLink
|
||||||
|
onClick={(e) => {
|
||||||
|
navigate(value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('View details')}
|
||||||
|
</ButtonLink>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[navigate]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AgGrid
|
||||||
|
ref={ref}
|
||||||
|
rowData={data}
|
||||||
|
getRowId={({ data }: { data: AssetFieldsFragment }) => data.id}
|
||||||
|
overlayNoRowsTemplate={t('This chain has no assets')}
|
||||||
|
domLayout="autoHeight"
|
||||||
|
defaultColDef={{
|
||||||
|
flex: 1,
|
||||||
|
resizable: true,
|
||||||
|
sortable: true,
|
||||||
|
filter: true,
|
||||||
|
filterParams: { buttons: ['reset'] },
|
||||||
|
autoHeight: true,
|
||||||
|
}}
|
||||||
|
columnDefs={columnDefs}
|
||||||
|
suppressCellFocus={true}
|
||||||
|
onRowClicked={({ data }: RowClickedEvent) => {
|
||||||
|
navigate(data.id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -2,11 +2,11 @@ import React from 'react';
|
|||||||
import type { BlockMeta } from '../../routes/blocks/tendermint-blockchain-response';
|
import type { BlockMeta } from '../../routes/blocks/tendermint-blockchain-response';
|
||||||
import { Routes } from '../../routes/route-names';
|
import { Routes } from '../../routes/route-names';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
|
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||||
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||||
import { TimeAgo } from '../time-ago';
|
import { TimeAgo } from '../time-ago';
|
||||||
import { TableWithTbody, TableRow, TableCell } from '../table';
|
import { TableWithTbody, TableRow, TableCell } from '../table';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
interface BlockProps {
|
interface BlockProps {
|
||||||
block: BlockMeta;
|
block: BlockMeta;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { TendermintBlockchainResponse } from '../../routes/blocks/tendermint-blockchain-response';
|
import type { TendermintBlockchainResponse } from '../../routes/blocks/tendermint-blockchain-response';
|
||||||
import { BlockData } from './block-data';
|
import { BlockData } from './block-data';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FixedSizeList as List } from 'react-window';
|
import { FixedSizeList as List } from 'react-window';
|
||||||
import InfiniteLoader from 'react-window-infinite-loader';
|
import InfiniteLoader from 'react-window-infinite-loader';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import type { BlockMeta } from '../../routes/blocks/tendermint-blockchain-response';
|
import type { BlockMeta } from '../../routes/blocks/tendermint-blockchain-response';
|
||||||
import { BlockData } from './block-data';
|
import { BlockData } from './block-data';
|
||||||
import EmptyList from '../empty-list/empty-list';
|
import EmptyList from '../empty-list/empty-list';
|
||||||
|
|||||||
@@ -1,29 +1,13 @@
|
|||||||
import WS from 'jest-websocket-mock';
|
import WS from 'jest-websocket-mock';
|
||||||
import useWebSocket from 'react-use-websocket';
|
import { render, screen } from '@testing-library/react';
|
||||||
import {
|
|
||||||
render,
|
|
||||||
screen,
|
|
||||||
fireEvent,
|
|
||||||
act,
|
|
||||||
waitFor,
|
|
||||||
} from '@testing-library/react';
|
|
||||||
import { TendermintWebsocketContext } from '../../contexts/websocket/tendermint-websocket-context';
|
|
||||||
import { BlocksRefetch } from './blocks-refetch';
|
import { BlocksRefetch } from './blocks-refetch';
|
||||||
|
|
||||||
const BlocksRefetchInWebsocketProvider = ({
|
const BlocksRefetchInWebsocketProvider = ({
|
||||||
callback,
|
callback,
|
||||||
mocketLocation,
|
|
||||||
}: {
|
}: {
|
||||||
callback: () => null;
|
callback: () => null;
|
||||||
mocketLocation: string;
|
|
||||||
}) => {
|
}) => {
|
||||||
const contextShape = useWebSocket(mocketLocation);
|
return <BlocksRefetch refetch={callback} />;
|
||||||
|
|
||||||
return (
|
|
||||||
<TendermintWebsocketContext.Provider value={{ ...contextShape }}>
|
|
||||||
<BlocksRefetch refetch={callback} />
|
|
||||||
</TendermintWebsocketContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Blocks refetch', () => {
|
describe('Blocks refetch', () => {
|
||||||
@@ -32,111 +16,8 @@ describe('Blocks refetch', () => {
|
|||||||
const mocket = new WS(mocketLocation, { jsonProtocol: true });
|
const mocket = new WS(mocketLocation, { jsonProtocol: true });
|
||||||
new WebSocket(mocketLocation);
|
new WebSocket(mocketLocation);
|
||||||
|
|
||||||
render(
|
render(<BlocksRefetchInWebsocketProvider callback={() => null} />);
|
||||||
<BlocksRefetchInWebsocketProvider
|
|
||||||
callback={() => null}
|
|
||||||
mocketLocation={mocketLocation}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
await mocket.connected;
|
|
||||||
expect(screen.getByTestId('new-blocks')).toHaveTextContent('new blocks');
|
|
||||||
expect(screen.getByTestId('refresh')).toBeInTheDocument();
|
expect(screen.getByTestId('refresh')).toBeInTheDocument();
|
||||||
mocket.close();
|
mocket.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should initiate callback when the button is clicked', async () => {
|
|
||||||
const mocketLocation = 'wss:localhost:3003';
|
|
||||||
const mocket = new WS(mocketLocation, { jsonProtocol: true });
|
|
||||||
new WebSocket(mocketLocation);
|
|
||||||
|
|
||||||
const callback = jest.fn();
|
|
||||||
render(
|
|
||||||
<BlocksRefetchInWebsocketProvider
|
|
||||||
callback={callback}
|
|
||||||
mocketLocation={mocketLocation}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
await mocket.connected;
|
|
||||||
const button = screen.getByTestId('refresh');
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
fireEvent.click(button);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(callback.mock.calls.length).toEqual(1);
|
|
||||||
mocket.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show new blocks as websocket is correctly updated', async () => {
|
|
||||||
const mocketLocation = 'wss:localhost:3004';
|
|
||||||
const mocket = new WS(mocketLocation, { jsonProtocol: true });
|
|
||||||
new WebSocket(mocketLocation);
|
|
||||||
render(
|
|
||||||
<BlocksRefetchInWebsocketProvider
|
|
||||||
callback={() => null}
|
|
||||||
mocketLocation={mocketLocation}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
await mocket.connected;
|
|
||||||
|
|
||||||
// Ensuring we send an ID equal to the one the client subscribed with.
|
|
||||||
await waitFor(() => expect(mocket.messages.length).toEqual(1));
|
|
||||||
// @ts-ignore id on messages
|
|
||||||
const id = mocket.messages[0].id;
|
|
||||||
|
|
||||||
const newBlockMessage = {
|
|
||||||
id,
|
|
||||||
result: {
|
|
||||||
query: "tm.event = 'NewBlock'",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(screen.getByTestId('new-blocks')).toHaveTextContent('0 new blocks');
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
mocket.send(newBlockMessage);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(screen.getByTestId('new-blocks')).toHaveTextContent('1 new blocks');
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
mocket.send(newBlockMessage);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(screen.getByTestId('new-blocks')).toHaveTextContent('2 new blocks');
|
|
||||||
mocket.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('will not show new blocks if websocket has wrong ID', async () => {
|
|
||||||
const mocketLocation = 'wss:localhost:3005';
|
|
||||||
const mocket = new WS(mocketLocation, { jsonProtocol: true });
|
|
||||||
new WebSocket(mocketLocation);
|
|
||||||
|
|
||||||
render(
|
|
||||||
<BlocksRefetchInWebsocketProvider
|
|
||||||
callback={() => null}
|
|
||||||
mocketLocation={mocketLocation}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
await mocket.connected;
|
|
||||||
|
|
||||||
// Ensuring we send an ID equal to the one the client subscribed with.
|
|
||||||
await waitFor(() => expect(mocket.messages.length).toEqual(1));
|
|
||||||
|
|
||||||
const newBlockMessageBadId = {
|
|
||||||
id: 'blahblahblah',
|
|
||||||
result: {
|
|
||||||
query: "tm.event = 'NewBlock'",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(screen.getByTestId('new-blocks')).toHaveTextContent('0 new blocks');
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
mocket.send(newBlockMessageBadId);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(screen.getByTestId('new-blocks')).toHaveTextContent('0 new blocks');
|
|
||||||
mocket.close();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,36 +1,19 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useTendermintWebsocket } from '../../hooks/use-tendermint-websocket';
|
import { Button, Icon } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
|
||||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
|
||||||
|
|
||||||
interface BlocksRefetchProps {
|
interface BlocksRefetchProps {
|
||||||
refetch: () => void;
|
refetch: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BlocksRefetch = ({ refetch }: BlocksRefetchProps) => {
|
export const BlocksRefetch = ({ refetch }: BlocksRefetchProps) => {
|
||||||
const [blocksToLoad, setBlocksToLoad] = useState<number>(0);
|
|
||||||
|
|
||||||
const { messages } = useTendermintWebsocket({
|
|
||||||
query: "tm.event = 'NewBlock'",
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (messages.length > 0) {
|
|
||||||
setBlocksToLoad((prev) => prev + 1);
|
|
||||||
}
|
|
||||||
}, [messages]);
|
|
||||||
|
|
||||||
const refresh = () => {
|
const refresh = () => {
|
||||||
refetch();
|
refetch();
|
||||||
setBlocksToLoad(0);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-4">
|
<Button onClick={refresh} data-testid="refresh" size="xs">
|
||||||
<span data-testid="new-blocks">{blocksToLoad} new blocks - </span>
|
<Icon name="refresh" className="!align-baseline mr-2" size={3} />
|
||||||
<ButtonLink onClick={refresh} data-testid="refresh">
|
{t('Load new')}
|
||||||
{t('refresh to see latest')}
|
</Button>
|
||||||
</ButtonLink>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
Icon,
|
||||||
|
SyntaxHighlighter,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
|
type JsonViewerDialogProps = {
|
||||||
|
title: string;
|
||||||
|
content: unknown;
|
||||||
|
open: boolean;
|
||||||
|
onChange: (isOpen: boolean) => void;
|
||||||
|
trigger?: HTMLElement;
|
||||||
|
};
|
||||||
|
export const JsonViewerDialog = ({
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
open,
|
||||||
|
onChange,
|
||||||
|
trigger,
|
||||||
|
}: JsonViewerDialogProps) => {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
size="medium"
|
||||||
|
title={title}
|
||||||
|
icon={<Icon name="info-sign"></Icon>}
|
||||||
|
open={open}
|
||||||
|
onChange={(isOpen) => onChange(isOpen)}
|
||||||
|
onCloseAutoFocus={(e) => {
|
||||||
|
/**
|
||||||
|
* This mimics radix's default behaviour that focuses the dialog's
|
||||||
|
* trigger after closing itself
|
||||||
|
*/
|
||||||
|
if (trigger) {
|
||||||
|
e.preventDefault();
|
||||||
|
trigger.focus();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="pr-8 mb-8 max-h-[70vh] overflow-y-scroll">
|
||||||
|
<SyntaxHighlighter size="smaller" data={content} />
|
||||||
|
</div>
|
||||||
|
<div className="w-1/4">
|
||||||
|
<Button
|
||||||
|
data-testid="close-asset-details-dialog"
|
||||||
|
fill={true}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => onChange(false)}
|
||||||
|
>
|
||||||
|
{t('Close')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -19,12 +19,12 @@ const EmptyList = ({ heading, label }: EmptyListProps) => {
|
|||||||
|
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
{heading ? (
|
{heading ? (
|
||||||
<h1 className="font-alpha text-xl uppercase text-center leading-relaxed">
|
<h1 className="font-alpha calt text-xl uppercase text-center leading-relaxed">
|
||||||
{heading}
|
{heading}
|
||||||
</h1>
|
</h1>
|
||||||
) : null}
|
) : null}
|
||||||
{label ? (
|
{label ? (
|
||||||
<p className="font-alpha text-gray-500 text-center">{label}</p>
|
<p className="font-alpha calt text-gray-500 text-center">{label}</p>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
query ExplorerEpoch($id: ID!) {
|
||||||
|
epoch(id: $id) {
|
||||||
|
id
|
||||||
|
timestamps {
|
||||||
|
start
|
||||||
|
end
|
||||||
|
firstBlock
|
||||||
|
lastBlock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query ExplorerFutureEpoch {
|
||||||
|
networkParameter(key: "validators.epoch.length") {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
epoch {
|
||||||
|
id
|
||||||
|
timestamps {
|
||||||
|
start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import * as Types from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
import { gql } from '@apollo/client';
|
||||||
|
import * as Apollo from '@apollo/client';
|
||||||
|
const defaultOptions = {} as const;
|
||||||
|
export type ExplorerEpochQueryVariables = Types.Exact<{
|
||||||
|
id: Types.Scalars['ID'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type ExplorerEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, firstBlock: string, lastBlock?: string | null } } };
|
||||||
|
|
||||||
|
export type ExplorerFutureEpochQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
|
export type ExplorerFutureEpochQuery = { __typename?: 'Query', networkParameter?: { __typename?: 'NetworkParameter', value: string } | null, epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null } } };
|
||||||
|
|
||||||
|
|
||||||
|
export const ExplorerEpochDocument = gql`
|
||||||
|
query ExplorerEpoch($id: ID!) {
|
||||||
|
epoch(id: $id) {
|
||||||
|
id
|
||||||
|
timestamps {
|
||||||
|
start
|
||||||
|
end
|
||||||
|
firstBlock
|
||||||
|
lastBlock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useExplorerEpochQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useExplorerEpochQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useExplorerEpochQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useExplorerEpochQuery({
|
||||||
|
* variables: {
|
||||||
|
* id: // value for 'id'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useExplorerEpochQuery(baseOptions: Apollo.QueryHookOptions<ExplorerEpochQuery, ExplorerEpochQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<ExplorerEpochQuery, ExplorerEpochQueryVariables>(ExplorerEpochDocument, options);
|
||||||
|
}
|
||||||
|
export function useExplorerEpochLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerEpochQuery, ExplorerEpochQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<ExplorerEpochQuery, ExplorerEpochQueryVariables>(ExplorerEpochDocument, options);
|
||||||
|
}
|
||||||
|
export type ExplorerEpochQueryHookResult = ReturnType<typeof useExplorerEpochQuery>;
|
||||||
|
export type ExplorerEpochLazyQueryHookResult = ReturnType<typeof useExplorerEpochLazyQuery>;
|
||||||
|
export type ExplorerEpochQueryResult = Apollo.QueryResult<ExplorerEpochQuery, ExplorerEpochQueryVariables>;
|
||||||
|
export const ExplorerFutureEpochDocument = gql`
|
||||||
|
query ExplorerFutureEpoch {
|
||||||
|
networkParameter(key: "validators.epoch.length") {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
epoch {
|
||||||
|
id
|
||||||
|
timestamps {
|
||||||
|
start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useExplorerFutureEpochQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `useExplorerFutureEpochQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useExplorerFutureEpochQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = useExplorerFutureEpochQuery({
|
||||||
|
* variables: {
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useExplorerFutureEpochQuery(baseOptions?: Apollo.QueryHookOptions<ExplorerFutureEpochQuery, ExplorerFutureEpochQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<ExplorerFutureEpochQuery, ExplorerFutureEpochQueryVariables>(ExplorerFutureEpochDocument, options);
|
||||||
|
}
|
||||||
|
export function useExplorerFutureEpochLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerFutureEpochQuery, ExplorerFutureEpochQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<ExplorerFutureEpochQuery, ExplorerFutureEpochQueryVariables>(ExplorerFutureEpochDocument, options);
|
||||||
|
}
|
||||||
|
export type ExplorerFutureEpochQueryHookResult = ReturnType<typeof useExplorerFutureEpochQuery>;
|
||||||
|
export type ExplorerFutureEpochLazyQueryHookResult = ReturnType<typeof useExplorerFutureEpochLazyQuery>;
|
||||||
|
export type ExplorerFutureEpochQueryResult = Apollo.QueryResult<ExplorerFutureEpochQuery, ExplorerFutureEpochQueryVariables>;
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import { IconForEpoch } from './epoch';
|
||||||
|
|
||||||
|
const THE_PAST = 'Monday, 17 February 2022 11:44:09';
|
||||||
|
const THE_FUTURE = 'Monday, 17 February 3023 11:44:09';
|
||||||
|
|
||||||
|
describe('IconForEpoch', () => {
|
||||||
|
it('Handles malformed dates', () => {
|
||||||
|
const start = 'This is n0t a d4te';
|
||||||
|
const end = '📅';
|
||||||
|
const screen = render(<IconForEpoch start={start} end={end} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('img')).toHaveAttribute(
|
||||||
|
'aria-label',
|
||||||
|
'calendar icon'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('defaults to a calendar icon', () => {
|
||||||
|
const start = null as unknown as string;
|
||||||
|
const end = null as unknown as string;
|
||||||
|
const screen = render(<IconForEpoch start={start} end={end} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('img')).toHaveAttribute(
|
||||||
|
'aria-label',
|
||||||
|
'calendar icon'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('if start and end are both in the future, stick with calendar', () => {
|
||||||
|
const screen = render(<IconForEpoch start={THE_FUTURE} end={THE_FUTURE} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('img')).toHaveAttribute(
|
||||||
|
'aria-label',
|
||||||
|
'calendar icon'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('if start is in the past and end is in the future, this is currently active', () => {
|
||||||
|
const screen = render(<IconForEpoch start={THE_PAST} end={THE_FUTURE} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('img')).toHaveAttribute(
|
||||||
|
'aria-label',
|
||||||
|
'circle icon'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('if start and end are in the paste, this is done', () => {
|
||||||
|
const screen = render(<IconForEpoch start={THE_PAST} end={THE_PAST} />);
|
||||||
|
|
||||||
|
expect(screen.getByRole('img')).toHaveAttribute(
|
||||||
|
'aria-label',
|
||||||
|
'tick-circle icon'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import EpochMissingOverview, { calculateEpochData } from './epoch-missing';
|
||||||
|
import { getSecondsFromInterval } from '@vegaprotocol/utils';
|
||||||
|
const START_DATE_PAST = 'Monday, 17 February 2022 11:44:09';
|
||||||
|
|
||||||
|
describe('getSecondsFromInterval', () => {
|
||||||
|
it('returns 0 for bad data', () => {
|
||||||
|
expect(getSecondsFromInterval(null as unknown as string)).toEqual(0);
|
||||||
|
expect(getSecondsFromInterval('')).toEqual(0);
|
||||||
|
expect(getSecondsFromInterval('🧙')).toEqual(0);
|
||||||
|
expect(getSecondsFromInterval(2 as unknown as string)).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses out months from a capital M', () => {
|
||||||
|
expect(getSecondsFromInterval('2M')).toEqual(5184000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses out days from a capital D', () => {
|
||||||
|
expect(getSecondsFromInterval('1D')).toEqual(86400);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses out hours from a lower case h', () => {
|
||||||
|
expect(getSecondsFromInterval('11h')).toEqual(39600);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses out minutes from a lower case m', () => {
|
||||||
|
expect(getSecondsFromInterval('10m')).toEqual(600);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses out seconds from a lower case s', () => {
|
||||||
|
expect(getSecondsFromInterval('99s')).toEqual(99);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses complex examples', () => {
|
||||||
|
expect(getSecondsFromInterval('24h')).toEqual(86400);
|
||||||
|
expect(getSecondsFromInterval('1h30m')).toEqual(5400);
|
||||||
|
expect(getSecondsFromInterval('1D1h30m1s')).toEqual(91801);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('calculateEpochData', () => {
|
||||||
|
it('Handles bad data', () => {
|
||||||
|
const currentEpochId = null as unknown as string;
|
||||||
|
const missingEpochId = null as unknown as string;
|
||||||
|
const epochStart = null as unknown as string;
|
||||||
|
const epochLength = null as unknown as string;
|
||||||
|
const res = calculateEpochData(
|
||||||
|
currentEpochId,
|
||||||
|
missingEpochId,
|
||||||
|
epochStart,
|
||||||
|
epochLength
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res).toHaveProperty('label', 'Missing data');
|
||||||
|
expect(res).toHaveProperty('isInFuture', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Calculates that a bigger epoch number is in the future from basic data', () => {
|
||||||
|
const currentEpochId = '10';
|
||||||
|
const missingEpochId = '20';
|
||||||
|
const epochStart = '';
|
||||||
|
const epochLength = '';
|
||||||
|
const res = calculateEpochData(
|
||||||
|
currentEpochId,
|
||||||
|
missingEpochId,
|
||||||
|
epochStart,
|
||||||
|
epochLength
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(res).toHaveProperty('isInFuture', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('If it has an epoch length and a start time, it provides an estimate', () => {
|
||||||
|
const currentEpochId = '10';
|
||||||
|
const missingEpochId = '20';
|
||||||
|
const epochStart = START_DATE_PAST;
|
||||||
|
const epochLength = '1s';
|
||||||
|
const res = calculateEpochData(
|
||||||
|
currentEpochId,
|
||||||
|
missingEpochId,
|
||||||
|
epochStart,
|
||||||
|
epochLength
|
||||||
|
);
|
||||||
|
|
||||||
|
// 'Estimate: 17/02/2022, 11:44:19 - in less than a minute')
|
||||||
|
expect(res).toHaveProperty('label');
|
||||||
|
expect(res.label).toMatch(/^Estimate/);
|
||||||
|
expect(res.label).toMatch(/in less than a minute$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Provide decent string for past', () => {
|
||||||
|
const currentEpochId = '20';
|
||||||
|
const missingEpochId = '10';
|
||||||
|
const epochStart = START_DATE_PAST;
|
||||||
|
const epochLength = '1s';
|
||||||
|
const res = calculateEpochData(
|
||||||
|
currentEpochId,
|
||||||
|
missingEpochId,
|
||||||
|
epochStart,
|
||||||
|
epochLength
|
||||||
|
);
|
||||||
|
|
||||||
|
// 'Estimate: 17/02/2022, 11:44:19 - in less than a minute')
|
||||||
|
expect(res).toHaveProperty('label');
|
||||||
|
expect(res.label).toMatch(/^Estimate/);
|
||||||
|
expect(res.label).toMatch(/less than a minute ago$/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('EpochMissingOverview', () => {
|
||||||
|
function renderComponent(missingEpochId: string) {
|
||||||
|
return render(
|
||||||
|
<MockedProvider>
|
||||||
|
<EpochMissingOverview missingEpochId={missingEpochId} />
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('renders a - if no id is provided', () => {
|
||||||
|
const n = null as unknown as string;
|
||||||
|
const screen = renderComponent(n);
|
||||||
|
expect(screen.getByTestId('empty')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import { useExplorerFutureEpochQuery } from './__generated__/Epoch';
|
||||||
|
|
||||||
|
import addSeconds from 'date-fns/addSeconds';
|
||||||
|
import formatDistance from 'date-fns/formatDistance';
|
||||||
|
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import isFuture from 'date-fns/isFuture';
|
||||||
|
import { getSecondsFromInterval, isValidDate } from '@vegaprotocol/utils';
|
||||||
|
|
||||||
|
export type EpochMissingOverviewProps = {
|
||||||
|
missingEpochId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a set of details for an epoch that has no representation in the
|
||||||
|
* data node. This is primarily for one of two reasons:
|
||||||
|
*
|
||||||
|
* 1. The epoch hasn't happened yet
|
||||||
|
* 2. The epoch happened before a snapshot, and thus the details don't exist
|
||||||
|
*
|
||||||
|
* This component is used when the API has responded with no data for an epoch
|
||||||
|
* by ID, so we already know that we can't display start time/block etc.
|
||||||
|
*
|
||||||
|
* We can detect 1 if the epoch is a higher number than the current epoch
|
||||||
|
* We can detect 2 if the epoch is in the past, but we still get no response.
|
||||||
|
*/
|
||||||
|
const EpochMissingOverview = ({
|
||||||
|
missingEpochId,
|
||||||
|
}: EpochMissingOverviewProps) => {
|
||||||
|
const { data, error, loading } = useExplorerFutureEpochQuery();
|
||||||
|
|
||||||
|
// This should not happen, but it's easily handled
|
||||||
|
if (!missingEpochId) {
|
||||||
|
return <span data-testid="empty">-</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No data should also not happen - we've requested the current epoch. This
|
||||||
|
// could happen at chain restart, but shouldn't. If it does, fallback.
|
||||||
|
if (!data || loading || error) {
|
||||||
|
return <span data-testid="empty">{missingEpochId}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have enough information to predict a future or past block time, let's do it
|
||||||
|
if (
|
||||||
|
!missingEpochId ||
|
||||||
|
!data.epoch.id ||
|
||||||
|
!data.epoch.timestamps.start ||
|
||||||
|
!data?.networkParameter?.value
|
||||||
|
) {
|
||||||
|
return <span data-testid="empty">{missingEpochId}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { label, isInFuture } = calculateEpochData(
|
||||||
|
data.epoch.id,
|
||||||
|
missingEpochId,
|
||||||
|
data.epoch.timestamps.start,
|
||||||
|
data.networkParameter.value
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip description={<p className="text-xs m-2">{label}</p>}>
|
||||||
|
<p>
|
||||||
|
{isInFuture ? (
|
||||||
|
<Icon name="calendar" className="mr-1" />
|
||||||
|
) : (
|
||||||
|
<Icon name="outdated" className="mr-1" />
|
||||||
|
)}
|
||||||
|
{missingEpochId}
|
||||||
|
</p>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function calculateEpochData(
|
||||||
|
currentEpochId: string,
|
||||||
|
missingEpochId: string,
|
||||||
|
epochStart: string,
|
||||||
|
epochLength: string
|
||||||
|
) {
|
||||||
|
// Blank string will be return 0 seconds from getSecondsFromInterval
|
||||||
|
const epochLengthInSeconds = getSecondsFromInterval(epochLength);
|
||||||
|
|
||||||
|
if (!epochStart || !epochLength) {
|
||||||
|
// Let's just take a guess
|
||||||
|
return {
|
||||||
|
label: 'Missing data',
|
||||||
|
isInFuture: parseInt(missingEpochId) > parseInt(currentEpochId),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const startFrom = new Date(epochStart);
|
||||||
|
|
||||||
|
const diff = parseInt(missingEpochId) - parseInt(currentEpochId);
|
||||||
|
const futureDate = addSeconds(startFrom, diff * epochLengthInSeconds);
|
||||||
|
|
||||||
|
const label =
|
||||||
|
isValidDate(futureDate) && isValidDate(startFrom)
|
||||||
|
? `Estimate: ${futureDate.toLocaleString()} - ${formatDistance(
|
||||||
|
futureDate,
|
||||||
|
startFrom,
|
||||||
|
{ addSuffix: true }
|
||||||
|
)}`
|
||||||
|
: 'Missing data';
|
||||||
|
|
||||||
|
return {
|
||||||
|
label,
|
||||||
|
isInFuture: isFuture(futureDate),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EpochMissingOverview;
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
import { useExplorerEpochQuery } from './__generated__/Epoch';
|
||||||
|
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { BlockLink } from '../links';
|
||||||
|
import { Time } from '../time';
|
||||||
|
import { TimeAgo } from '../time-ago';
|
||||||
|
import EpochMissingOverview from './epoch-missing';
|
||||||
|
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import type { IconProps } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import isPast from 'date-fns/isPast';
|
||||||
|
|
||||||
|
const borderClass =
|
||||||
|
'border-solid border-2 border-vega-dark-200 border-collapse';
|
||||||
|
|
||||||
|
export type EpochOverviewProps = {
|
||||||
|
id?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays detailed information about an epoch, given an ID. This
|
||||||
|
* works for past epochs and current epochs - future epochs, and a
|
||||||
|
* few other situations (see epoch-missing) will not return us
|
||||||
|
* enough information to render this.
|
||||||
|
*
|
||||||
|
* The details are hidden in a tooltip, behind the epoch number
|
||||||
|
*/
|
||||||
|
const EpochOverview = ({ id }: EpochOverviewProps) => {
|
||||||
|
const { data, error, loading } = useExplorerEpochQuery({
|
||||||
|
variables: { id: id || '' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const ti = data?.epoch.timestamps;
|
||||||
|
if (
|
||||||
|
error?.message &&
|
||||||
|
error.message.includes('no resource corresponding to this id')
|
||||||
|
) {
|
||||||
|
return <EpochMissingOverview missingEpochId={id} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ti || loading || error) {
|
||||||
|
return <span>{id}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const description = (
|
||||||
|
<table className="text-xs m-2">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th className={`text-center ${borderClass}`}>{t('Block')}</th>
|
||||||
|
<th className={`text-center ${borderClass}`}>{t('Time')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th className={`px-2 ${borderClass}`}>{t('Start')}</th>
|
||||||
|
<td className={`px-2 ${borderClass}`}>
|
||||||
|
{ti.firstBlock ? <BlockLink height={ti.firstBlock} /> : '-'}
|
||||||
|
</td>
|
||||||
|
<td className={`px-2 ${borderClass}`}>
|
||||||
|
<Time date={ti.start} />
|
||||||
|
<br />
|
||||||
|
<TimeAgo date={ti.start} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th className={`px-2 ${borderClass}`}>{t('End')}</th>
|
||||||
|
<td className={`px-2 ${borderClass}`}>
|
||||||
|
{ti.lastBlock ? (
|
||||||
|
<BlockLink height={ti.lastBlock} />
|
||||||
|
) : (
|
||||||
|
t('In progress')
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td className={`px-2 ${borderClass}`}>
|
||||||
|
{ti.end ? (
|
||||||
|
<>
|
||||||
|
<Time date={ti.end} />
|
||||||
|
<br />
|
||||||
|
<TimeAgo date={ti.end} />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<span>{t('-')}</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip description={description}>
|
||||||
|
<p>
|
||||||
|
<IconForEpoch start={ti.start} end={ti.end} />
|
||||||
|
{id}
|
||||||
|
</p>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IconForEpochProps = {
|
||||||
|
start: string;
|
||||||
|
end: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chooses an icon to display next to the epoch number, representing
|
||||||
|
* when the epoch is relative to now (i.e. not yet started, started,
|
||||||
|
* finished)
|
||||||
|
*/
|
||||||
|
export function IconForEpoch({ start, end }: IconForEpochProps) {
|
||||||
|
const startHasPassed = start ? isPast(new Date(start)) : false;
|
||||||
|
const endHasPassed = end ? isPast(new Date(end)) : false;
|
||||||
|
|
||||||
|
let i: IconProps['name'] = 'calendar';
|
||||||
|
|
||||||
|
if (!startHasPassed && !endHasPassed) {
|
||||||
|
i = 'calendar';
|
||||||
|
} else if (startHasPassed && !endHasPassed) {
|
||||||
|
i = 'circle';
|
||||||
|
} else if (startHasPassed && endHasPassed) {
|
||||||
|
i = 'tick-circle';
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Icon name={i} className="mr-2" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EpochOverview;
|
||||||
@@ -1,15 +1,32 @@
|
|||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import {
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
useEnvironment,
|
||||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
useNodeSwitcherStore,
|
||||||
|
} from '@vegaprotocol/environment';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||||
|
import { Link, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { ENV } from '../../config/env';
|
||||||
|
import { Routes } from '../../routes/route-names';
|
||||||
|
import { Link as RouteLink } from 'react-router-dom';
|
||||||
|
|
||||||
export const Footer = () => {
|
export const Footer = () => {
|
||||||
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL, setNodeSwitcherOpen } =
|
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment();
|
||||||
useEnvironment();
|
const setNodeSwitcherOpen = useNodeSwitcherStore(
|
||||||
|
(store) => store.setDialogOpen
|
||||||
|
);
|
||||||
|
|
||||||
|
const { screenSize } = useScreenDimensions();
|
||||||
|
const showFullFeedbackLabel = useMemo(
|
||||||
|
() => ['md', 'lg', 'xl', 'xxl', 'xxxl'].includes(screenSize),
|
||||||
|
[screenSize]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="grid grid-rows-2 grid-cols-[1fr_auto] text-xs md:text-md md:flex md:col-span-2 px-4 py-2 gap-4 border-t border-neutral-700 dark:border-neutral-300">
|
<footer className="grid grid-rows-2 lg:grid-cols-[1fr_auto] text-xs md:text-md lg:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
|
||||||
<div className="flex justify-between gap-2 align-middle">
|
<div className="flex justify-between gap-2 align-middle">
|
||||||
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
{GIT_COMMIT_HASH && (
|
||||||
{GIT_COMMIT_HASH && (
|
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
||||||
<p data-testid="git-commit-hash">
|
<p data-testid="git-commit-hash">
|
||||||
{t('Version')}:{' '}
|
{t('Version')}:{' '}
|
||||||
<Link
|
<Link
|
||||||
@@ -23,27 +40,36 @@ export const Footer = () => {
|
|||||||
{GIT_COMMIT_HASH}
|
{GIT_COMMIT_HASH}
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
<div className="flex pl-2 content-center">
|
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
||||||
{VEGA_URL && <NodeUrl url={VEGA_URL} />}
|
{VEGA_URL && <NodeUrl url={VEGA_URL} />}
|
||||||
<Link className="ml-2" onClick={setNodeSwitcherOpen}>
|
<Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
|
||||||
{t('Change')}
|
{t('Change')}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{ENV.addresses.feedback ? (
|
||||||
|
<div className="flex pl-2 content-center">
|
||||||
|
<ExternalLink href={ENV.addresses.feedback}>
|
||||||
|
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
|
||||||
|
</ExternalLink>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<div className="pl-2 align-center lg:align-right lg:flex lg:justify-end gap-2 align-middle lg:max-w-xs lg:ml-auto">
|
||||||
|
<RouteLink to={`/${Routes.DISCLAIMER}`} className="underline">
|
||||||
|
Disclaimer
|
||||||
|
</RouteLink>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const NodeUrl = ({ url }: { url: string }) => {
|
export const NodeUrl = ({ url }: { url: string }) => {
|
||||||
// get base url from api url, api sub domain
|
// get base url from api url, api sub domain
|
||||||
const urlObj = new URL(url);
|
const urlObj = new URL(url);
|
||||||
const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, '');
|
const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, '');
|
||||||
return (
|
return <span className="cursor-default">{nodeUrl}</span>;
|
||||||
<Link href={'https://' + nodeUrl} target="_blank">
|
|
||||||
{nodeUrl}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Header } from './header';
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
|
...jest.requireActual('@vegaprotocol/environment'),
|
||||||
NetworkSwitcher: () => (
|
NetworkSwitcher: () => (
|
||||||
<div data-testid="network-switcher">NetworkSwitcher</div>
|
<div data-testid="network-switcher">NetworkSwitcher</div>
|
||||||
),
|
),
|
||||||
@@ -13,19 +14,18 @@ jest.mock('../search', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const renderComponent = () => (
|
const renderComponent = () => (
|
||||||
<MemoryRouter>
|
<MemoryRouter initialEntries={['/txs']}>
|
||||||
<Header menuOpen={false} setMenuOpen={jest.fn()} />
|
<Header />
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
);
|
);
|
||||||
|
|
||||||
describe('Header', () => {
|
describe('Header', () => {
|
||||||
it('should render heading', () => {
|
it('should render navigation', () => {
|
||||||
render(renderComponent());
|
render(renderComponent());
|
||||||
|
|
||||||
expect(screen.getByTestId('explorer-header')).toHaveTextContent(
|
expect(screen.getByTestId('navigation')).toHaveTextContent('Explorer');
|
||||||
'Vega Explorer'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render search', () => {
|
it('should render search', () => {
|
||||||
render(renderComponent());
|
render(renderComponent());
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +1,114 @@
|
|||||||
import classnames from 'classnames';
|
import { matchPath, useLocation, useMatch } from 'react-router-dom';
|
||||||
import { Link } from 'react-router-dom';
|
import {
|
||||||
import { ThemeSwitcher, Icon } from '@vegaprotocol/ui-toolkit';
|
ThemeSwitcher,
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
Navigation,
|
||||||
import { Search } from '../search';
|
NavigationList,
|
||||||
|
NavigationItem,
|
||||||
|
NavigationLink,
|
||||||
|
NavigationBreakpoint,
|
||||||
|
NavigationTrigger,
|
||||||
|
NavigationContent,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { Routes } from '../../routes/route-names';
|
import { Routes } from '../../routes/route-names';
|
||||||
import type { Dispatch, SetStateAction } from 'react';
|
|
||||||
import { NetworkSwitcher } from '@vegaprotocol/environment';
|
import { NetworkSwitcher } from '@vegaprotocol/environment';
|
||||||
|
import type { Navigable } from '../../routes/router-config';
|
||||||
|
import { isNavigable } from '../../routes/router-config';
|
||||||
|
import { routerConfig } from '../../routes/router-config';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import compact from 'lodash/compact';
|
||||||
|
import { Search } from '../search';
|
||||||
|
|
||||||
interface ThemeToggleProps {
|
const routeToNavigationItem = (r: Navigable) => (
|
||||||
menuOpen: boolean;
|
<NavigationItem key={r.handle.name}>
|
||||||
setMenuOpen: Dispatch<SetStateAction<boolean>>;
|
<NavigationLink to={r.path}>{r.handle.text}</NavigationLink>
|
||||||
}
|
</NavigationItem>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Header = () => {
|
||||||
|
const isHome = Boolean(useMatch(Routes.HOME));
|
||||||
|
const pages = routerConfig[0].children || [];
|
||||||
|
const mainItems = compact(
|
||||||
|
[Routes.TX, Routes.BLOCKS, Routes.ORACLES, Routes.VALIDATORS].map((n) =>
|
||||||
|
pages.find((r) => r.path === n)
|
||||||
|
)
|
||||||
|
).filter(isNavigable);
|
||||||
|
|
||||||
|
const groupedItems = compact(
|
||||||
|
[
|
||||||
|
Routes.PARTIES,
|
||||||
|
Routes.ASSETS,
|
||||||
|
Routes.MARKETS,
|
||||||
|
Routes.GOVERNANCE,
|
||||||
|
Routes.NETWORK_PARAMETERS,
|
||||||
|
Routes.GENESIS,
|
||||||
|
].map((n) => pages.find((r) => r.path === n))
|
||||||
|
).filter(isNavigable);
|
||||||
|
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because the grouped items are displayed in a sub menu under an "Other" item
|
||||||
|
* we need to determine whether any underlying item is active to highlight the
|
||||||
|
* trigger in the same fashion as any other top-level `NavigationLink`.
|
||||||
|
* This function checks whether the current location pathname is one of the
|
||||||
|
* underlying NavigationLinks.
|
||||||
|
*/
|
||||||
|
const isOnOther = useMemo(() => {
|
||||||
|
for (const path of groupedItems.map((r) => r.path)) {
|
||||||
|
const matched = matchPath(`${path}/*`, pathname);
|
||||||
|
if (matched) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}, [groupedItems, pathname]);
|
||||||
|
|
||||||
export const Header = ({ menuOpen, setMenuOpen }: ThemeToggleProps) => {
|
|
||||||
const headerClasses = classnames(
|
|
||||||
'md:col-span-2',
|
|
||||||
'grid grid-rows-2 md:grid-rows-1 grid-cols-[1fr_auto] md:grid-cols-[auto_1fr_auto] items-center',
|
|
||||||
'p-4 gap-2 md:gap-4',
|
|
||||||
'border-b border-neutral-700 dark:border-neutral-300 bg-black',
|
|
||||||
'dark text-white'
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<header className={headerClasses}>
|
<Navigation
|
||||||
<div className="flex h-full items-center sm:items-stretch gap-4">
|
appName="Explorer"
|
||||||
<Link to={Routes.HOME}>
|
theme="system"
|
||||||
<h1
|
breakpoints={[490, 900]}
|
||||||
className="text-white text-3xl font-alpha uppercase calt mb-0"
|
actions={
|
||||||
data-testid="explorer-header"
|
<>
|
||||||
>
|
<ThemeSwitcher />
|
||||||
{t('Vega Explorer')}
|
{!isHome && <Search />}
|
||||||
</h1>
|
</>
|
||||||
</Link>
|
}
|
||||||
<NetworkSwitcher />
|
onResize={(width, el) => {
|
||||||
</div>
|
if (width < 1157) {
|
||||||
<button
|
// switch to magnifying glass trigger when width < 1157
|
||||||
data-testid="open-menu"
|
el.classList.remove('nav-search-full');
|
||||||
className="md:hidden text-white"
|
el.classList.add('nav-search-compact');
|
||||||
onClick={() => setMenuOpen(!menuOpen)}
|
} else {
|
||||||
|
el.classList.remove('nav-search-compact');
|
||||||
|
el.classList.add('nav-search-full');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NavigationList hide={[NavigationBreakpoint.Small]}>
|
||||||
|
<NavigationItem>
|
||||||
|
<NetworkSwitcher />
|
||||||
|
</NavigationItem>
|
||||||
|
</NavigationList>
|
||||||
|
<NavigationList
|
||||||
|
hide={[NavigationBreakpoint.Small, NavigationBreakpoint.Narrow]}
|
||||||
>
|
>
|
||||||
<Icon name={menuOpen ? 'cross' : 'menu'} />
|
{mainItems.map(routeToNavigationItem)}
|
||||||
</button>
|
{groupedItems && groupedItems.length > 0 && (
|
||||||
<Search />
|
<NavigationItem>
|
||||||
<ThemeSwitcher className="-my-4" />
|
<NavigationTrigger
|
||||||
</header>
|
isActive={Boolean(isOnOther)}
|
||||||
|
data-testid="other"
|
||||||
|
>
|
||||||
|
{t('Other')}
|
||||||
|
</NavigationTrigger>
|
||||||
|
<NavigationContent>
|
||||||
|
<NavigationList>
|
||||||
|
{groupedItems.map(routeToNavigationItem)}
|
||||||
|
</NavigationList>
|
||||||
|
</NavigationContent>
|
||||||
|
</NavigationItem>
|
||||||
|
)}
|
||||||
|
</NavigationList>
|
||||||
|
</Navigation>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export interface InfoBlockProps {
|
export interface InfoBlockProps {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -11,14 +10,14 @@ export const InfoBlock = ({ title, subtitle, tooltipInfo }: InfoBlockProps) => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col text-center ">
|
<div className="flex flex-col text-center ">
|
||||||
<h3 className="text-4xl">{title}</h3>
|
<h3 className="text-4xl">{title}</h3>
|
||||||
<p className="text-zinc-800 dark:text-zinc-300">
|
<p className="text-vega-dark-100 dark:text-vega-light-200">
|
||||||
{subtitle}
|
{subtitle}
|
||||||
{tooltipInfo ? (
|
{tooltipInfo ? (
|
||||||
<Tooltip description={tooltipInfo} align="center">
|
<Tooltip description={tooltipInfo} align="center">
|
||||||
<span>
|
<span>
|
||||||
<Icon
|
<Icon
|
||||||
name="info-sign"
|
name="info-sign"
|
||||||
className="ml-2 text-zinc-400 dark:text-zinc-600"
|
className="ml-2 text-vega-light-300 dark:text-vega-dark-300"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -44,12 +44,12 @@ export const InfoPanel = ({
|
|||||||
text={id}
|
text={id}
|
||||||
startChars={visibleChars}
|
startChars={visibleChars}
|
||||||
endChars={visibleChars}
|
endChars={visibleChars}
|
||||||
className="text-black dark:text-zinc-200"
|
className="text-vega-dark-100 dark:text-vega-light-200"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<p
|
<p
|
||||||
title={id}
|
title={id}
|
||||||
className="text-black dark:text-zinc-200 truncate ..."
|
className="text-vega-dark-100 dark:text-vega-light-200 truncate ..."
|
||||||
>
|
>
|
||||||
{id}
|
{id}
|
||||||
</p>
|
</p>
|
||||||
@@ -70,7 +70,7 @@ export const InfoPanel = ({
|
|||||||
</div>
|
</div>
|
||||||
{copy && (
|
{copy && (
|
||||||
<CopyWithTooltip text={id}>
|
<CopyWithTooltip text={id}>
|
||||||
<button className="bg-zinc-100 dark:bg-zinc-900 rounded-sm py-2 px-3">
|
<button className="bg-vega-light-100 dark:bg-vega-dark-100 rounded-sm py-2 px-3">
|
||||||
<Icon name="duplicate" />
|
<Icon name="duplicate" />
|
||||||
</button>
|
</button>
|
||||||
</CopyWithTooltip>
|
</CopyWithTooltip>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Routes } from '../../routes/route-names';
|
import { Routes } from '../../routes/route-names';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { HTMLInputTypeAttribute, SyntheticEvent } from 'react';
|
import type { HTMLInputTypeAttribute, SyntheticEvent } from 'react';
|
||||||
import { Input, Button } from '@vegaprotocol/ui-toolkit';
|
import { Input, Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
interface JumpToProps {
|
interface JumpToProps {
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
query ExplorerAsset($id: ID!) {
|
|
||||||
asset(id: $id) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
status
|
|
||||||
decimals
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import * as Types from '@vegaprotocol/types';
|
|
||||||
|
|
||||||
import { gql } from '@apollo/client';
|
|
||||||
import * as Apollo from '@apollo/client';
|
|
||||||
const defaultOptions = {} as const;
|
|
||||||
export type ExplorerAssetQueryVariables = Types.Exact<{
|
|
||||||
id: Types.Scalars['ID'];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type ExplorerAssetQuery = { __typename?: 'Query', asset?: { __typename?: 'Asset', id: string, name: string, status: Types.AssetStatus, decimals: number } | null };
|
|
||||||
|
|
||||||
|
|
||||||
export const ExplorerAssetDocument = gql`
|
|
||||||
query ExplorerAsset($id: ID!) {
|
|
||||||
asset(id: $id) {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
status
|
|
||||||
decimals
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useExplorerAssetQuery__
|
|
||||||
*
|
|
||||||
* To run a query within a React component, call `useExplorerAssetQuery` and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useExplorerAssetQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
|
||||||
* you can use to render your UI.
|
|
||||||
*
|
|
||||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const { data, loading, error } = useExplorerAssetQuery({
|
|
||||||
* variables: {
|
|
||||||
* id: // value for 'id'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useExplorerAssetQuery(baseOptions: Apollo.QueryHookOptions<ExplorerAssetQuery, ExplorerAssetQueryVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useQuery<ExplorerAssetQuery, ExplorerAssetQueryVariables>(ExplorerAssetDocument, options);
|
|
||||||
}
|
|
||||||
export function useExplorerAssetLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerAssetQuery, ExplorerAssetQueryVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useLazyQuery<ExplorerAssetQuery, ExplorerAssetQueryVariables>(ExplorerAssetDocument, options);
|
|
||||||
}
|
|
||||||
export type ExplorerAssetQueryHookResult = ReturnType<typeof useExplorerAssetQuery>;
|
|
||||||
export type ExplorerAssetLazyQueryHookResult = ReturnType<typeof useExplorerAssetLazyQuery>;
|
|
||||||
export type ExplorerAssetQueryResult = Apollo.QueryResult<ExplorerAssetQuery, ExplorerAssetQueryVariables>;
|
|
||||||
@@ -1,63 +1,37 @@
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import { render } from '@testing-library/react';
|
import { render, waitFor } from '@testing-library/react';
|
||||||
import AssetLink from './asset-link';
|
import { AssetLink } from './asset-link';
|
||||||
import { ExplorerAssetDocument } from './__generated__/Asset';
|
import { mockAssetA1 } from '../../../mocks/assets';
|
||||||
|
|
||||||
function renderComponent(id: string, mock: MockedResponse[]) {
|
function renderComponent(id: string, mock: MockedResponse[]) {
|
||||||
return (
|
return (
|
||||||
<MockedProvider mocks={mock}>
|
<MockedProvider mocks={mock} addTypename={false}>
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<AssetLink id={id} />
|
<AssetLink assetId={id} />
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Asset link component', () => {
|
describe('AssetLink', () => {
|
||||||
it('Renders the ID at first', () => {
|
it('renders the asset id when not found and makes the button disabled', async () => {
|
||||||
const res = render(renderComponent('123', []));
|
const res = render(renderComponent('123', []));
|
||||||
expect(res.getByText('123')).toBeInTheDocument();
|
expect(res.getByText('123')).toBeInTheDocument();
|
||||||
|
expect(await res.findByTestId('asset-link')).toBeDisabled();
|
||||||
|
await waitFor(async () => {
|
||||||
|
expect(await res.queryByText('A ONE')).toBeFalsy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders the asset name when the query returns a result', async () => {
|
it('renders the asset name when found and make the button enabled', async () => {
|
||||||
const mock = {
|
const res = render(renderComponent('123', [mockAssetA1]));
|
||||||
request: {
|
|
||||||
query: ExplorerAssetDocument,
|
|
||||||
variables: {
|
|
||||||
id: '123',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
result: {
|
|
||||||
data: {
|
|
||||||
asset: {
|
|
||||||
id: '123',
|
|
||||||
name: 'test-label',
|
|
||||||
status: 'irrelevant-test-data',
|
|
||||||
decimals: 18,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = render(renderComponent('123', [mock]));
|
|
||||||
expect(res.getByText('123')).toBeInTheDocument();
|
expect(res.getByText('123')).toBeInTheDocument();
|
||||||
expect(await res.findByText('test-label')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Leaves the asset id when the asset is not found', async () => {
|
await waitFor(async () => {
|
||||||
const mock = {
|
expect(await res.findByText('A ONE')).toBeInTheDocument();
|
||||||
request: {
|
expect(await res.findByTestId('asset-link')).not.toBeDisabled();
|
||||||
query: ExplorerAssetDocument,
|
});
|
||||||
variables: {
|
|
||||||
id: '123',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
error: new Error('No such asset'),
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = render(renderComponent('123', [mock]));
|
|
||||||
expect(await res.findByText('123')).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,36 +1,54 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Routes } from '../../../routes/route-names';
|
|
||||||
import { useExplorerAssetQuery } from './__generated__/Asset';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
import type { ComponentProps } from 'react';
|
import type { ComponentProps } from 'react';
|
||||||
import Hash from '../hash';
|
import Hash from '../hash';
|
||||||
|
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import {
|
||||||
|
useAssetDataProvider,
|
||||||
|
useAssetDetailsDialogStore,
|
||||||
|
} from '@vegaprotocol/assets';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { Routes } from '../../../routes/route-names';
|
||||||
|
|
||||||
export type AssetLinkProps = Partial<ComponentProps<typeof Link>> & {
|
export type AssetLinkProps = Partial<ComponentProps<typeof ButtonLink>> & {
|
||||||
id: string;
|
assetId: string;
|
||||||
|
asDialog?: boolean;
|
||||||
|
showAssetSymbol?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an asset ID, it will fetch the asset name and show that,
|
* Given an asset ID, it will fetch the asset name and show that,
|
||||||
* with a link to the assets list. If the name does not come back
|
* with a link to the assets modal. If the name does not come back
|
||||||
* it will use the ID instead.
|
* it will use the ID instead.
|
||||||
*/
|
*/
|
||||||
const AssetLink = ({ id, ...props }: AssetLinkProps) => {
|
export const AssetLink = ({
|
||||||
const { data } = useExplorerAssetQuery({
|
assetId,
|
||||||
variables: { id },
|
asDialog,
|
||||||
});
|
showAssetSymbol = false,
|
||||||
|
...props
|
||||||
|
}: AssetLinkProps) => {
|
||||||
|
const { data: asset } = useAssetDataProvider(assetId);
|
||||||
|
|
||||||
let label: string = id;
|
const open = useAssetDetailsDialogStore((state) => state.open);
|
||||||
|
const navigate = useNavigate();
|
||||||
if (data?.asset?.name) {
|
const label = asset
|
||||||
label = data.asset.name;
|
? showAssetSymbol
|
||||||
}
|
? asset?.symbol
|
||||||
|
: asset?.name
|
||||||
|
: assetId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link className="underline" {...props} to={`/${Routes.ASSETS}#${id}`}>
|
<ButtonLink
|
||||||
|
data-testid="asset-link"
|
||||||
|
disabled={!asset}
|
||||||
|
onClick={(e) => {
|
||||||
|
if (asDialog) {
|
||||||
|
open(assetId, e.target as HTMLElement);
|
||||||
|
} else {
|
||||||
|
navigate(`/${Routes.ASSETS}/${asset?.id}`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
<Hash text={label} />
|
<Hash text={label} />
|
||||||
</Link>
|
</ButtonLink>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AssetLink;
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user