mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-02-08 13:49:43 +00:00
Compare commits
1500 Commits
v5.0.2
...
spreadshee
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed0f8892a8 | ||
|
|
452e3f046c | ||
|
|
797fcefbc8 | ||
|
|
9677545c6c | ||
|
|
00f37c4562 | ||
|
|
69dfd2a672 | ||
|
|
8f35d495f2 | ||
|
|
e6e91e96d7 | ||
|
|
7b0e05069e | ||
|
|
425b36db7f | ||
|
|
7e28102477 | ||
|
|
722ae13c00 | ||
|
|
11863e2f91 | ||
|
|
0b5e629f78 | ||
|
|
1639325741 | ||
|
|
665372b397 | ||
|
|
0d218e174f | ||
|
|
c88a4c19fe | ||
|
|
bb1b810c72 | ||
|
|
d098a6c0f1 | ||
|
|
f0edbce9c6 | ||
|
|
112f7d1b70 | ||
|
|
cd6eba4882 | ||
|
|
b1aae0904c | ||
|
|
019977d7e7 | ||
|
|
c556e0de1a | ||
|
|
8fdf1ba312 | ||
|
|
7fe4f0f96d | ||
|
|
c70efee29a | ||
|
|
b1c1687c48 | ||
|
|
082894054b | ||
|
|
da19e7404b | ||
|
|
3cb7106c4f | ||
|
|
716a75872d | ||
|
|
ae1c0d8ee1 | ||
|
|
463db22a2e | ||
|
|
f104d939a2 | ||
|
|
9da74fb81d | ||
|
|
b694daa52e | ||
|
|
4fd3e3c910 | ||
|
|
4c619fa5c9 | ||
|
|
83bf1ae2cd | ||
|
|
f75ed5734c | ||
|
|
09f229602d | ||
|
|
c8225a71bb | ||
|
|
1e881ddd6e | ||
|
|
095c5d2059 | ||
|
|
019f367466 | ||
|
|
28918e792b | ||
|
|
57d832549d | ||
|
|
7c0768fc7a | ||
|
|
fd69705fd9 | ||
|
|
55b58a744c | ||
|
|
e264d654c6 | ||
|
|
d0f00bd7f6 | ||
|
|
1a62deaf35 | ||
|
|
7fcf4047a1 | ||
|
|
ac4fd8d5b3 | ||
|
|
48d1c87908 | ||
|
|
dd7a13f3cf | ||
|
|
db3c81388a | ||
|
|
1e6dd77759 | ||
|
|
8a81491c29 | ||
|
|
86245ae162 | ||
|
|
05b957a8d3 | ||
|
|
21828c6ae7 | ||
|
|
89e91e5c0e | ||
|
|
06c4d41273 | ||
|
|
60cb216fbd | ||
|
|
f4d916a0de | ||
|
|
eb54d808cf | ||
|
|
0565451a97 | ||
|
|
8174ac2725 | ||
|
|
493bd69a1e | ||
|
|
794d8255cf | ||
|
|
edadfe493b | ||
|
|
c7bda57838 | ||
|
|
947a23a4cb | ||
|
|
bf30dabe4d | ||
|
|
c5b6b8da95 | ||
|
|
e171252ecc | ||
|
|
7d090a4147 | ||
|
|
bbd2cd777b | ||
|
|
d7bc875d83 | ||
|
|
ae997416a2 | ||
|
|
97d94b2512 | ||
|
|
14ec0a8e4e | ||
|
|
985a802314 | ||
|
|
a3b4526c33 | ||
|
|
42e595ed85 | ||
|
|
cc62ceae3b | ||
|
|
1d0e748c91 | ||
|
|
b70a6cba99 | ||
|
|
cb469e97c5 | ||
|
|
0ebc789de0 | ||
|
|
299f565d66 | ||
|
|
8dd6aba59c | ||
|
|
ebf0e4fadf | ||
|
|
2e1aeffbf8 | ||
|
|
0a83fee55d | ||
|
|
5a89117c6f | ||
|
|
eb0e96ea42 | ||
|
|
a9f38fa9e9 | ||
|
|
b680633b5c | ||
|
|
ff5ce9298a | ||
|
|
2b4bf2aef4 | ||
|
|
fd59a1a1be | ||
|
|
3ff93dd0e6 | ||
|
|
b8edfad166 | ||
|
|
6aaf42d883 | ||
|
|
9306c6f6d7 | ||
|
|
1ddc854da9 | ||
|
|
ae5c48f5e5 | ||
|
|
e47848bf5a | ||
|
|
e09002fa43 | ||
|
|
eb817de8e4 | ||
|
|
b2bc210eca | ||
|
|
da230e0cbe | ||
|
|
ffae640c3f | ||
|
|
9f3e0d6098 | ||
|
|
4078927d58 | ||
|
|
c7994e0479 | ||
|
|
26c87de08c | ||
|
|
0b938dd51c | ||
|
|
8427d8688b | ||
|
|
9f198cbd23 | ||
|
|
a97e378800 | ||
|
|
790f13ef2b | ||
|
|
179d59c195 | ||
|
|
87bdc2f859 | ||
|
|
d75cf817c4 | ||
|
|
ca0806d5b1 | ||
|
|
3318386fe6 | ||
|
|
44a835cfe8 | ||
|
|
81b28b9602 | ||
|
|
dc8ea54c91 | ||
|
|
1d40582cbd | ||
|
|
e4cbcfdf74 | ||
|
|
7ab385e552 | ||
|
|
9b713a7f38 | ||
|
|
11400e3690 | ||
|
|
fc31bd6fc6 | ||
|
|
d1d9a254d7 | ||
|
|
d79f3a887d | ||
|
|
849ec9c91a | ||
|
|
13afd0fddb | ||
|
|
fa7120571d | ||
|
|
4d4e882dcc | ||
|
|
af8c7179d1 | ||
|
|
c743054184 | ||
|
|
935d850296 | ||
|
|
ad6b6886f1 | ||
|
|
d5d08c89de | ||
|
|
5a246f732e | ||
|
|
669ad421d9 | ||
|
|
98e8393d14 | ||
|
|
de8dfb7e93 | ||
|
|
61b32d6490 | ||
|
|
5057fb1cc8 | ||
|
|
ed49dfc2fa | ||
|
|
839018795c | ||
|
|
c541f9d3e5 | ||
|
|
5e5957e4c4 | ||
|
|
84a759267e | ||
|
|
eb1e6fab0d | ||
|
|
6094953009 | ||
|
|
b845c73ad6 | ||
|
|
2788606105 | ||
|
|
aebf267320 | ||
|
|
33896ea8db | ||
|
|
cb8699f315 | ||
|
|
2a43db6560 | ||
|
|
0ad1200870 | ||
|
|
2debdbfd38 | ||
|
|
07c96bd4bd | ||
|
|
704c6abe7c | ||
|
|
e0e0e608e2 | ||
|
|
40e15a0720 | ||
|
|
d6235fd147 | ||
|
|
c5a3a911e0 | ||
|
|
4e7a037bc5 | ||
|
|
f1c3f46ad7 | ||
|
|
6c0e39da20 | ||
|
|
2a9d638acf | ||
|
|
426e1ba8e8 | ||
|
|
1bb36ef207 | ||
|
|
67cde0fd59 | ||
|
|
0696cd20d5 | ||
|
|
34fce5188f | ||
|
|
273ba0381f | ||
|
|
63a05d86e8 | ||
|
|
283e115d0a | ||
|
|
b513ebba8e | ||
|
|
e324cea7b9 | ||
|
|
c3cda91a2d | ||
|
|
bb9f611629 | ||
|
|
6cc8e62b15 | ||
|
|
5a44b76adb | ||
|
|
109d6bc617 | ||
|
|
7249ff6107 | ||
|
|
406830fa13 | ||
|
|
a6a3278443 | ||
|
|
9fd8531529 | ||
|
|
c687976796 | ||
|
|
ae511929f7 | ||
|
|
e78e756206 | ||
|
|
2e9e0dac1a | ||
|
|
bff97712e2 | ||
|
|
78bc25cce0 | ||
|
|
a2e829417d | ||
|
|
9902413daf | ||
|
|
06a35f4a73 | ||
|
|
e2a46157b9 | ||
|
|
c4913a94c4 | ||
|
|
d6e04c3ae8 | ||
|
|
5b17ef5217 | ||
|
|
8edeedc32c | ||
|
|
543dbc9e50 | ||
|
|
3710f4297b | ||
|
|
91d6ba4da8 | ||
|
|
fbfa3c1abe | ||
|
|
d13d7bef0c | ||
|
|
510f5d7190 | ||
|
|
595b98d4b7 | ||
|
|
c696107aeb | ||
|
|
632722cd7b | ||
|
|
60c264ea49 | ||
|
|
10ce92264d | ||
|
|
2e5c7aa6bc | ||
|
|
1e7e2c5b51 | ||
|
|
46790ce3fe | ||
|
|
7bd891f8f0 | ||
|
|
1ae16acbac | ||
|
|
2281fb7f61 | ||
|
|
89e8db6c6e | ||
|
|
11300692e5 | ||
|
|
a31161c6d8 | ||
|
|
edd85f8ec0 | ||
|
|
0d420604dd | ||
|
|
777f5666e2 | ||
|
|
e7bded641f | ||
|
|
6f0dfbe038 | ||
|
|
0e92eeb50e | ||
|
|
50b6cb879a | ||
|
|
739ebde6a8 | ||
|
|
24ef74f16d | ||
|
|
9e0a57ca5b | ||
|
|
feaebb6f0f | ||
|
|
9a45759414 | ||
|
|
14c5a0447b | ||
|
|
7793e03d82 | ||
|
|
f422573fcc | ||
|
|
516109ecd3 | ||
|
|
31d32b16a5 | ||
|
|
435c1bd6e2 | ||
|
|
6d5ebf3f58 | ||
|
|
b97036b447 | ||
|
|
d7ef3cb896 | ||
|
|
2218eefa67 | ||
|
|
666d33d767 | ||
|
|
3f4f83d354 | ||
|
|
3dea547559 | ||
|
|
0e9c6acb84 | ||
|
|
1b6881673e | ||
|
|
f069b33b60 | ||
|
|
c0a86e31da | ||
|
|
7b95778efe | ||
|
|
ef8a102d0a | ||
|
|
95448de3ed | ||
|
|
0a574762c7 | ||
|
|
59b1440990 | ||
|
|
98d6729d4b | ||
|
|
135a0bbe5c | ||
|
|
c3f579931d | ||
|
|
21c51e81d2 | ||
|
|
e6538c95ad | ||
|
|
0e63e87f9b | ||
|
|
1e900ec775 | ||
|
|
5ff30874dd | ||
|
|
94550006c4 | ||
|
|
202636ce72 | ||
|
|
cae8c6f622 | ||
|
|
0e03c4377f | ||
|
|
7497ea1262 | ||
|
|
d68bb34f6f | ||
|
|
732a6f4942 | ||
|
|
70fb896ae1 | ||
|
|
034eae6722 | ||
|
|
3472949bf0 | ||
|
|
c333b8ca30 | ||
|
|
17e3fbdabf | ||
|
|
30c5c9dfaf | ||
|
|
585d1ee38a | ||
|
|
3255afb487 | ||
|
|
9f75648b50 | ||
|
|
50406f8984 | ||
|
|
f24c7ebc5f | ||
|
|
75dcecfab0 | ||
|
|
34c603ce53 | ||
|
|
7785a73876 | ||
|
|
cfa8f731f2 | ||
|
|
5ecd05c7b3 | ||
|
|
4ec95c2f1c | ||
|
|
9e4413b02e | ||
|
|
38d0d689b3 | ||
|
|
799c5e9e4e | ||
|
|
b98dffda8f | ||
|
|
d816d841a8 | ||
|
|
1730e02dc3 | ||
|
|
53737d2f3c | ||
|
|
932dc3f9f6 | ||
|
|
567fdb6a36 | ||
|
|
3e323b929d | ||
|
|
a53fb126ac | ||
|
|
ec2c17bc21 | ||
|
|
3894f31c2c | ||
|
|
342b96801a | ||
|
|
36f1dfe2e0 | ||
|
|
f90951add1 | ||
|
|
7d65c45833 | ||
|
|
3629235a00 | ||
|
|
09ff7744dc | ||
|
|
1fa9d59634 | ||
|
|
ccec9dbc70 | ||
|
|
a49dbad913 | ||
|
|
4f0497158b | ||
|
|
4757d158a8 | ||
|
|
4bbd371a42 | ||
|
|
10b4b877de | ||
|
|
3bd030c397 | ||
|
|
7c408808c9 | ||
|
|
39dcaa8fc4 | ||
|
|
04cdd584a2 | ||
|
|
2693487f81 | ||
|
|
d73dce8804 | ||
|
|
cf333e8c15 | ||
|
|
5aea28611b | ||
|
|
c58ccf0a66 | ||
|
|
d5c870eb9d | ||
|
|
a41b00e0be | ||
|
|
383ee7e577 | ||
|
|
b3b1f887ef | ||
|
|
b6089f5d28 | ||
|
|
2d7d5cad39 | ||
|
|
cb06833882 | ||
|
|
cf3ab86cec | ||
|
|
efd3d18484 | ||
|
|
e6baaa6184 | ||
|
|
81c943ecda | ||
|
|
587a4f479c | ||
|
|
5638152121 | ||
|
|
81fc744270 | ||
|
|
2437b9e028 | ||
|
|
d148cac10e | ||
|
|
af8c9e3cc3 | ||
|
|
b64c71c307 | ||
|
|
2d60d8fcc1 | ||
|
|
814221997e | ||
|
|
5e112b6f59 | ||
|
|
6b253c30b2 | ||
|
|
b5b0c1efa7 | ||
|
|
5dce6f81af | ||
|
|
6ff186d7de | ||
|
|
801a1cab1a | ||
|
|
737539e3a8 | ||
|
|
2992f868ef | ||
|
|
e718ef80dc | ||
|
|
220a441422 | ||
|
|
8440eb513a | ||
|
|
09004fd7db | ||
|
|
e9fb2bf677 | ||
|
|
81261e2413 | ||
|
|
2c7b51dc00 | ||
|
|
47447badc1 | ||
|
|
1233a2347d | ||
|
|
b0cec6f668 | ||
|
|
05e25d51ec | ||
|
|
81c27836c2 | ||
|
|
24aa1ec1b3 | ||
|
|
6c7bc84ff4 | ||
|
|
bc1d139cca | ||
|
|
c1afef18f3 | ||
|
|
f46ff6237b | ||
|
|
fd94e8037d | ||
|
|
b95bf75dce | ||
|
|
b08b3aa3b1 | ||
|
|
5ed931243e | ||
|
|
f5ab48c8af | ||
|
|
75202f4ff8 | ||
|
|
3c679674d5 | ||
|
|
6f7921691c | ||
|
|
13ba136c66 | ||
|
|
abd80af816 | ||
|
|
aac927e6ea | ||
|
|
f7e0d7d698 | ||
|
|
eb633bae3a | ||
|
|
76a0b5e3e2 | ||
|
|
0679525905 | ||
|
|
3396c7bca4 | ||
|
|
5031ce3936 | ||
|
|
8b774b47e2 | ||
|
|
f0e7a398fa | ||
|
|
72c3f23c67 | ||
|
|
33c01b611e | ||
|
|
7cc2e6953b | ||
|
|
1284d4ab80 | ||
|
|
cd83f01132 | ||
|
|
3b4480fa8b | ||
|
|
a9f8fbbff5 | ||
|
|
e37b055e24 | ||
|
|
8eeda0e647 | ||
|
|
c28b47e231 | ||
|
|
ab989107a5 | ||
|
|
268fe93dd9 | ||
|
|
e154fb05ea | ||
|
|
fb14fe587b | ||
|
|
e72a17ce32 | ||
|
|
6c11409e7e | ||
|
|
f43db71b01 | ||
|
|
dc3611474f | ||
|
|
5c5a306f7f | ||
|
|
024d9fb5ac | ||
|
|
6fb480042f | ||
|
|
acc1f8624e | ||
|
|
0b97853a57 | ||
|
|
8408b55e08 | ||
|
|
e49d9f7eab | ||
|
|
6202bd78f3 | ||
|
|
a2a5916e09 | ||
|
|
3f52e42661 | ||
|
|
224032d0c2 | ||
|
|
4dece2e58a | ||
|
|
7eb85d4454 | ||
|
|
bd750e6f97 | ||
|
|
ee0d4d29e1 | ||
|
|
10d2ea7c0a | ||
|
|
0ab76d53ec | ||
|
|
3dea0a5f67 | ||
|
|
41c2e04cfa | ||
|
|
d8183feb07 | ||
|
|
e49fe2bca8 | ||
|
|
67664a2816 | ||
|
|
eefbe00aec | ||
|
|
be67d5f120 | ||
|
|
fa1fe694cf | ||
|
|
9f681c0c09 | ||
|
|
18dde2849e | ||
|
|
5678774de0 | ||
|
|
f5370e1384 | ||
|
|
934d7cb104 | ||
|
|
33b201147d | ||
|
|
abe2006e15 | ||
|
|
81b5cb386d | ||
|
|
c713dd6c87 | ||
|
|
eda5c490b7 | ||
|
|
b6bc9e1257 | ||
|
|
123ffd8886 | ||
|
|
9a2c09c6e8 | ||
|
|
5b5fdde9a2 | ||
|
|
0cc50d70da | ||
|
|
c79ecddee4 | ||
|
|
e929edbd53 | ||
|
|
8de7a1ec70 | ||
|
|
0b6ae4d225 | ||
|
|
c69bb8bb2b | ||
|
|
e8dda6f946 | ||
|
|
3daebfc0c1 | ||
|
|
d5fcbb97c3 | ||
|
|
97555e7849 | ||
|
|
7eb799d2a6 | ||
|
|
d06a85a3b6 | ||
|
|
f616eadccf | ||
|
|
b69d6c193b | ||
|
|
d12908778e | ||
|
|
95c42f3266 | ||
|
|
597e6de682 | ||
|
|
406141a4f1 | ||
|
|
0db7fe63b0 | ||
|
|
e5a43ef5b6 | ||
|
|
4d9a9c9ac2 | ||
|
|
3cc1f6b994 | ||
|
|
4322361269 | ||
|
|
44d3651aa8 | ||
|
|
03423f32c8 | ||
|
|
bf8c950a8a | ||
|
|
f16ae734cc | ||
|
|
91196dca39 | ||
|
|
ea36ff5011 | ||
|
|
dad418140b | ||
|
|
d5f8877768 | ||
|
|
95f2dfa8ac | ||
|
|
d60b14c686 | ||
|
|
fabcd7b8f4 | ||
|
|
045880c234 | ||
|
|
d2071102c5 | ||
|
|
864bc8c7f9 | ||
|
|
b63b861b8a | ||
|
|
23effbb3ec | ||
|
|
c0a87c834c | ||
|
|
4d02213260 | ||
|
|
46118964ea | ||
|
|
ca52c588fd | ||
|
|
b9905e10de | ||
|
|
7ea08af177 | ||
|
|
fa59aff6d5 | ||
|
|
c90e3bedb1 | ||
|
|
62cf565a09 | ||
|
|
4503361877 | ||
|
|
f2bc7ef3ed | ||
|
|
f9505e42c4 | ||
|
|
d340df758e | ||
|
|
741ecf0955 | ||
|
|
90ab2e468e | ||
|
|
360b9d7730 | ||
|
|
bb698dbe9d | ||
|
|
5e120a7328 | ||
|
|
206daba741 | ||
|
|
79e86e8b76 | ||
|
|
398c17c75a | ||
|
|
49adc95170 | ||
|
|
2b71bd1178 | ||
|
|
c53938bc0a | ||
|
|
718827d757 | ||
|
|
b4c87e7e10 | ||
|
|
20dd0e9b2d | ||
|
|
397e2d207e | ||
|
|
29f7b6cb74 | ||
|
|
a004b55e8c | ||
|
|
221d8f0697 | ||
|
|
e7a243ab7f | ||
|
|
ce1ffe2070 | ||
|
|
32e2419b67 | ||
|
|
5f7079e526 | ||
|
|
4ffeae11e2 | ||
|
|
7621e31d38 | ||
|
|
c00c751127 | ||
|
|
a09f550b52 | ||
|
|
0aeb1cfa97 | ||
|
|
46da7e45b9 | ||
|
|
83bf448bdd | ||
|
|
5ab15fb812 | ||
|
|
0dc70f3e42 | ||
|
|
5bc86a75bc | ||
|
|
51d3df9282 | ||
|
|
31925ea099 | ||
|
|
a56e6540e8 | ||
|
|
03bccb4cb4 | ||
|
|
bba9b7b6ec | ||
|
|
5b8ea6e4b0 | ||
|
|
91676d802c | ||
|
|
395d343f69 | ||
|
|
201993ff08 | ||
|
|
f9499904f3 | ||
|
|
0a152474a2 | ||
|
|
8dc522e054 | ||
|
|
eb8209d575 | ||
|
|
96b3ac7435 | ||
|
|
42d7113896 | ||
|
|
7f19786053 | ||
|
|
52b121a9f4 | ||
|
|
d191a8223e | ||
|
|
b371b43a86 | ||
|
|
c243f384a5 | ||
|
|
e10f8d759c | ||
|
|
977e5b8cb6 | ||
|
|
b68f95a7e7 | ||
|
|
e30b01de23 | ||
|
|
68f87db33e | ||
|
|
f2bfa462a0 | ||
|
|
adffcf5229 | ||
|
|
a4f37cbcfa | ||
|
|
18d8328a9c | ||
|
|
a466578ebf | ||
|
|
c92f8833e2 | ||
|
|
2f46c58028 | ||
|
|
a1c24e346c | ||
|
|
fb7fb62a0e | ||
|
|
7378ee1a32 | ||
|
|
0bee5b4365 | ||
|
|
47bfd6f539 | ||
|
|
1b92cb7f86 | ||
|
|
2931d47d05 | ||
|
|
7668a54648 | ||
|
|
aa0aaf7c10 | ||
|
|
4b80da96ab | ||
|
|
396fa19d7b | ||
|
|
4cd235a08e | ||
|
|
edd21956c5 | ||
|
|
f842156253 | ||
|
|
ce0ce0f229 | ||
|
|
ae2e9219c3 | ||
|
|
146b374ca8 | ||
|
|
9264676407 | ||
|
|
eb74460fcf | ||
|
|
16ed582052 | ||
|
|
a7ddb81ad1 | ||
|
|
d09f3870b1 | ||
|
|
9a8e249964 | ||
|
|
c63d9cf0b9 | ||
|
|
f6648f80ec | ||
|
|
250bff7ef3 | ||
|
|
fa0b925aa9 | ||
|
|
28fe2949f2 | ||
|
|
d7f46216e9 | ||
|
|
3f0d82a427 | ||
|
|
d2811c9fba | ||
|
|
028e26ef59 | ||
|
|
4086332857 | ||
|
|
1eebfccf14 | ||
|
|
f1127f917d | ||
|
|
6cc8700bb9 | ||
|
|
3cabcd5cf9 | ||
|
|
c5aaeb67b5 | ||
|
|
47fcd54d1c | ||
|
|
5dd5cd8ae1 | ||
|
|
8ab18160a5 | ||
|
|
b70b697f3f | ||
|
|
48eb35e20d | ||
|
|
4fd899133f | ||
|
|
3b134cf8b5 | ||
|
|
86bc538601 | ||
|
|
fb00fbc850 | ||
|
|
309f9fcfa1 | ||
|
|
253aa63980 | ||
|
|
93dea13f20 | ||
|
|
3d3d1718b7 | ||
|
|
90c2a5d51f | ||
|
|
8846a229d8 | ||
|
|
acf0591adc | ||
|
|
95bee47826 | ||
|
|
b701976356 | ||
|
|
2b61941e34 | ||
|
|
1526ffa3af | ||
|
|
bb4227e537 | ||
|
|
778691405d | ||
|
|
cb7d3b91c6 | ||
|
|
fd8cf16336 | ||
|
|
2403cf222f | ||
|
|
20152dd8a6 | ||
|
|
b459207b7a | ||
|
|
8b009509c8 | ||
|
|
c456891a11 | ||
|
|
6b044d8086 | ||
|
|
3162de84ed | ||
|
|
641768240a | ||
|
|
8b7ade4b9c | ||
|
|
c7db9394c8 | ||
|
|
513e63329b | ||
|
|
cfd104385d | ||
|
|
2d9641eecf | ||
|
|
65a78125b2 | ||
|
|
28572ba4d3 | ||
|
|
28a603ca1e | ||
|
|
b9fa303f7f | ||
|
|
756dde90ab | ||
|
|
c2a396167e | ||
|
|
c1fd207723 | ||
|
|
8f1fc0a164 | ||
|
|
538ec3c744 | ||
|
|
6fed13bf12 | ||
|
|
104cc7c900 | ||
|
|
8fa699a92e | ||
|
|
1ab7059830 | ||
|
|
377b2613db | ||
|
|
bc7b0a9bdb | ||
|
|
8665a351db | ||
|
|
dd02bf8b8d | ||
|
|
487c423eef | ||
|
|
87bcfa729c | ||
|
|
0332e8c671 | ||
|
|
7264354ce6 | ||
|
|
bde3315994 | ||
|
|
7ecd08b0d8 | ||
|
|
93feb382ca | ||
|
|
186d9b3798 | ||
|
|
1f3e44819d | ||
|
|
4b942f2f45 | ||
|
|
7214cd7179 | ||
|
|
90c11b5c04 | ||
|
|
4007339d26 | ||
|
|
8bc43443a7 | ||
|
|
a389dc702b | ||
|
|
1b39fa37e0 | ||
|
|
bfa18f72fa | ||
|
|
82c2ec0c43 | ||
|
|
3810d088b5 | ||
|
|
7b34b096fe | ||
|
|
831b0bd2d1 | ||
|
|
5875057282 | ||
|
|
b78df8df2a | ||
|
|
7fa3d08e61 | ||
|
|
549303a34c | ||
|
|
9d2cbae115 | ||
|
|
d1a76922c5 | ||
|
|
648889d2d2 | ||
|
|
984e566fe2 | ||
|
|
145296ee10 | ||
|
|
91b91ca96f | ||
|
|
5ea1e9d6d5 | ||
|
|
78f83fe103 | ||
|
|
1b0ee6a757 | ||
|
|
c185853405 | ||
|
|
f50b8bceb6 | ||
|
|
559a10603a | ||
|
|
defe38daaa | ||
|
|
6acdaf0603 | ||
|
|
e9991fc995 | ||
|
|
7ad14174d7 | ||
|
|
a638387dd4 | ||
|
|
3818d0e607 | ||
|
|
3ce4f8da3a | ||
|
|
9d272a1b19 | ||
|
|
3b9224b4da | ||
|
|
b254152746 | ||
|
|
a2d796476e | ||
|
|
898e744767 | ||
|
|
9ef9c5b3de | ||
|
|
b734eeb252 | ||
|
|
92d69d9053 | ||
|
|
4879c49476 | ||
|
|
e81ea15bb0 | ||
|
|
647f174b53 | ||
|
|
d79e6a7606 | ||
|
|
3fd3916ef9 | ||
|
|
b9b73d44c2 | ||
|
|
afa7c2030c | ||
|
|
f668a7a629 | ||
|
|
958fb43ac2 | ||
|
|
9ef8f592ba | ||
|
|
e6feae6c68 | ||
|
|
9dc5810eaa | ||
|
|
c960d6d96a | ||
|
|
45d95d7b45 | ||
|
|
3d1c32a5b4 | ||
|
|
6a67580c33 | ||
|
|
dbafa0a473 | ||
|
|
d348e73b55 | ||
|
|
3f3c1bd6e3 | ||
|
|
1a3f907c62 | ||
|
|
4f8027c6f7 | ||
|
|
86cd2a976b | ||
|
|
52a935bf09 | ||
|
|
ea7c3c4a5b | ||
|
|
9639d98dc3 | ||
|
|
3bfc16b6a8 | ||
|
|
1e54619e81 | ||
|
|
fc4ac98246 | ||
|
|
534e3ee98f | ||
|
|
9b8b8f020d | ||
|
|
aea7fb8069 | ||
|
|
b933633cc0 | ||
|
|
a822eb521d | ||
|
|
b6bb19538f | ||
|
|
c0a1f86801 | ||
|
|
439d79c677 | ||
|
|
b6ee0f118e | ||
|
|
ed037ca6d7 | ||
|
|
58f6239cb2 | ||
|
|
5e7bfcb591 | ||
|
|
03cd99c43c | ||
|
|
dc1742aca6 | ||
|
|
a23142cc3f | ||
|
|
de47319b00 | ||
|
|
c9a2549018 | ||
|
|
966253f7b6 | ||
|
|
b0ce7fb3cf | ||
|
|
f1f85a6563 | ||
|
|
c10ee5e0a8 | ||
|
|
7e86d3128c | ||
|
|
4f3da0ea2e | ||
|
|
406c7d8c6b | ||
|
|
809f1192f4 | ||
|
|
ff280d27f1 | ||
|
|
c566eb95a2 | ||
|
|
393d80600c | ||
|
|
1e44165d18 | ||
|
|
03f4774810 | ||
|
|
b64e0446a3 | ||
|
|
e51cab2418 | ||
|
|
f533d106c1 | ||
|
|
2557691053 | ||
|
|
5f53f95b5d | ||
|
|
2c71574072 | ||
|
|
45d38494f2 | ||
|
|
5c03d0d14c | ||
|
|
8404294f56 | ||
|
|
7f519bb71f | ||
|
|
7a5995002d | ||
|
|
ba8fe8e830 | ||
|
|
949a5b19cc | ||
|
|
5a68a00f38 | ||
|
|
d52d91852a | ||
|
|
b8b39e1ebd | ||
|
|
e795e63d02 | ||
|
|
27dc58a90f | ||
|
|
d4679353af | ||
|
|
73a53c8829 | ||
|
|
eb51f78f8c | ||
|
|
8667443e64 | ||
|
|
0650472551 | ||
|
|
7653ea683a | ||
|
|
d633d2f424 | ||
|
|
98ec370545 | ||
|
|
51b9579d6e | ||
|
|
af42791fa2 | ||
|
|
f7e9a88575 | ||
|
|
331ac4787e | ||
|
|
4ac2e0aa49 | ||
|
|
0cb10c206a | ||
|
|
5fd031f518 | ||
|
|
48c204a7b0 | ||
|
|
e61fc1c84e | ||
|
|
fb64381af9 | ||
|
|
84954f1919 | ||
|
|
cdabe516eb | ||
|
|
e5ddb2cef9 | ||
|
|
963185b927 | ||
|
|
f7ef6a207e | ||
|
|
8088d14340 | ||
|
|
f710b782a3 | ||
|
|
b13921f9aa | ||
|
|
522191052a | ||
|
|
e9e8844342 | ||
|
|
ccbf7b24cf | ||
|
|
57f1417d4c | ||
|
|
d5318e6e88 | ||
|
|
978e3fc610 | ||
|
|
dcb5eef814 | ||
|
|
518ad838e7 | ||
|
|
ba6ced577e | ||
|
|
138253e728 | ||
|
|
ffd299962b | ||
|
|
354a147155 | ||
|
|
a623017325 | ||
|
|
654bd80abd | ||
|
|
3249591351 | ||
|
|
40f934d7a6 | ||
|
|
53717dcc46 | ||
|
|
f16a00fe2e | ||
|
|
ab1af0ce28 | ||
|
|
be444bbbc4 | ||
|
|
e161872604 | ||
|
|
e21fa015bf | ||
|
|
fb3275d89f | ||
|
|
f42e4ffc71 | ||
|
|
7b15c190d3 | ||
|
|
73c2d27620 | ||
|
|
288db125ce | ||
|
|
0effaae435 | ||
|
|
a71f1dd3c6 | ||
|
|
c08843d587 | ||
|
|
44094cd4f4 | ||
|
|
a4bd9d518c | ||
|
|
a739a6e24a | ||
|
|
955b52d54b | ||
|
|
004293008c | ||
|
|
9f26a0b512 | ||
|
|
b16f95a4ba | ||
|
|
c079531d2e | ||
|
|
773f2dc074 | ||
|
|
64d783179b | ||
|
|
3dafd6c277 | ||
|
|
fb2fdb10b2 | ||
|
|
50dedf84d5 | ||
|
|
d7b96a7dca | ||
|
|
1d1acdf7c8 | ||
|
|
440bc76f08 | ||
|
|
d2a598dbf5 | ||
|
|
931f9dab6f | ||
|
|
10ec2af398 | ||
|
|
46382ef89f | ||
|
|
c4116feda4 | ||
|
|
4c18342cf4 | ||
|
|
92f103c1dd | ||
|
|
8c9374e00a | ||
|
|
870f3d2ef9 | ||
|
|
d75b3596aa | ||
|
|
c388d84f57 | ||
|
|
782f6c9655 | ||
|
|
078d12f2a1 | ||
|
|
271e1aa26a | ||
|
|
c9c11c7ff7 | ||
|
|
75da4667b7 | ||
|
|
a2591f7f77 | ||
|
|
72b4e8bd39 | ||
|
|
740b0bc3b1 | ||
|
|
3e48b15db0 | ||
|
|
f1b8a22cc8 | ||
|
|
02b75fcb68 | ||
|
|
9b2541975f | ||
|
|
d3ff9a5b7c | ||
|
|
75acf7d132 | ||
|
|
d6a9430c20 | ||
|
|
5a89720f59 | ||
|
|
bee7133e81 | ||
|
|
8522f88d66 | ||
|
|
10ecc5c75d | ||
|
|
cdd722c975 | ||
|
|
7fd9b258aa | ||
|
|
5368d398fe | ||
|
|
30cc8c8711 | ||
|
|
f7d1fea2af | ||
|
|
ee5674bb6d | ||
|
|
5e1fa61c55 | ||
|
|
92f968f933 | ||
|
|
9eabd75864 | ||
|
|
df121e68b4 | ||
|
|
be0721fa5d | ||
|
|
d670872d73 | ||
|
|
68eb9162a9 | ||
|
|
a65ce23a10 | ||
|
|
bea9bca891 | ||
|
|
9f2c6fe8cc | ||
|
|
07e4276190 | ||
|
|
3db15c168d | ||
|
|
7e9f66ec61 | ||
|
|
c7ce4ec4e3 | ||
|
|
ad32aed204 | ||
|
|
2df9235516 | ||
|
|
a0efb0b9a4 | ||
|
|
a244741640 | ||
|
|
e3f5360a7f | ||
|
|
c280bae14f | ||
|
|
e99671ba81 | ||
|
|
73338462e5 | ||
|
|
1912e69375 | ||
|
|
60a3789e9c | ||
|
|
a89bc5a5ce | ||
|
|
08f3126f46 | ||
|
|
53e746146b | ||
|
|
1e7cc0d655 | ||
|
|
b37a81b297 | ||
|
|
6aec8118f2 | ||
|
|
756732803f | ||
|
|
a6b8abb9ce | ||
|
|
38feb54237 | ||
|
|
ee828de086 | ||
|
|
bc82638fea | ||
|
|
edb6f58f66 | ||
|
|
23b9a6e7bf | ||
|
|
0b3c6058e2 | ||
|
|
cb6572980e | ||
|
|
78e5bbb9dc | ||
|
|
12b52b3e79 | ||
|
|
e226749388 | ||
|
|
001cd0cdcd | ||
|
|
c3806348a0 | ||
|
|
c6a8d80d45 | ||
|
|
2babacd15a | ||
|
|
456ab29a34 | ||
|
|
1de201841a | ||
|
|
a5586fd4ea | ||
|
|
a14ac78957 | ||
|
|
1193ede1ca | ||
|
|
8ad75a1773 | ||
|
|
9ea1b44e64 | ||
|
|
b92ef7922d | ||
|
|
3d6eff639f | ||
|
|
011e761d7f | ||
|
|
a2fdb687a1 | ||
|
|
ff846e0cc1 | ||
|
|
df0ad0d9f1 | ||
|
|
716d1a6cba | ||
|
|
3e7468177d | ||
|
|
d4113e6715 | ||
|
|
7f7ce06d0e | ||
|
|
7cfea596fd | ||
|
|
7eae5d0f6e | ||
|
|
a390658ad9 | ||
|
|
ecc403cbbc | ||
|
|
e870ca856c | ||
|
|
e7b8fec063 | ||
|
|
7767878c4c | ||
|
|
63de20fe36 | ||
|
|
6114a9a8ed | ||
|
|
d5514ca0cd | ||
|
|
8cf0565211 | ||
|
|
32246b1d69 | ||
|
|
418dbf09ec | ||
|
|
a198d09b71 | ||
|
|
f3bb0e7b90 | ||
|
|
b3917e19a2 | ||
|
|
e7d8b7454f | ||
|
|
4d4660eed9 | ||
|
|
b80e47672d | ||
|
|
3b370841cc | ||
|
|
7ec2a9a0a5 | ||
|
|
e340a376ef | ||
|
|
a2709afc5c | ||
|
|
9c7ed95f23 | ||
|
|
524f1f99cb | ||
|
|
2ac6d7f2bb | ||
|
|
6b3fd9efae | ||
|
|
d99daa6538 | ||
|
|
13e0bba379 | ||
|
|
ed7c5bc537 | ||
|
|
03b7968b99 | ||
|
|
274d64246b | ||
|
|
4450053fd3 | ||
|
|
8c321f18e1 | ||
|
|
e015807edc | ||
|
|
4ebcd2f214 | ||
|
|
67cd8b7a61 | ||
|
|
0a802ca73b | ||
|
|
097f37bfbc | ||
|
|
524e42980a | ||
|
|
34eebc9406 | ||
|
|
65f20c232c | ||
|
|
d742fdae86 | ||
|
|
397e2baf6c | ||
|
|
4b1d951083 | ||
|
|
ab65a5a975 | ||
|
|
9947cbf47b | ||
|
|
f781ba0c6a | ||
|
|
e3c605a7a9 | ||
|
|
849e6761b8 | ||
|
|
407ef36b70 | ||
|
|
fef5ceb36d | ||
|
|
2e22f556d8 | ||
|
|
95b833402f | ||
|
|
c0e7418e7c | ||
|
|
4d72ef1efe | ||
|
|
a00bce399f | ||
|
|
2b8658b233 | ||
|
|
fc2784450b | ||
|
|
896d9bd3ae | ||
|
|
9199096f69 | ||
|
|
bdb2694734 | ||
|
|
98ab3cd3d0 | ||
|
|
22f7d3f9f4 | ||
|
|
7f27dafe32 | ||
|
|
37a1e6d4b5 | ||
|
|
91d5473bf2 | ||
|
|
b883cccb30 | ||
|
|
2ca7b3ba5f | ||
|
|
faada7ae7b | ||
|
|
3910fb778f | ||
|
|
4577d063cc | ||
|
|
1f5c70166e | ||
|
|
f920f9f08d | ||
|
|
e6272e88e6 | ||
|
|
4be24abf7f | ||
|
|
85cc05a144 | ||
|
|
7f726c4e2a | ||
|
|
5c86ffd2ac | ||
|
|
257444b640 | ||
|
|
c61d453de4 | ||
|
|
4b88b18e78 | ||
|
|
1125894e7a | ||
|
|
e8894360fa | ||
|
|
168c071ac3 | ||
|
|
d6dd67951e | ||
|
|
c7e4470a60 | ||
|
|
cb6789504a | ||
|
|
314664c4e2 | ||
|
|
58794f806c | ||
|
|
6a470a6448 | ||
|
|
27bf1713a0 | ||
|
|
c1e428dd3e | ||
|
|
661e5b50f8 | ||
|
|
749c2cf600 | ||
|
|
153e9e01bc | ||
|
|
c1797cc215 | ||
|
|
00eb31cd88 | ||
|
|
bd4b1e485b | ||
|
|
387eacf5ff | ||
|
|
5d00e79e0b | ||
|
|
3aac6a785d | ||
|
|
3506256dff | ||
|
|
5c49ab32e5 | ||
|
|
f128209c3a | ||
|
|
4a4254847a | ||
|
|
d21697367c | ||
|
|
1ceaab2788 | ||
|
|
0c56f20f55 | ||
|
|
261339c7e9 | ||
|
|
1d1335fed5 | ||
|
|
c9b5a53be3 | ||
|
|
11a037aeec | ||
|
|
0743bb5f54 | ||
|
|
9fe5163d86 | ||
|
|
acd107b1a2 | ||
|
|
1ff96df224 | ||
|
|
fb537c46d4 | ||
|
|
a588b454ae | ||
|
|
0f145800fa | ||
|
|
44dfa72f16 | ||
|
|
19152f498c | ||
|
|
056a61c9fe | ||
|
|
a0cfc5d267 | ||
|
|
eb6dbf0c67 | ||
|
|
af2083120e | ||
|
|
95d4c0e992 | ||
|
|
b14f67685a | ||
|
|
b82fa04aec | ||
|
|
93d1e8604a | ||
|
|
53d32dbcc2 | ||
|
|
da43d91b5a | ||
|
|
0694ab0777 | ||
|
|
207940426d | ||
|
|
58a204ed56 | ||
|
|
f68a2719f4 | ||
|
|
4311b019f4 | ||
|
|
c1b7ce0bcc | ||
|
|
fe0ca5403c | ||
|
|
c5c33ebfeb | ||
|
|
2c24843e4d | ||
|
|
b0e17e572c | ||
|
|
7cd5727ccc | ||
|
|
d02bafc7a6 | ||
|
|
ca4bdad32a | ||
|
|
4fab7f9180 | ||
|
|
224f54c676 | ||
|
|
fbc1dbb443 | ||
|
|
4da4e532c5 | ||
|
|
a16447faa2 | ||
|
|
a883b26dda | ||
|
|
b9937ee6ca | ||
|
|
40ed1d5841 | ||
|
|
0d3b86d06b | ||
|
|
f8464d6f23 | ||
|
|
d2995e3b6c | ||
|
|
eda37027d5 | ||
|
|
11f9e5cfa4 | ||
|
|
a59a02a6a7 | ||
|
|
26db867bd5 | ||
|
|
18a92cbaca | ||
|
|
62ac151847 | ||
|
|
0b3628e085 | ||
|
|
ffb23e7f34 | ||
|
|
678d861e22 | ||
|
|
652e08ebbe | ||
|
|
95672569c5 | ||
|
|
c9bedc9315 | ||
|
|
dbaeb39564 | ||
|
|
1e9801b0d4 | ||
|
|
eed82f2b6e | ||
|
|
a0f1545421 | ||
|
|
a69909ff23 | ||
|
|
65c99d3fbd | ||
|
|
687094fec0 | ||
|
|
757debaecc | ||
|
|
6d5dda80ee | ||
|
|
b9d9e965d5 | ||
|
|
e845df2a10 | ||
|
|
93cfc0e15d | ||
|
|
7a35bc2340 | ||
|
|
dc7eebb8ae | ||
|
|
1dfc967900 | ||
|
|
bb5217948d | ||
|
|
c2269711c5 | ||
|
|
e707b99860 | ||
|
|
ec43241ccc | ||
|
|
c45436dcf4 | ||
|
|
d41c3ee72b | ||
|
|
0058d0aa56 | ||
|
|
868638f031 | ||
|
|
a8250f17c3 | ||
|
|
c3bbc7e31c | ||
|
|
5d5565b832 | ||
|
|
5da0ff61b2 | ||
|
|
79efa87d39 | ||
|
|
a56981bc70 | ||
|
|
24f30bf18f | ||
|
|
0fa843fd78 | ||
|
|
aecee331a4 | ||
|
|
c1b7cc6d65 | ||
|
|
b8ddfa6537 | ||
|
|
fd4bd631e5 | ||
|
|
96b8901cde | ||
|
|
124033d122 | ||
|
|
cbb292b828 | ||
|
|
22b5fc2452 | ||
|
|
d549a314bc | ||
|
|
f32fe71dbc | ||
|
|
169cf14fa6 | ||
|
|
3befe007fd | ||
|
|
6d84bafa2e | ||
|
|
f3badc0ad4 | ||
|
|
df9887b629 | ||
|
|
cbecf5b954 | ||
|
|
040cccebe2 | ||
|
|
efffdf54c3 | ||
|
|
c3cb0c8968 | ||
|
|
d676c67b50 | ||
|
|
9316170c89 | ||
|
|
9101614d35 | ||
|
|
7312b2859d | ||
|
|
bf7d99ccba | ||
|
|
fb0d588cbf | ||
|
|
8e7bd6323a | ||
|
|
8bebd1933d | ||
|
|
2429b2c027 | ||
|
|
769820792f | ||
|
|
36f39b5023 | ||
|
|
bb675ee040 | ||
|
|
9b4aff7a3f | ||
|
|
e312d71286 | ||
|
|
a397c686b8 | ||
|
|
9b92005810 | ||
|
|
d4e0c8c3bb | ||
|
|
ae1ec9aaa2 | ||
|
|
e811bb9213 | ||
|
|
7de5d90c82 | ||
|
|
bf5265c15b | ||
|
|
49b111a0e9 | ||
|
|
85964d9f17 | ||
|
|
4f9075fa77 | ||
|
|
0856c9005c | ||
|
|
31d8bccfcf | ||
|
|
9213e3d3ef | ||
|
|
3665079cc9 | ||
|
|
44881e521c | ||
|
|
c136d8050b | ||
|
|
a5cc04b46f | ||
|
|
a9a8b6314b | ||
|
|
5c7be3bcfc | ||
|
|
e3893198af | ||
|
|
6a4e6dc5a1 | ||
|
|
643916f733 | ||
|
|
e964851d53 | ||
|
|
79e495dc14 | ||
|
|
13326879f0 | ||
|
|
293a871db4 | ||
|
|
4c9dc6350d | ||
|
|
4c54afdc55 | ||
|
|
d60841b040 | ||
|
|
7e640f47a5 | ||
|
|
f3d1b273f1 | ||
|
|
e80f5b85e9 | ||
|
|
1b5725caee | ||
|
|
820e8f8323 | ||
|
|
30672e52bb | ||
|
|
ac9796f50d | ||
|
|
66b7b4cad0 | ||
|
|
f417b9cc73 | ||
|
|
a304d4643c | ||
|
|
8386e5a2b5 | ||
|
|
27f57b96c0 | ||
|
|
d9a049d523 | ||
|
|
f53f724b04 | ||
|
|
a41105656a | ||
|
|
54269abfd1 | ||
|
|
e812ff1337 | ||
|
|
ae28f8f239 | ||
|
|
470f261dc0 | ||
|
|
80cfda23b6 | ||
|
|
5321c4d52b | ||
|
|
0466912f3e | ||
|
|
3d3932e657 | ||
|
|
d63117a17a | ||
|
|
ba06a72326 | ||
|
|
2671f6ca61 | ||
|
|
8dddfcbc92 | ||
|
|
ebc5793f36 | ||
|
|
4946221651 | ||
|
|
de9817a97f | ||
|
|
1218c4f385 | ||
|
|
e9cee62d03 | ||
|
|
fa7cf37eab | ||
|
|
b9ecb7556f | ||
|
|
661b38e1e6 | ||
|
|
769f4ba589 | ||
|
|
6118253ebf | ||
|
|
0f7f717330 | ||
|
|
0b16f1eb97 | ||
|
|
e733894c02 | ||
|
|
c47dcfbe27 | ||
|
|
8ae008ec9d | ||
|
|
45ef0c78a3 | ||
|
|
3ac46f239e | ||
|
|
1e106f7ada | ||
|
|
09738b2dd7 | ||
|
|
dc67a0b0fc | ||
|
|
1258bbc770 | ||
|
|
5960318a60 | ||
|
|
d4faf758f4 | ||
|
|
f88216b9a0 | ||
|
|
e21d648588 | ||
|
|
dbcbdc9bbc | ||
|
|
c5d16e8381 | ||
|
|
ac1aa6f3b1 | ||
|
|
866c222c74 | ||
|
|
06843477cb | ||
|
|
9a7a6fc6ab | ||
|
|
4986da415f | ||
|
|
cdbd20b794 | ||
|
|
49567ade65 | ||
|
|
4d857449d2 | ||
|
|
dc03621a14 | ||
|
|
b83cca17e2 | ||
|
|
343afffd40 | ||
|
|
163eac9511 | ||
|
|
167bfcb0b3 | ||
|
|
f0f94eb84c | ||
|
|
7123c9ca12 | ||
|
|
41e12d8e54 | ||
|
|
473ab14ae6 | ||
|
|
127ffd451c | ||
|
|
32bb43b66f | ||
|
|
765f5acbcc | ||
|
|
8ac137f22d | ||
|
|
10bbf6b157 | ||
|
|
390dc0605b | ||
|
|
6fda9f0691 | ||
|
|
0a1c5830d2 | ||
|
|
33efa2316d | ||
|
|
b3de9374a7 | ||
|
|
1d869e8d2b | ||
|
|
56156dd5ff | ||
|
|
35c9f55c41 | ||
|
|
e70e3559eb | ||
|
|
5957439475 | ||
|
|
322ed552ed | ||
|
|
bf848d5ef8 | ||
|
|
285057a9df | ||
|
|
4de62ffa55 | ||
|
|
f97fde1387 | ||
|
|
8a5bba625c | ||
|
|
108a209ac8 | ||
|
|
8390a4aba9 | ||
|
|
9740416e56 | ||
|
|
8cc82642f9 | ||
|
|
5f4804f4cf | ||
|
|
24dbee5d3d | ||
|
|
298f84d25e | ||
|
|
182c66f0a9 | ||
|
|
fd11c34646 | ||
|
|
6bcc7388bb | ||
|
|
60da0c0082 | ||
|
|
2b2b48bec4 | ||
|
|
d579f79399 | ||
|
|
bf85df68b3 | ||
|
|
e6e7ec49df | ||
|
|
e7801e1222 | ||
|
|
3327254022 | ||
|
|
d53d09e358 | ||
|
|
34c02ac391 | ||
|
|
cf4eb0c9cd | ||
|
|
ee6220b1fe | ||
|
|
a264638726 | ||
|
|
3e6a10a57d | ||
|
|
5258816770 | ||
|
|
fb4457f9a1 | ||
|
|
627a74038c | ||
|
|
54c297e898 | ||
|
|
a4d7201f7d | ||
|
|
b04cf69b90 | ||
|
|
ea654f0b8d | ||
|
|
9b16aaf9e4 | ||
|
|
87672de761 | ||
|
|
171a8def6d | ||
|
|
f09179f4cd | ||
|
|
03f62068b3 | ||
|
|
828b4230e3 | ||
|
|
2b23e5d472 | ||
|
|
17dbe70460 | ||
|
|
587c3a0647 | ||
|
|
83f0879b11 | ||
|
|
212b741828 | ||
|
|
ee81b608f9 | ||
|
|
ad17080ee8 | ||
|
|
6a8611c348 | ||
|
|
3ba3610b21 | ||
|
|
8deed23e01 | ||
|
|
356106263b | ||
|
|
0566244b61 | ||
|
|
17d76b4e6f | ||
|
|
9c3490f275 | ||
|
|
b1b6499d7c | ||
|
|
a36a74091f | ||
|
|
4dfbd676af | ||
|
|
f72f8b3e39 | ||
|
|
45cc6880f9 | ||
|
|
17281cd3b1 | ||
|
|
332b452b56 | ||
|
|
c647515186 | ||
|
|
6faf0ed9b9 | ||
|
|
fad024d9bb | ||
|
|
67718df278 | ||
|
|
273c3283c2 | ||
|
|
aa7306ebd1 | ||
|
|
56cb173ad5 | ||
|
|
174f0a9393 | ||
|
|
a1dbc1a6fb | ||
|
|
d9de2ee0bc | ||
|
|
cf1e0eda27 | ||
|
|
ccc82e5a61 | ||
|
|
0859f939dd | ||
|
|
519c133c0c | ||
|
|
9f62860821 | ||
|
|
238ed1ac2c | ||
|
|
cd5903b358 | ||
|
|
f78125c4fb | ||
|
|
6394241b9f | ||
|
|
d4dfc9bbab | ||
|
|
cf668be964 | ||
|
|
6ef443d724 | ||
|
|
75af217864 | ||
|
|
7a7343c3c8 | ||
|
|
18bfa8f562 | ||
|
|
a76a0815a0 | ||
|
|
332f384a65 | ||
|
|
3c5e1216c8 | ||
|
|
c7c11a1c1e | ||
|
|
0dd993c1d0 | ||
|
|
c977e9b59c | ||
|
|
81055b5500 | ||
|
|
c8df446846 | ||
|
|
97ac4621b9 | ||
|
|
9346c474cf | ||
|
|
d9a55965d3 | ||
|
|
50306343a8 | ||
|
|
08d7457620 | ||
|
|
b80a382d2f | ||
|
|
ab066bb441 | ||
|
|
4a64260d6e | ||
|
|
9390077007 | ||
|
|
baf5386e34 | ||
|
|
86e72b2f7f | ||
|
|
70bc02b12e | ||
|
|
6bce8b0195 | ||
|
|
dd4afb1d0a | ||
|
|
95493fe157 | ||
|
|
42b9ef3956 | ||
|
|
97ee7b7a7a | ||
|
|
230860f773 | ||
|
|
5f7f6d975e | ||
|
|
e7c95fdcc1 | ||
|
|
933367a685 | ||
|
|
4857307533 | ||
|
|
1ee50e42ca | ||
|
|
5bbc108331 | ||
|
|
11d62516d9 | ||
|
|
ef714a8bfc | ||
|
|
c6b8441a79 | ||
|
|
200ad86575 | ||
|
|
54fe2f260f | ||
|
|
e056cf743d | ||
|
|
8dbc747928 | ||
|
|
969b37e4e9 | ||
|
|
79a111668e | ||
|
|
9c90e3110b | ||
|
|
59237fb9c6 | ||
|
|
fc3705f019 | ||
|
|
15adbc0823 | ||
|
|
ca6472f5f2 | ||
|
|
de38322f34 | ||
|
|
ed45d30639 | ||
|
|
f99fc6f15f | ||
|
|
56aaa082da | ||
|
|
0aaa2f20bd | ||
|
|
bac589e3ec | ||
|
|
1dc7b02a3c | ||
|
|
83965afe68 | ||
|
|
0e9b110023 | ||
|
|
44f01a77fd | ||
|
|
58552b7a50 | ||
|
|
ddf3feac51 | ||
|
|
c2d59fcee1 | ||
|
|
ee3b2592aa | ||
|
|
ed79dfb220 | ||
|
|
6fbb942ad9 | ||
|
|
94a9340ee3 | ||
|
|
ef6d2b7d2b | ||
|
|
7f0b603f60 | ||
|
|
04ec31ea5c | ||
|
|
d1ff36c44a | ||
|
|
d703443469 | ||
|
|
5455a02c4d | ||
|
|
4bbb186f63 | ||
|
|
b62e8bf976 | ||
|
|
7ccc4fe33a | ||
|
|
b52d1cf1e2 | ||
|
|
6ee9db0eb8 | ||
|
|
36ac2a5bfb | ||
|
|
e1841c652a | ||
|
|
6fc2c88482 | ||
|
|
b8a13a5ba4 | ||
|
|
d36623c1fb | ||
|
|
06ab88556c | ||
|
|
b552790035 | ||
|
|
6610dd22df | ||
|
|
04dddf92d8 | ||
|
|
bedd9c97c2 | ||
|
|
81c859ba31 | ||
|
|
041e662e07 | ||
|
|
65c7c66b50 | ||
|
|
287f2762f9 | ||
|
|
3750e94ff5 | ||
|
|
e127d60345 | ||
|
|
5189298884 | ||
|
|
5a9c370b29 | ||
|
|
ec4f370f29 | ||
|
|
da194dd88d | ||
|
|
91e6b86eb3 | ||
|
|
550e2d59b4 | ||
|
|
80b7ef08c8 | ||
|
|
7c9ddf0c3f | ||
|
|
9abdb1d47b |
11
.github/ISSUE_TEMPLATE/bug_report.md
vendored
11
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -11,10 +11,10 @@ assignees: ''
|
||||
|
||||
IMPORTANT: Read this first!!!
|
||||
|
||||
1. If you own a Radzen Professional or Еnterprise subscription you can report your issue or ask us a question via email at info@radzen.com. Radzen staff will reply within 24 hours (Professional) or 16 hours (Enterprise)
|
||||
1. If you own a Radzen Blazor Pro or Team subscription you can also report your issue or ask us a question via email at info@radzen.com. Radzen staff will reply within 24 hours (Pro) or 16 hours (Team)
|
||||
2. The Radzen staff guarantees a response to issues in this repo only to paid subscribers.
|
||||
3. If you have a HOW TO question start a new forum thread in the Radzen Community forum: https://forum.radzen.com. Radzen staff will close issues that are HOWTO questions.
|
||||
4. Please adhere to the issue template. Specify all the steps required to reproduce the issue or link a project which reproduces it easily (without requiring extra steps such as restoring a database).
|
||||
4. Please adhere to the issue template. Specify all the steps required to reproduce the issue.
|
||||
-->
|
||||
|
||||
**Describe the bug**
|
||||
@@ -27,7 +27,12 @@ Steps to reproduce the behavior:
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
Alternatively link your repo with a sample project that can be run.
|
||||
Alternatively make a new [playground](https://blazor.radzen.com/playground) snippet and paste its URL.
|
||||
1. Go to any live demo at https://blazor.radzen.com
|
||||
2. Click the **Edit Source** tab.
|
||||
3. Then click **Open in Playground**.
|
||||
4. Reproduce the problem and save the snippet.
|
||||
5. Copy the snippet URL and provide it in the issue description.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -5,4 +5,4 @@ contact_links:
|
||||
about: Please ask and answer questions here.
|
||||
- name: Radzen Commercial Support
|
||||
url: info@radzen.com
|
||||
about: Radzen Professional or Enterprise subscribers can get dedicated support over email.
|
||||
about: Radzen Blazor subscribers can get dedicated support over email.
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -11,7 +11,7 @@ assignees: ''
|
||||
|
||||
IMPORTANT: Read this first!!!
|
||||
|
||||
1. If you own a Radzen Professional or Еnterprise subscription you can request your feature via email at info@radzen.com. Radzen staff will reply within 24 hours (Professional) or 16 hours (Enterprise)
|
||||
1. If you own a Radzen Blazor subscription you can request your feature via email at info@radzen.com. Radzen staff will reply within 24 hours (Pro) or 16 hours (Team)
|
||||
2. The Radzen staff guarantees a response to issues in this repo only to paid subscribers.
|
||||
3. If you have a HOW TO question start a new forum thread in the Radzen Community forum: https://forum.radzen.com. Radzen staff will close issues that are HOWTO questions.
|
||||
4. Please adhere to the issue template.
|
||||
|
||||
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -15,11 +15,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build Radzen.Blazor/Radzen.Blazor.csproj
|
||||
- name: Test
|
||||
|
||||
66
.github/workflows/deploy.yml
vendored
Normal file
66
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
name: Deploy to blazor.radzen.com
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: blazor-radzen-prod
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Compute image tag (timestamp)
|
||||
id: meta
|
||||
run: |
|
||||
TAG=$(date -u +"%Y%m%d%H%M%S")
|
||||
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG}"
|
||||
echo "image=$IMAGE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.image }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Push to dokku
|
||||
uses: dokku/github-action@master
|
||||
with:
|
||||
git_remote_url: ${{ secrets.DOKKU_REPO }}
|
||||
ssh_private_key: ${{ secrets.DOKKU_SSH_PRIVATE_KEY }}
|
||||
deploy_docker_image: ${{ steps.meta.outputs.image }}
|
||||
|
||||
- name: Prune GHCR versions (keep 1)
|
||||
uses: actions/delete-package-versions@v5
|
||||
with:
|
||||
package-name: radzen-blazor
|
||||
package-type: container
|
||||
min-versions-to-keep: 1
|
||||
delete-only-pre-release-versions: false
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -341,3 +341,10 @@ Radzen.DocFX/_exported_templates
|
||||
Radzen.DocFX/api/*.yml
|
||||
!Radzen.DocFX/api/index.md
|
||||
Radzen.DocFX/api/.manifest
|
||||
Radzen.Blazor.min.js
|
||||
/.claude
|
||||
/SANKEY_PATTERN_COMPARISON.md
|
||||
*.md
|
||||
/.gitignore
|
||||
/.gitignore
|
||||
RadzenBlazorDemos/wwwroot/demos
|
||||
|
||||
@@ -19,7 +19,7 @@ You can ask your question here. Please use the [Radzen.Blazor Components](https:
|
||||
|
||||
### Dedicated technical support
|
||||
|
||||
Radzen staff provides technical support with guaranteed response time to Radzen Professional and Enterprise subscribers. The pricing options are available [here](https://www.radzen.com/blazor-studio/pricing/).
|
||||
Radzen staff provides technical support with guaranteed response time to Radzen Professional and Enterprise subscribers. The pricing options are available [here](https://www.radzen.com/pricing).
|
||||
|
||||
## How Can I Contribute?
|
||||
|
||||
|
||||
85
Directory.Build.props
Normal file
85
Directory.Build.props
Normal file
@@ -0,0 +1,85 @@
|
||||
<Project>
|
||||
<!--
|
||||
Common build properties for all projects in the Radzen.Blazor solution.
|
||||
|
||||
To use this file:
|
||||
1. Rename to Directory.Build.props (remove .sample extension)
|
||||
2. Adjust settings based on your needs
|
||||
3. Review the analyzer settings in .editorconfig
|
||||
|
||||
This file will be automatically imported by all projects in subdirectories.
|
||||
-->
|
||||
|
||||
<PropertyGroup Label="Language Configuration">
|
||||
<!-- Use latest C# language features -->
|
||||
<LangVersion>latest</LangVersion>
|
||||
|
||||
<!-- Do NOT enable implicit usings - explicit imports preferred for library code -->
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<NoWarn>CA2007</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Code Analysis Configuration">
|
||||
<!-- Enable .NET code analyzers -->
|
||||
<AnalysisLevel>latest</AnalysisLevel>
|
||||
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
||||
|
||||
<!-- Run analyzers during build and in IDE -->
|
||||
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
|
||||
<RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>
|
||||
|
||||
<!-- Don't enforce code style in build (yet) - just show warnings -->
|
||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||
|
||||
<!-- Don't treat warnings as errors (yet) - too many to fix immediately -->
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
|
||||
<!-- Report all analyzer diagnostics -->
|
||||
<AnalysisMode>All</AnalysisMode>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Build Quality">
|
||||
<!-- Enable deterministic builds for reproducibility -->
|
||||
<Deterministic>true</Deterministic>
|
||||
|
||||
<!-- Enable deterministic builds in CI/CD -->
|
||||
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
|
||||
|
||||
<!-- Embed source files for better debugging -->
|
||||
<EmbedAllSources>true</EmbedAllSources>
|
||||
<!--
|
||||
IMPORTANT:
|
||||
- NuGet symbol packages (.snupkg) require portable PDB files.
|
||||
- If DebugType=embedded, there are no standalone PDBs, so the .snupkg ends up effectively empty.
|
||||
Use portable PDBs when symbols are enabled; otherwise use embedded for local debugging convenience.
|
||||
-->
|
||||
<!--
|
||||
NOTE: Directory.Build.props is imported before project files, so properties like IncludeSymbols
|
||||
set in a .csproj may not be available yet for Conditions here.
|
||||
IsPacking *is* set by `dotnet pack`, so use that to switch DebugType for symbol packages.
|
||||
-->
|
||||
<DebugType Condition="'$(IsPacking)' == 'true'">portable</DebugType>
|
||||
<DebugType Condition="'$(IsPacking)' != 'true'">embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Demos and Tests Project Configuration" Condition="$(MSBuildProjectName.Contains('Demos')) OR $(MSBuildProjectName.Contains('Tests'))">
|
||||
<!-- Demo projects and Tests should not be packable -->
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
<!-- DISABLE ALL ANALYZERS FOR DEMO PROJECTS AND TESTS -->
|
||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
|
||||
<RunAnalyzersDuringLiveAnalysis>false</RunAnalyzersDuringLiveAnalysis>
|
||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Performance">
|
||||
<!-- Optimize startup time -->
|
||||
<TieredCompilation>true</TieredCompilation>
|
||||
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
63
Dockerfile
63
Dockerfile
@@ -1,31 +1,48 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM mono:latest
|
||||
# =============================
|
||||
# BUILD STAGE
|
||||
# =============================
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||
WORKDIR /src
|
||||
|
||||
ENV DOCFX_VER 2.58.4
|
||||
# Copy project files first for better caching
|
||||
COPY Radzen.Blazor/*.csproj Radzen.Blazor/
|
||||
COPY RadzenBlazorDemos/*.csproj RadzenBlazorDemos/
|
||||
COPY RadzenBlazorDemos.Host/*.csproj RadzenBlazorDemos.Host/
|
||||
|
||||
RUN apt-get update && apt-get install unzip wget git -y && wget -q -P /tmp https://github.com/dotnet/docfx/releases/download/v${DOCFX_VER}/docfx.zip && \
|
||||
mkdir -p /opt/docfx && \
|
||||
unzip /tmp/docfx.zip -d /opt/docfx && \
|
||||
echo '#!/bin/bash\nmono /opt/docfx/docfx.exe $@' > /usr/bin/docfx && \
|
||||
chmod +x /usr/bin/docfx && \
|
||||
rm -rf /tmp/*
|
||||
# Radzen.DocFX usually has no csproj → copy full folder
|
||||
COPY Radzen.DocFX/ Radzen.DocFX/
|
||||
|
||||
COPY Radzen.Blazor /app/Radzen.Blazor
|
||||
COPY Radzen.DocFX /app/DocFX
|
||||
COPY RadzenBlazorDemos /app/RadzenBlazorDemos
|
||||
COPY RadzenBlazorDemos.Host /app/RadzenBlazorDemos.Host
|
||||
# Restore dependencies
|
||||
RUN dotnet restore RadzenBlazorDemos.Host/RadzenBlazorDemos.Host.csproj
|
||||
|
||||
# Copy full source after restore layer
|
||||
COPY . .
|
||||
|
||||
# Install docfx (build stage only)
|
||||
RUN dotnet tool install -g docfx
|
||||
ENV PATH="$PATH:/root/.dotnet/tools"
|
||||
|
||||
# Build shared project (keep net8.0 if required)
|
||||
RUN dotnet build -c Release Radzen.Blazor/Radzen.Blazor.csproj -f net8.0
|
||||
|
||||
# Generate documentation
|
||||
RUN docfx Radzen.DocFX/docfx.json
|
||||
|
||||
# Publish the Blazor host app
|
||||
WORKDIR /src/RadzenBlazorDemos.Host
|
||||
RUN dotnet publish -c Release -o /app/out
|
||||
|
||||
|
||||
# =============================
|
||||
# RUNTIME STAGE
|
||||
# =============================
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
|
||||
WORKDIR /app
|
||||
RUN docfx DocFX/docfx.json
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0
|
||||
# Copy only published output
|
||||
COPY --from=build /app/out ./
|
||||
|
||||
COPY --from=0 /app/RadzenBlazorDemos.Host /app/RadzenBlazorDemos.Host
|
||||
COPY --from=0 /app/RadzenBlazorDemos /app/RadzenBlazorDemos
|
||||
|
||||
WORKDIR /app/RadzenBlazorDemos.Host
|
||||
RUN dotnet publish -c Release -o out
|
||||
|
||||
ENV ASPNETCORE_URLS http://*:5000
|
||||
WORKDIR /app/RadzenBlazorDemos.Host/out
|
||||
# Set runtime URL
|
||||
ENV ASPNETCORE_URLS=http://+:5000
|
||||
|
||||
ENTRYPOINT ["dotnet", "RadzenBlazorDemos.Host.dll"]
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2024 Radzen Ltd
|
||||
Copyright (c) 2018-2026 Radzen Ltd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
48
README.md
48
README.md
@@ -1,39 +1,13 @@
|
||||

|
||||

|
||||
|
||||
<h1 align="center">
|
||||
Radzen Blazor Components
|
||||
</h1>
|
||||
Radzen Blazor Components
|
||||
========================
|
||||
|
||||
<p align="center">
|
||||
A set of <strong>70+ free and open source</strong> native Blazor UI controls.
|
||||
</p>
|
||||
The most sophisticated free UI component library for Blazor, featuring **100+ native components**.
|
||||
|
||||
<div align="center">
|
||||
See Online Demos or Read the Docs
|
||||
|
||||
[See Online Demos](https://blazor.radzen.com) or [Read the Docs](https://blazor.radzen.com/docs/)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/radzenhq/radzen-blazor/blob/master/LICENSE">
|
||||
<img alt="License - MIT" src="https://img.shields.io/github/license/radzenhq/radzen-blazor?logo=github&style=for-the-badge" />
|
||||
</a>
|
||||
<a href="https://www.nuget.org/packages/Radzen.Blazor">
|
||||
<img alt="NuGet Downloads" src="https://img.shields.io/nuget/dt/Radzen.Blazor?color=%232694F9&label=nuget%20downloads&logo=nuget&style=for-the-badge" />
|
||||
</a>
|
||||
<img alt="Last Commit" src="https://img.shields.io/github/last-commit/radzenhq/radzen-blazor?logo=github&style=for-the-badge" />
|
||||
<a href="https://github.com/radzenhq/radzen-blazor/graphs/contributors">
|
||||
<img alt="Github Contributors" src="https://img.shields.io/github/contributors/radzenhq/radzen-blazor?logo=github&style=for-the-badge" />
|
||||
</a>
|
||||
<a href="https://blazor.radzen.com">
|
||||
<img alt="Radzen Blazor Components - Online Demos" src="https://img.shields.io/badge/demos-online-brightgreen?color=%232694F9&logo=blazor&style=for-the-badge" />
|
||||
</a>
|
||||
<a href="https://blazor.radzen.com/docs">
|
||||
<img alt="Radzen Blazor Components - Documentation" src="https://img.shields.io/badge/docs-online-brightgreen?color=%232694F9&logo=blazor&style=for-the-badge" />
|
||||
</a>
|
||||
</p>
|
||||
[](https://github.com/radzenhq/radzen-blazor/blob/master/LICENSE)[ ](https://www.nuget.org/packages/Radzen.Blazor) [ ](https://github.com/radzenhq/radzen-blazor/graphs/contributors)[ ](https://blazor.radzen.com)[](https://blazor.radzen.com/docs)
|
||||
|
||||
## Why choose Radzen Blazor Components?
|
||||
|
||||
@@ -41,7 +15,7 @@
|
||||
|
||||
Radzen Blazor Components are open source and free for commercial use. You can install them from [NuGet](https://www.nuget.org/packages/Radzen.Blazor) or build your own copy from source.
|
||||
|
||||
Paid support is available as part of the [Radzen Professional subscription](https://www.radzen.com/blazor-studio/pricing/).
|
||||
Paid support is available as part of the [Radzen Blazor subscription](https://www.radzen.com/pricing).
|
||||
|
||||
### :computer: Native
|
||||
|
||||
@@ -64,9 +38,9 @@ Everybody is welcome to visit the [Radzen Community forum](https://forum.radzen.
|
||||
|
||||
The Radzen team monitors the forum threads, but does not guarantee a response to every question. For guaranteed responses you may consider the dedicated support option.
|
||||
|
||||
Dedicated support for the Radzen Blazor Components is available as part of the [Radzen Professional subscription](https://www.radzen.com/blazor-studio/pricing/).
|
||||
Dedicated support for the Radzen Blazor Components is available as part of the [Radzen Blazor subscription](https://www.radzen.com/pricing).
|
||||
|
||||
Our flagship product [Radzen Blazor Studio](https://www.radzen.com/blazor-studio/) provides tons of productivity features for Blazor developers:
|
||||
Our flagship product [Radzen Blazor Studio](https://www.radzen.com/blazor-studio) provides tons of productivity features for Blazor developers:
|
||||
- An industry-leading WYSIWYG Blazor design time canvas
|
||||
- Scaffolding a complete CRUD applications from a database
|
||||
- Built-in security - authentication and authorization
|
||||
@@ -76,8 +50,8 @@ Our flagship product [Radzen Blazor Studio](https://www.radzen.com/blazor-studio
|
||||
|
||||
## Get started with Radzen Blazor Components
|
||||
|
||||
Check the [getting started](https://blazor.radzen.com/getting-started) instructions to start making awesome Blazor applications.
|
||||
Check the [getting started](https://blazor.radzen.com/get-started) instructions to start making awesome Blazor applications.
|
||||
|
||||
## Run demos locally
|
||||
|
||||
Use Radzen.Server.sln to open and run demos as Blazor server application or Radzen.WebAssembly.sln to open and run demos as Blazor WebAssembly application. Radzen.sln has reference to all projects including tests.
|
||||
Use **Radzen.Server.sln** to open and run demos as Blazor server application or **Radzen.WebAssembly.sln** to open and run demos as Blazor WebAssembly application. The demos require the .NET 10 SDK and should preferably be opened in VS2026.
|
||||
|
||||
171
Radzen.Blazor.Tests/AIChatTests.cs
Normal file
171
Radzen.Blazor.Tests/AIChatTests.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using Bunit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Radzen;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class AIChatTests
|
||||
{
|
||||
private void RegisterChatService(TestContext ctx)
|
||||
{
|
||||
// Register a dummy HttpClient and default options for AIChatService
|
||||
ctx.Services.AddSingleton(new HttpClient());
|
||||
ctx.Services.AddScoped<IAIChatService, AIChatService>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenAIChat_ShouldRenderWithDefaultProperties()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
RegisterChatService(ctx);
|
||||
var component = ctx.RenderComponent<RadzenAIChat>();
|
||||
Assert.Contains("Type your message...", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenAIChat_ShouldRenderWithCustomTitle()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
RegisterChatService(ctx);
|
||||
var component = ctx.RenderComponent<RadzenAIChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Custom Chat"));
|
||||
Assert.Contains("Custom Chat", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenAIChat_ShouldRenderWithCustomPlaceholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
RegisterChatService(ctx);
|
||||
var component = ctx.RenderComponent<RadzenAIChat>(parameters => parameters
|
||||
.Add(p => p.Placeholder, "Enter your message here..."));
|
||||
Assert.Contains("Enter your message here...", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenAIChat_ShouldRenderWithCustomEmptyMessage()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
RegisterChatService(ctx);
|
||||
var component = ctx.RenderComponent<RadzenAIChat>(parameters => parameters
|
||||
.Add(p => p.EmptyMessage, "No messages yet"));
|
||||
Assert.Contains("No messages yet", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenAIChat_ShouldShowClearButtonByDefault()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
RegisterChatService(ctx);
|
||||
var component = ctx.RenderComponent<RadzenAIChat>();
|
||||
Assert.Contains("rz-chat-header-clear", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenAIChat_ShouldHideClearButtonWhenShowClearButtonIsFalse()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
RegisterChatService(ctx);
|
||||
var component = ctx.RenderComponent<RadzenAIChat>(parameters => parameters
|
||||
.Add(p => p.ShowClearButton, false));
|
||||
Assert.DoesNotContain("clear_all", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenAIChat_ShouldBeDisabledWhenDisabledIsTrue()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
RegisterChatService(ctx);
|
||||
var component = ctx.RenderComponent<RadzenAIChat>(parameters => parameters
|
||||
.Add(p => p.Disabled, true));
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenAIChat_ShouldBeReadOnlyWhenReadOnlyIsTrue()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
RegisterChatService(ctx);
|
||||
var component = ctx.RenderComponent<RadzenAIChat>(parameters => parameters
|
||||
.Add(p => p.ReadOnly, true));
|
||||
Assert.Contains("readonly", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenAIChat_ShouldHaveCorrectCssClass()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
RegisterChatService(ctx);
|
||||
var component = ctx.RenderComponent<RadzenAIChat>();
|
||||
Assert.Contains("rz-chat", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ChatMessage_ShouldHaveCorrectProperties()
|
||||
{
|
||||
// Arrange
|
||||
var message = new ChatMessage
|
||||
{
|
||||
Content = "Test message",
|
||||
IsUser = true,
|
||||
Timestamp = DateTime.Now
|
||||
};
|
||||
// Assert
|
||||
Assert.NotEmpty(message.Id);
|
||||
Assert.Equal("Test message", message.Content);
|
||||
Assert.True(message.IsUser);
|
||||
Assert.False(message.IsStreaming);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenAIChat_AddMessage_ShouldAddMessageToList()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
RegisterChatService(ctx);
|
||||
var component = ctx.RenderComponent<RadzenAIChat>();
|
||||
// Act
|
||||
component.Instance.AddMessage("Test message", true);
|
||||
// Assert
|
||||
var messages = component.Instance.GetMessages();
|
||||
Assert.Single(messages);
|
||||
Assert.Equal("Test message", messages[0].Content);
|
||||
Assert.True(messages[0].IsUser);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenAIChat_ClearChat_ShouldRemoveAllMessages()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
RegisterChatService(ctx);
|
||||
var component = ctx.RenderComponent<RadzenAIChat>();
|
||||
component.Instance.AddMessage("Test message 1", true);
|
||||
component.Instance.AddMessage("Test message 2", false);
|
||||
// Act
|
||||
component.InvokeAsync(async () => await component.Instance.ClearChat()).Wait();
|
||||
// Assert
|
||||
Assert.Empty(component.Instance.GetMessages());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenAIChat_ShouldLimitMessagesToMaxMessages()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
RegisterChatService(ctx);
|
||||
var component = ctx.RenderComponent<RadzenAIChat>(parameters => parameters.Add(p => p.MaxMessages, 3));
|
||||
component.Instance.AddMessage("Message 1", true);
|
||||
component.Instance.AddMessage("Message 2", false);
|
||||
component.Instance.AddMessage("Message 3", true);
|
||||
component.Instance.AddMessage("Message 4", false);
|
||||
// Assert
|
||||
var messages = component.Instance.GetMessages();
|
||||
Assert.Equal(3, messages.Count);
|
||||
Assert.Equal("Message 2", messages[0].Content);
|
||||
Assert.Equal("Message 3", messages[1].Content);
|
||||
Assert.Equal("Message 4", messages[2].Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
195
Radzen.Blazor.Tests/AccordionTests.cs
Normal file
195
Radzen.Blazor.Tests/AccordionTests.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class AccordionTests
|
||||
{
|
||||
[Fact]
|
||||
public void Accordion_Renders_CssClasses()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAccordion>();
|
||||
|
||||
Assert.Contains(@"rz-accordion", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_Renders_AccordionItems()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenAccordionItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Test Item");
|
||||
builder.AddAttribute(2, "ChildContent", (RenderFragment)(contentBuilder =>
|
||||
{
|
||||
contentBuilder.AddContent(0, "Item Content");
|
||||
}));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("Test Item", component.Markup);
|
||||
Assert.Contains("Item Content", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_Renders_ItemWithIcon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenAccordionItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Orders");
|
||||
builder.AddAttribute(2, "Icon", "account_balance_wallet");
|
||||
builder.AddAttribute(3, "ChildContent", (RenderFragment)(contentBuilder =>
|
||||
{
|
||||
contentBuilder.AddContent(0, "Order Details");
|
||||
}));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("account_balance_wallet", component.Markup);
|
||||
Assert.Contains("Orders", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_SingleExpand_OnlyOneItemExpanded()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, false); // Single expand mode
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
// Add first item
|
||||
builder.OpenComponent<RadzenAccordionItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Item 1");
|
||||
builder.AddAttribute(2, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Content 1")));
|
||||
builder.CloseComponent();
|
||||
|
||||
// Add second item
|
||||
builder.OpenComponent<RadzenAccordionItem>(1);
|
||||
builder.AddAttribute(1, "Text", "Item 2");
|
||||
builder.AddAttribute(2, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Content 2")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.False(component.Instance.Multiple);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_MultipleExpand_AllowsMultipleItemsExpanded()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
});
|
||||
|
||||
Assert.True(component.Instance.Multiple);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_Raises_ExpandEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var expandRaised = false;
|
||||
int expandedIndex = -1;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Expand, EventCallback.Factory.Create<int>(this, (index) =>
|
||||
{
|
||||
expandRaised = true;
|
||||
expandedIndex = index;
|
||||
}));
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenAccordionItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Test Item");
|
||||
builder.AddAttribute(2, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Content")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Find and click the accordion header link to expand
|
||||
var header = component.Find(".rz-accordion-header a");
|
||||
header.Click();
|
||||
|
||||
Assert.True(expandRaised);
|
||||
Assert.Equal(0, expandedIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_Raises_CollapseEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var collapseRaised = false;
|
||||
int collapsedIndex = -1;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Collapse, EventCallback.Factory.Create<int>(this, (index) =>
|
||||
{
|
||||
collapseRaised = true;
|
||||
collapsedIndex = index;
|
||||
}));
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenAccordionItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Test Item");
|
||||
builder.AddAttribute(2, "Selected", true); // Start expanded
|
||||
builder.AddAttribute(3, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Content")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Find and click the accordion header link to collapse
|
||||
var header = component.Find(".rz-accordion-header a");
|
||||
header.Click();
|
||||
|
||||
Assert.True(collapseRaised);
|
||||
Assert.Equal(0, collapsedIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accordion_DisabledItem_CannotExpand()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var expandRaised = false;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAccordion>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Expand, EventCallback.Factory.Create<int>(this, (_) => expandRaised = true));
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenAccordionItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Disabled Item");
|
||||
builder.AddAttribute(2, "Disabled", true);
|
||||
builder.AddAttribute(3, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Content")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Try to click the disabled item
|
||||
var header = component.Find(".rz-accordion-header a");
|
||||
header.Click();
|
||||
|
||||
// Event should not be raised for disabled item
|
||||
Assert.False(expandRaised);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
195
Radzen.Blazor.Tests/AlertTests.cs
Normal file
195
Radzen.Blazor.Tests/AlertTests.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class AlertTests
|
||||
{
|
||||
[Fact]
|
||||
public void Alert_Renders_CssClasses()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
Assert.Contains(@"rz-alert", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Renders_AlertStyle()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AlertStyle, AlertStyle.Danger));
|
||||
Assert.Contains("rz-danger", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AlertStyle, AlertStyle.Success));
|
||||
Assert.Contains("rz-success", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AlertStyle, AlertStyle.Warning));
|
||||
Assert.Contains("rz-warning", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AlertStyle, AlertStyle.Info));
|
||||
Assert.Contains("rz-info", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Renders_Shade()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters
|
||||
.Add(p => p.AlertStyle, AlertStyle.Primary)
|
||||
.Add(p => p.Shade, Shade.Lighter));
|
||||
|
||||
Assert.Contains("rz-shade-lighter", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters
|
||||
.Add(p => p.AlertStyle, AlertStyle.Primary)
|
||||
.Add(p => p.Shade, Shade.Darker));
|
||||
|
||||
Assert.Contains("rz-shade-darker", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Renders_Variant()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Variant, Variant.Outlined));
|
||||
Assert.Contains("rz-variant-outlined", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Variant, Variant.Flat));
|
||||
Assert.Contains("rz-variant-flat", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Renders_Title()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
var title = "Alert Title";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Title, title));
|
||||
|
||||
Assert.Contains(title, component.Markup);
|
||||
Assert.Contains("rz-alert-title", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Renders_Text()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
var text = "This is an alert message";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Text, text));
|
||||
|
||||
Assert.Contains(text, component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Renders_ChildContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>(parameters =>
|
||||
{
|
||||
parameters.AddChildContent("Custom alert content");
|
||||
});
|
||||
|
||||
Assert.Contains("Custom alert content", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_ShowIcon_DisplaysIcon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
// Default should show icon
|
||||
Assert.Contains("rz-alert-icon", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ShowIcon, false));
|
||||
Assert.DoesNotContain("rz-alert-icon", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_AllowClose_DisplaysCloseButton()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenAlert>();
|
||||
|
||||
// Default AllowClose is true - should contain a button with close icon
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AllowClose, true));
|
||||
Assert.Contains("close", component.Markup);
|
||||
Assert.Contains("rz-button", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AllowClose, false));
|
||||
// When AllowClose is false, should not have close button
|
||||
var buttonCount = System.Text.RegularExpressions.Regex.Matches(component.Markup, "rz-button").Count;
|
||||
Assert.Equal(0, buttonCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_CloseButton_RaisesCloseEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var closeRaised = false;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAlert>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowClose, true);
|
||||
parameters.Add(p => p.Close, () => closeRaised = true);
|
||||
});
|
||||
|
||||
var closeButton = component.Find("button.rz-button");
|
||||
closeButton.Click();
|
||||
|
||||
Assert.True(closeRaised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_Visible_ControlsDisplay()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAlert>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Visible, true);
|
||||
parameters.Add(p => p.Text, "Visible Alert");
|
||||
});
|
||||
|
||||
Assert.Contains("Visible Alert", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Visible, false));
|
||||
|
||||
// When not visible, component should not render
|
||||
Assert.DoesNotContain("Visible Alert", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alert_CloseButton_SetsVisibleToFalse()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var visibleValue = true;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAlert>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Visible, visibleValue);
|
||||
parameters.Add(p => p.AllowClose, true);
|
||||
parameters.Add(p => p.VisibleChanged, (bool value) => visibleValue = value);
|
||||
});
|
||||
|
||||
var closeButton = component.Find("button.rz-button");
|
||||
closeButton.Click();
|
||||
|
||||
Assert.False(visibleValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,77 @@
|
||||
using Xunit;
|
||||
using System.Collections;
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class AutoCompleteTests
|
||||
{
|
||||
[Fact]
|
||||
public void AutoComplete_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenAutoComplete>();
|
||||
|
||||
Assert.Contains(@"rz-autocomplete", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoComplete_Renders_InputElement()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenAutoComplete>();
|
||||
|
||||
Assert.Contains("type=\"text\"", component.Markup);
|
||||
Assert.Contains("rz-inputtext", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoComplete_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenAutoComplete>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoComplete_Renders_Placeholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenAutoComplete>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Placeholder, "Type to search...");
|
||||
});
|
||||
|
||||
Assert.Contains("placeholder=\"Type to search...\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoComplete_Renders_WithData()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Apple", "Banana", "Cherry" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenAutoComplete>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-autocomplete-panel", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoComplete_Enum_Converts_To_Attr_Value()
|
||||
{
|
||||
@@ -70,5 +138,30 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Equal("additional-name", AutoCompleteType.MiddleName.GetAutoCompleteValue());
|
||||
Assert.Equal("family-name", AutoCompleteType.LastName.GetAutoCompleteValue());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoComplete_Filters_StringList()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Apple", "Banana", "Cherry" };
|
||||
|
||||
var component = ctx.RenderComponent<AutoCompleteWithAccessibleView>(parameters =>
|
||||
{
|
||||
parameters
|
||||
.Add(p => p.Data, data)
|
||||
.Add(p => p.SearchText, "Ban")
|
||||
.Add(p => p.OpenOnFocus, true);
|
||||
});
|
||||
|
||||
Assert.Contains("Banana", component.Instance.CurrentView.OfType<string>());
|
||||
Assert.DoesNotContain("Apple", component.Instance.CurrentView.OfType<string>());
|
||||
Assert.DoesNotContain("Cherry", component.Instance.CurrentView.OfType<string>());
|
||||
}
|
||||
|
||||
private sealed class AutoCompleteWithAccessibleView : RadzenAutoComplete
|
||||
{
|
||||
public IEnumerable CurrentView => View;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
|
||||
|
||||
Assert.Contains(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -48,7 +48,7 @@ namespace Radzen.Blazor.Tests
|
||||
);
|
||||
|
||||
// does not render the actual icon when busy
|
||||
Assert.DoesNotContain(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.DoesNotContain(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
|
||||
// renders the icon with busy spin animation
|
||||
Assert.Contains(@"<i style=""animation: rotation", component.Markup);
|
||||
@@ -71,7 +71,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add(p => p.Icon, icon);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Image, image));
|
||||
|
||||
Assert.Contains(@$"<img class=""rz-button-icon-left rzi"" src=""{image}"" alt=""button"" />", component.Markup);
|
||||
Assert.Contains(@$"<img class=""notranslate rz-button-icon-left rzi"" src=""{image}"" alt=""button"" />", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -106,7 +106,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add(p => p.ImageAlternateText, text);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"<img class=""rz-button-icon-left rzi"" src=""{image}"" alt=""{text}"" />", component.Markup);
|
||||
Assert.Contains(@$"<img class=""notranslate rz-button-icon-left rzi"" src=""{image}"" alt=""{text}"" />", component.Markup);
|
||||
Assert.Contains(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
|
||||
}
|
||||
|
||||
|
||||
46
Radzen.Blazor.Tests/CardTests.cs
Normal file
46
Radzen.Blazor.Tests/CardTests.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class CardTests
|
||||
{
|
||||
[Fact]
|
||||
public void Card_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCard>();
|
||||
|
||||
Assert.Contains(@"rz-card", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Card_Renders_ChildContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCard>(parameters =>
|
||||
{
|
||||
parameters.AddChildContent("<div>Card Content</div>");
|
||||
});
|
||||
|
||||
Assert.Contains("Card Content", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Card_Renders_Variant()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCard>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Variant, Variant.Outlined));
|
||||
Assert.Contains("rz-variant-outlined", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Variant, Variant.Filled));
|
||||
Assert.Contains("rz-variant-filled", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Variant, Variant.Flat));
|
||||
Assert.Contains("rz-variant-flat", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
173
Radzen.Blazor.Tests/CarouselTests.cs
Normal file
173
Radzen.Blazor.Tests/CarouselTests.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class CarouselTests
|
||||
{
|
||||
[Fact]
|
||||
public void Carousel_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCarousel>();
|
||||
|
||||
Assert.Contains(@"rz-carousel", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_AllowPaging_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenCarouselItem>(0);
|
||||
builder.AddAttribute(1, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Slide 1")));
|
||||
builder.CloseComponent();
|
||||
|
||||
builder.OpenComponent<RadzenCarouselItem>(2);
|
||||
builder.AddAttribute(3, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Slide 2")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("rz-carousel-pager-button", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_AllowPaging_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, false);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenCarouselItem>(0);
|
||||
builder.AddAttribute(1, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Slide 1")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-carousel-pager-button", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_AllowNavigation_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowNavigation, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-carousel-prev", component.Markup);
|
||||
Assert.Contains("rz-carousel-next", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_AllowNavigation_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowNavigation, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-carousel-prev", component.Markup);
|
||||
Assert.DoesNotContain("rz-carousel-next", component.Markup);
|
||||
Assert.Contains("rz-carousel-no-navigation", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_PagerPosition_Top()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.PagerPosition, PagerPosition.Top);
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenCarouselItem>(0);
|
||||
builder.AddAttribute(1, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Slide")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("rz-carousel-pager-top", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_PagerPosition_Bottom()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.PagerPosition, PagerPosition.Bottom);
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenCarouselItem>(0);
|
||||
builder.AddAttribute(1, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Slide")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("rz-carousel-pager-bottom", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_PagerPosition_TopAndBottom()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.PagerPosition, PagerPosition.TopAndBottom);
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenCarouselItem>(0);
|
||||
builder.AddAttribute(1, "ChildContent", (RenderFragment)(b => b.AddContent(0, "Slide")));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("rz-carousel-pager-top", component.Markup);
|
||||
Assert.Contains("rz-carousel-pager-bottom", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_PagerOverlay_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.PagerOverlay, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-carousel-pager-overlay", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Carousel_Renders_PagerOverlay_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCarousel>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.PagerOverlay, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-carousel-pager-overlay", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bunit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Radzen.Blazor.Rendering;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
@@ -23,6 +24,9 @@ public class ChartTests
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.Setup<Rect>("Radzen.createChart", _ => true).SetResult(new Rect {Left = 0, Top = 0, Width = 200, Height = 200});
|
||||
ctx.Services.AddScoped<TooltipService>();
|
||||
ctx.JSInterop.SetupVoid("Radzen.openChartTooltip", _ => true);
|
||||
ctx.RenderComponent<RadzenChartTooltip>();
|
||||
|
||||
var seriesData = Enumerable.Range(0, 5000).Select(i => new Point { X = i, Y = i });
|
||||
var chart = ctx.RenderComponent<RadzenChart>(chartParameters =>
|
||||
@@ -42,12 +46,12 @@ public class ChartTests
|
||||
})));
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
foreach (var _ in Enumerable.Range(0, 10))
|
||||
foreach (var invocation in Enumerable.Range(0, 10))
|
||||
{
|
||||
await chart.InvokeAsync(() => chart.Instance.MouseMove(100, 80));
|
||||
Assert.Contains("<div class=\"rz-chart-tooltip", chart.Markup);
|
||||
Assert.Equal((invocation + 1) * 2, ctx.JSInterop.Invocations.Count(x => x.Identifier == "Radzen.openChartTooltip"));
|
||||
await chart.InvokeAsync(() => chart.Instance.MouseMove(0, 0));
|
||||
Assert.DoesNotContain("<div class=\"rz-chart-tooltip", chart.Markup);
|
||||
Assert.Equal(invocation + 1, ctx.JSInterop.Invocations.Count(x => x.Identifier == "Radzen.closeTooltip"));
|
||||
}
|
||||
output.WriteLine($"Time took: {stopwatch.Elapsed}");
|
||||
}
|
||||
|
||||
369
Radzen.Blazor.Tests/ChatTests.cs
Normal file
369
Radzen.Blazor.Tests/ChatTests.cs
Normal file
@@ -0,0 +1,369 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Radzen.Blazor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class ChatTests
|
||||
{
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldRenderWithTitle()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
);
|
||||
|
||||
Assert.Contains("Test Chat", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowEmptyMessageWhenNoMessages()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.EmptyMessage, "No messages yet!")
|
||||
);
|
||||
|
||||
Assert.Contains("No messages yet!", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldDisplayMessages()
|
||||
{
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new ChatMessage { Content = "Hello", UserId = "user1", Timestamp = DateTime.Now },
|
||||
new ChatMessage { Content = "Hi there!", UserId = "user2", Timestamp = DateTime.Now }
|
||||
};
|
||||
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" },
|
||||
new ChatUser { Id = "user2", Name = "Jane" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, messages)
|
||||
);
|
||||
|
||||
Assert.Contains("Hello", component.Markup);
|
||||
Assert.Contains("Hi there!", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowUsersInHeader()
|
||||
{
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" },
|
||||
new ChatUser { Id = "user2", Name = "Jane" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ShowUsers, true)
|
||||
);
|
||||
|
||||
Assert.Contains("John", component.Markup);
|
||||
Assert.Contains("Jane", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowUserNamesAboveMessages()
|
||||
{
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new ChatMessage { Content = "Hello", UserId = "user2", Timestamp = DateTime.Now }
|
||||
};
|
||||
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" },
|
||||
new ChatUser { Id = "user2", Name = "Jane" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, messages)
|
||||
.Add(p => p.ShowUserNames, true)
|
||||
);
|
||||
|
||||
Assert.Contains("Jane", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowClearButtonWhenEnabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ShowClearButton, true)
|
||||
);
|
||||
|
||||
var clearButton = component.Find(".rz-chat-header-clear");
|
||||
Assert.NotNull(clearButton);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldNotShowClearButtonWhenDisabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ShowClearButton, false)
|
||||
);
|
||||
|
||||
var clearButton = component.FindAll(".rz-chat-header-clear");
|
||||
Assert.Empty(clearButton);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldLimitVisibleUsers()
|
||||
{
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" },
|
||||
new ChatUser { Id = "user2", Name = "Jane" },
|
||||
new ChatUser { Id = "user3", Name = "Bob" },
|
||||
new ChatUser { Id = "user4", Name = "Alice" },
|
||||
new ChatUser { Id = "user5", Name = "Charlie" },
|
||||
new ChatUser { Id = "user6", Name = "David" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ShowUsers, true)
|
||||
.Add(p => p.MaxVisibleUsers, 3)
|
||||
);
|
||||
|
||||
// Should show "+3" for the remaining users
|
||||
Assert.Contains("+3", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowUserMessagesOnRight()
|
||||
{
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new ChatMessage { Content = "My message", UserId = "user1", Timestamp = DateTime.Now }
|
||||
};
|
||||
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, messages)
|
||||
);
|
||||
|
||||
var userMessage = component.Find(".rz-chat-message-user");
|
||||
Assert.NotNull(userMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowUserMessagesOnLeft()
|
||||
{
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new ChatMessage { Content = "Other message", UserId = "user2", Timestamp = DateTime.Now }
|
||||
};
|
||||
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" },
|
||||
new ChatUser { Id = "user2", Name = "Jane" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, messages)
|
||||
);
|
||||
|
||||
var participantMessage = component.Find(".rz-chat-message-participant");
|
||||
Assert.NotNull(participantMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowAvatarInitials()
|
||||
{
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John Doe" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ShowUsers, true)
|
||||
);
|
||||
|
||||
Assert.Contains("JD", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowAvatarImageWhenProvided()
|
||||
{
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John", AvatarUrl = "https://example.com/avatar.jpg" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ShowUsers, true)
|
||||
);
|
||||
|
||||
var avatarImage = component.Find(".rz-chat-participant-image");
|
||||
Assert.NotNull(avatarImage);
|
||||
Assert.Equal("https://example.com/avatar.jpg", avatarImage.GetAttribute("src"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldBeDisabledWhenDisabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.Disabled, true)
|
||||
);
|
||||
|
||||
var textarea = component.Find(".rz-chat-textarea");
|
||||
Assert.True(textarea.HasAttribute("disabled"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldBeReadOnlyWhenReadOnly()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.ReadOnly, true)
|
||||
);
|
||||
|
||||
var textarea = component.Find(".rz-chat-textarea");
|
||||
Assert.True(textarea.HasAttribute("readonly"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowCustomPlaceholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, new List<ChatUser>())
|
||||
.Add(p => p.Messages, new List<ChatMessage>())
|
||||
.Add(p => p.Placeholder, "Custom placeholder")
|
||||
);
|
||||
|
||||
var textarea = component.Find(".rz-chat-textarea");
|
||||
Assert.Equal("Custom placeholder", textarea.GetAttribute("placeholder"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowStreamingIndicator()
|
||||
{
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new ChatMessage { Content = "Streaming message", UserId = "user1", IsStreaming = true, Timestamp = DateTime.Now }
|
||||
};
|
||||
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, messages)
|
||||
);
|
||||
|
||||
var streamingIcon = component.Find(".rz-chat-message-streaming-icon");
|
||||
Assert.NotNull(streamingIcon);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenChat_ShouldShowMessageTimestamps()
|
||||
{
|
||||
var timestamp = DateTime.Now.AddHours(-1);
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new ChatMessage { Content = "Test message", UserId = "user1", Timestamp = timestamp }
|
||||
};
|
||||
|
||||
var users = new List<ChatUser>
|
||||
{
|
||||
new ChatUser { Id = "user1", Name = "John" }
|
||||
};
|
||||
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenChat>(parameters => parameters
|
||||
.Add(p => p.Title, "Test Chat")
|
||||
.Add(p => p.CurrentUserId, "user1")
|
||||
.Add(p => p.Users, users)
|
||||
.Add(p => p.Messages, messages)
|
||||
);
|
||||
|
||||
Assert.Contains(timestamp.ToString("HH:mm"), component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
168
Radzen.Blazor.Tests/CheckBoxListTests.cs
Normal file
168
Radzen.Blazor.Tests/CheckBoxListTests.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class CheckBoxListTests
|
||||
{
|
||||
class Item
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool Disabled { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<int>>();
|
||||
|
||||
Assert.Contains(@"rz-checkbox-list", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_WithData()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<string> { "Option 1", "Option 2", "Option 3" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("Option 1", component.Markup);
|
||||
Assert.Contains("Option 2", component.Markup);
|
||||
Assert.Contains("Option 3", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_WithCustomTextValueProperties()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "First" },
|
||||
new Item { Id = 2, Name = "Second" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
parameters.Add(p => p.ValueProperty, "Id");
|
||||
});
|
||||
|
||||
Assert.Contains("First", component.Markup);
|
||||
Assert.Contains("Second", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_Orientation_Horizontal()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Horizontal);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-flex-row", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_Orientation_Vertical()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Vertical);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-flex-column", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_AllowSelectAll()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<string> { "Option 1", "Option 2" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowSelectAll, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-multiselect-header", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_SelectAllText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<string> { "Option 1", "Option 2" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowSelectAll, true);
|
||||
parameters.Add(p => p.SelectAllText, "Select All Options");
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("Select All Options", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_CheckboxInputs()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<string> { "Option 1", "Option 2" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("type=\"checkbox\"", component.Markup);
|
||||
Assert.Contains("rz-chkbox", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckBoxList_Renders_DisabledItems()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "Enabled", Disabled = false },
|
||||
new Item { Id = 2, Name = "Disabled", Disabled = true }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenCheckBoxList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
parameters.Add(p => p.ValueProperty, "Id");
|
||||
parameters.Add(p => p.DisabledProperty, "Disabled");
|
||||
});
|
||||
|
||||
Assert.Contains("Enabled", component.Markup);
|
||||
Assert.Contains("Disabled", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
74
Radzen.Blazor.Tests/ColumnTests.cs
Normal file
74
Radzen.Blazor.Tests/ColumnTests.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class ColumnTests
|
||||
{
|
||||
[Fact]
|
||||
public void Column_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenColumn>();
|
||||
|
||||
Assert.Contains(@"rz-col", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_Renders_ChildContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenColumn>(parameters =>
|
||||
{
|
||||
parameters.AddChildContent("<div>Column Content</div>");
|
||||
});
|
||||
|
||||
Assert.Contains("Column Content", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_Renders_SizeParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenColumn>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, 6));
|
||||
|
||||
Assert.Contains("rz-col-6", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_Renders_SizeMD()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenColumn>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.SizeMD, 4));
|
||||
|
||||
Assert.Contains("rz-col-md-4", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_Renders_SizeSM()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenColumn>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.SizeSM, 12));
|
||||
|
||||
Assert.Contains("rz-col-sm-12", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_Renders_Offset()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenColumn>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Offset, 2));
|
||||
|
||||
Assert.Contains("rz-offset-2", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -155,7 +155,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<bool>(p => p.AllowClear, true);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"<i class=""rz-dropdown-clear-icon rzi rzi-times""", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rz-dropdown-clear-icon rzi rzi-times""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -174,6 +174,34 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Contains(@$"tabindex=""{value}""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Renders_EmptyCssClass_WhenValueIsEmpty()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Value, null));
|
||||
|
||||
Assert.Contains(@$"rz-state-empty", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_DoesNotRender_EmptyCssClass_WhenValueIsNotEmpty()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Value, DateTime.Now));
|
||||
|
||||
Assert.DoesNotContain(@$"rz-state-empty", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Renders_DisabledParameter()
|
||||
{
|
||||
@@ -704,5 +732,130 @@ namespace Radzen.Blazor.Tests
|
||||
var weekNumberHeader = component.Find(".rz-calendar-view th.rz-datepicker-week-number");
|
||||
Assert.Contains("Wk", weekNumberHeader.InnerHtml);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Multiple_Selects_IEnumerableDateTime()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
IEnumerable<DateTime> emitted = null;
|
||||
var initial = new DateTime(2024, 1, 1);
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<IEnumerable<DateTime>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.InitialViewDate, initial);
|
||||
parameters.Add(p => p.ValueChanged, args => { emitted = args; });
|
||||
});
|
||||
|
||||
component.InvokeAsync(() => component.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "10").ParentElement.Click());
|
||||
component.InvokeAsync(() => component.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "12").ParentElement.Click());
|
||||
|
||||
Assert.NotNull(emitted);
|
||||
var list = emitted.ToList();
|
||||
Assert.Equal(2, list.Count);
|
||||
Assert.Contains(new DateTime(2024, 1, 10), list.Select(d => d.Date));
|
||||
Assert.Contains(new DateTime(2024, 1, 12), list.Select(d => d.Date));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Multiple_Selects_IEnumerableNullableDateTime()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
IEnumerable<DateTime?> emitted = null;
|
||||
var initial = new DateTime(2024, 2, 1);
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<IEnumerable<DateTime?>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.InitialViewDate, initial);
|
||||
parameters.Add(p => p.ValueChanged, args => { emitted = args; });
|
||||
});
|
||||
|
||||
component.InvokeAsync(() => component.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "3").ParentElement.Click());
|
||||
component.InvokeAsync(() => component.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "14").ParentElement.Click());
|
||||
|
||||
Assert.NotNull(emitted);
|
||||
var list = emitted.ToList();
|
||||
Assert.Equal(2, list.Count);
|
||||
Assert.Contains(new DateTime(2024, 2, 3), list.Select(d => d.Value.Date));
|
||||
Assert.Contains(new DateTime(2024, 2, 14), list.Select(d => d.Value.Date));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Multiple_Emits_IEnumerableDateTimeOffsetNullable_WithUtcKind()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
IEnumerable<DateTimeOffset?> emitted = null;
|
||||
var initial = new DateTime(2024, 3, 1);
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<IEnumerable<DateTimeOffset?>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.InitialViewDate, initial);
|
||||
parameters.Add(p => p.Kind, DateTimeKind.Utc);
|
||||
parameters.Add(p => p.ValueChanged, args => { emitted = args; });
|
||||
});
|
||||
|
||||
component.InvokeAsync(() => component.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "5").ParentElement.Click());
|
||||
component.InvokeAsync(() => component.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "20").ParentElement.Click());
|
||||
|
||||
Assert.NotNull(emitted);
|
||||
var list = emitted.ToList();
|
||||
Assert.Equal(2, list.Count);
|
||||
Assert.All(list, dto => Assert.Equal(TimeSpan.Zero, dto.Value.Offset));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_Multiple_Emits_IEnumerableDateOnlyAndTimeOnlyNullable()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
IEnumerable<DateOnly?> emittedDates = null;
|
||||
IEnumerable<TimeOnly?> emittedTimes = null;
|
||||
var initial = new DateTime(2024, 4, 1);
|
||||
|
||||
var compDates = ctx.RenderComponent<RadzenDatePicker<IEnumerable<DateOnly?>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.InitialViewDate, initial);
|
||||
parameters.Add(p => p.ValueChanged, args => { emittedDates = args; });
|
||||
});
|
||||
|
||||
compDates.InvokeAsync(() => compDates.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "7").ParentElement.Click());
|
||||
compDates.InvokeAsync(() => compDates.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "9").ParentElement.Click());
|
||||
|
||||
Assert.NotNull(emittedDates);
|
||||
var dateList = emittedDates.ToList();
|
||||
Assert.Equal(2, dateList.Count);
|
||||
Assert.Contains(new DateOnly(2024, 4, 7), dateList.Select(d => d.Value));
|
||||
Assert.Contains(new DateOnly(2024, 4, 9), dateList.Select(d => d.Value));
|
||||
|
||||
// TimeOnly? emission should produce midnight times for selected dates
|
||||
var compTimes = ctx.RenderComponent<RadzenDatePicker<IEnumerable<TimeOnly?>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.InitialViewDate, initial);
|
||||
parameters.Add(p => p.ValueChanged, args => { emittedTimes = args; });
|
||||
});
|
||||
|
||||
compTimes.InvokeAsync(() => compTimes.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "1").ParentElement.Click());
|
||||
compTimes.InvokeAsync(() => compTimes.FindAll("td:not(.rz-calendar-other-month) span").First(e => e.TextContent == "2").ParentElement.Click());
|
||||
|
||||
Assert.NotNull(emittedTimes);
|
||||
var timeList = emittedTimes.ToList();
|
||||
Assert.Equal(2, timeList.Count);
|
||||
Assert.All(timeList, t => Assert.Equal(new TimeOnly(0, 0, 0), t.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
413
Radzen.Blazor.Tests/DialogServiceTests.cs
Normal file
413
Radzen.Blazor.Tests/DialogServiceTests.cs
Normal file
@@ -0,0 +1,413 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class DialogServiceTests : ComponentBase
|
||||
{
|
||||
public class OpenDialogTests
|
||||
{
|
||||
[Fact(DisplayName = "DialogOptions default values are set correctly")]
|
||||
public void DialogOptions_DefaultValues_AreSetCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var options = new DialogOptions();
|
||||
var dialogService = new DialogService(null, null);
|
||||
|
||||
// Act
|
||||
dialogService.OpenDialog<DialogServiceTests>("Test", [], options);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("600px", options.Width);
|
||||
Assert.Equal("", options.Left);
|
||||
Assert.Equal("", options.Top);
|
||||
Assert.Equal("", options.Bottom);
|
||||
Assert.Equal("", options.Height);
|
||||
Assert.Equal("", options.Style);
|
||||
Assert.Equal("", options.CssClass);
|
||||
Assert.Equal("", options.WrapperCssClass);
|
||||
Assert.Equal("", options.ContentCssClass);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "DialogOptions values are retained after OpenDialog call")]
|
||||
public void DialogOptions_Values_AreRetained_AfterOpenDialogCall()
|
||||
{
|
||||
// Arrange
|
||||
var options = new DialogOptions
|
||||
{
|
||||
Width = "800px",
|
||||
Left = "10px",
|
||||
Top = "20px",
|
||||
Bottom = "30px",
|
||||
Height = "400px",
|
||||
Style = "background-color: red;",
|
||||
CssClass = "custom-class",
|
||||
WrapperCssClass = "wrapper-class",
|
||||
ContentCssClass = "content-class"
|
||||
};
|
||||
var dialogService = new DialogService(null, null);
|
||||
|
||||
// Act
|
||||
dialogService.OpenDialog<DialogServiceTests>("Test", [], options);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("800px", options.Width);
|
||||
Assert.Equal("10px", options.Left);
|
||||
Assert.Equal("20px", options.Top);
|
||||
Assert.Equal("30px", options.Bottom);
|
||||
Assert.Equal("400px", options.Height);
|
||||
Assert.Equal("background-color: red;", options.Style);
|
||||
Assert.Equal("custom-class", options.CssClass);
|
||||
Assert.Equal("wrapper-class", options.WrapperCssClass);
|
||||
Assert.Equal("content-class", options.ContentCssClass);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "DialogOptions is null and default values are set correctly")]
|
||||
public void DialogOptions_IsNull_DefaultValues_AreSetCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
DialogOptions resultingOptions = null;
|
||||
var dialogService = new DialogService(null, null);
|
||||
dialogService.OnOpen += (title, type, parameters, options) => resultingOptions = options;
|
||||
|
||||
// Act
|
||||
dialogService.OpenDialog<DialogServiceTests>("Test", [], null);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(resultingOptions);
|
||||
Assert.Equal("600px", resultingOptions.Width);
|
||||
Assert.Equal("", resultingOptions.Left);
|
||||
Assert.Equal("", resultingOptions.Top);
|
||||
Assert.Equal("", resultingOptions.Bottom);
|
||||
Assert.Equal("", resultingOptions.Height);
|
||||
Assert.Equal("", resultingOptions.Style);
|
||||
Assert.Equal("", resultingOptions.CssClass);
|
||||
Assert.Equal("", resultingOptions.WrapperCssClass);
|
||||
Assert.Equal("", resultingOptions.ContentCssClass);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Open with dynamic component type reflective calls are resolved without exception")]
|
||||
public void Open_DynamicComponentType_Reflective_Calls_Resolve()
|
||||
{
|
||||
// Arrange
|
||||
string resultingTitle = null;
|
||||
Type resultingType = null;
|
||||
var dialogService = new DialogService(null, null);
|
||||
dialogService.OnOpen += (title, type, _, _) =>
|
||||
{
|
||||
resultingTitle = title;
|
||||
resultingType = type;
|
||||
};
|
||||
|
||||
dialogService.Open("Dynamic Open", typeof(RadzenButton), []);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Dynamic Open", resultingTitle);
|
||||
Assert.Equal(typeof(RadzenButton), resultingType);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "OpenAsync with dynamic component type reflective calls are resolved without exception")]
|
||||
public async Task OpenAsync_DynamicComponentType_Reflective_Calls_Resolve()
|
||||
{
|
||||
// Arrange
|
||||
string resultingTitle = null;
|
||||
Type resultingType = null;
|
||||
var dialogService = new DialogService(null, null);
|
||||
dialogService.OnOpen += (title, type, _, _) =>
|
||||
{
|
||||
resultingTitle = title;
|
||||
resultingType = type;
|
||||
};
|
||||
|
||||
var openTask = dialogService.OpenAsync("Dynamic Open", typeof(RadzenButton), []);
|
||||
dialogService.Close();
|
||||
await openTask;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Dynamic Open", resultingTitle);
|
||||
Assert.Equal(typeof(RadzenButton), resultingType);
|
||||
}
|
||||
}
|
||||
|
||||
public class OpenSideDialogTests
|
||||
{
|
||||
[Fact(DisplayName = "SideDialogOptions resizable option is retained after OpenSideDialog call")]
|
||||
public void SideDialogOptions_Resizable_AreRetained_AfterOpenSideDialogCall()
|
||||
{
|
||||
// Arrange
|
||||
var options = new SideDialogOptions { Resizable = true };
|
||||
SideDialogOptions resultingOptions = null;
|
||||
var dialogService = new DialogService(null, null);
|
||||
dialogService.OnSideOpen += (_, _, sideOptions) => resultingOptions = sideOptions;
|
||||
|
||||
// Act
|
||||
dialogService.OpenSide<DialogServiceTests>("Test", [], options);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(resultingOptions);
|
||||
Assert.Same(options, resultingOptions);
|
||||
Assert.True(resultingOptions.Resizable);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Side dialog shows resize bar when Resizable is true")]
|
||||
public void SideDialog_Resizable_ShowsResizeBar()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
|
||||
// Render the dialog host
|
||||
var cut = ctx.RenderComponent<RadzenDialog>();
|
||||
|
||||
// Open a side dialog with Resizable=true
|
||||
var dialogService = ctx.Services.GetRequiredService<DialogService>();
|
||||
cut.InvokeAsync(() => dialogService.OpenSide("Test", typeof(RadzenButton),
|
||||
new Dictionary<string, object>(), new SideDialogOptions { Resizable = true }));
|
||||
|
||||
// Assert: the resize bar element is present
|
||||
cut.WaitForAssertion(() =>
|
||||
{
|
||||
var markup = cut.Markup;
|
||||
Assert.Contains("rz-dialog-resize-bar", markup);
|
||||
// Optionally ensure the inner handle exists too
|
||||
Assert.Contains("rz-resize", markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Side dialog hides resize bar when Resizable is false")]
|
||||
public void SideDialog_NonResizable_HidesResizeBar()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
|
||||
// Render the dialog host
|
||||
var cut = ctx.RenderComponent<RadzenDialog>();
|
||||
|
||||
// Open a side dialog with Resizable=false
|
||||
var dialogService = ctx.Services.GetRequiredService<DialogService>();
|
||||
cut.InvokeAsync(() => dialogService.OpenSide("Test", typeof(RadzenButton),
|
||||
new Dictionary<string, object>(), new SideDialogOptions()));
|
||||
|
||||
// Assert: the resize bar element is not present
|
||||
cut.WaitForAssertion(() =>
|
||||
{
|
||||
var markup = cut.Markup;
|
||||
Assert.DoesNotContain("rz-dialog-resize-bar", markup);
|
||||
});
|
||||
}
|
||||
}
|
||||
public class ConfirmTests
|
||||
{
|
||||
[Fact(DisplayName = "ConfirmOptions is null and default values are set correctly")]
|
||||
public async Task ConfirmOptions_IsNull_AreSetCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var dialogService = new DialogService(null, null);
|
||||
ConfirmOptions resultingOptions = null;
|
||||
dialogService.OnOpen += (title, type, parameters, options) => resultingOptions = options as ConfirmOptions;
|
||||
|
||||
using var cancellationTokenSource = new CancellationTokenSource();
|
||||
cancellationTokenSource.Cancel();
|
||||
|
||||
// Act
|
||||
try
|
||||
{
|
||||
await dialogService.Confirm(cancellationToken: cancellationTokenSource.Token);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// this is expected
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(resultingOptions);
|
||||
Assert.Equal("Ok", resultingOptions.OkButtonText);
|
||||
Assert.Equal("Cancel", resultingOptions.CancelButtonText);
|
||||
Assert.Equal("600px", resultingOptions.Width);
|
||||
Assert.Equal("", resultingOptions.Style);
|
||||
Assert.Equal("rz-dialog-confirm", resultingOptions.CssClass);
|
||||
Assert.Equal("rz-dialog-wrapper", resultingOptions.WrapperCssClass);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "ConfirmOptions default values are set correctly")]
|
||||
public async Task ConfirmOptions_DefaultValues_AreSetCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var dialogService = new DialogService(null, null);
|
||||
ConfirmOptions resultingOptions = null;
|
||||
dialogService.OnOpen += (title, type, parameters, options) => resultingOptions = options as ConfirmOptions;
|
||||
|
||||
using var cancellationTokenSource = new CancellationTokenSource();
|
||||
cancellationTokenSource.Cancel();
|
||||
|
||||
// Act
|
||||
try
|
||||
{
|
||||
await dialogService.Confirm(options: new(), cancellationToken: cancellationTokenSource.Token);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// this is expected
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(resultingOptions);
|
||||
Assert.Equal("Ok", resultingOptions.OkButtonText);
|
||||
Assert.Equal("Cancel", resultingOptions.CancelButtonText);
|
||||
Assert.Equal("600px", resultingOptions.Width);
|
||||
Assert.Equal("", resultingOptions.Style);
|
||||
Assert.Equal("rz-dialog-confirm", resultingOptions.CssClass);
|
||||
Assert.Equal("rz-dialog-wrapper", resultingOptions.WrapperCssClass);
|
||||
}
|
||||
[Fact(DisplayName = "ConfirmOptions values are retained after Confirm call")]
|
||||
public async Task Confirm_ProvidedValues_AreRetained()
|
||||
{
|
||||
// Arrange
|
||||
var dialogService = new DialogService(null, null);
|
||||
var options = new ConfirmOptions
|
||||
{
|
||||
OkButtonText = "XXX",
|
||||
CancelButtonText = "YYY",
|
||||
Width = "800px",
|
||||
Style = "background-color: red;",
|
||||
CssClass = "custom-class",
|
||||
WrapperCssClass = "wrapper-class"
|
||||
};
|
||||
ConfirmOptions resultingOptions = null;
|
||||
dialogService.OnOpen += (title, type, parameters, options) => resultingOptions = options as ConfirmOptions;
|
||||
|
||||
// We break out of the dialog immediately, but the options should still be set
|
||||
using var cancellationTokenSource = new CancellationTokenSource();
|
||||
cancellationTokenSource.Cancel();
|
||||
|
||||
// Act
|
||||
try
|
||||
{
|
||||
await dialogService.Confirm("Confirm?", "Confirm", options, cancellationToken: cancellationTokenSource.Token);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// this is expected
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(resultingOptions);
|
||||
Assert.Equal("XXX", resultingOptions.OkButtonText);
|
||||
Assert.Equal("YYY", resultingOptions.CancelButtonText);
|
||||
Assert.Equal("800px", resultingOptions.Width);
|
||||
Assert.Equal("background-color: red;", resultingOptions.Style);
|
||||
Assert.Equal("rz-dialog-confirm custom-class", resultingOptions.CssClass);
|
||||
Assert.Equal("rz-dialog-wrapper wrapper-class", resultingOptions.WrapperCssClass);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class AlertTests
|
||||
{
|
||||
[Fact(DisplayName = "AlertOptions is null and default values are set correctly")]
|
||||
public async Task AlertOptions_IsNull_AreSetCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var dialogService = new DialogService(null, null);
|
||||
AlertOptions resultingOptions = null;
|
||||
dialogService.OnOpen += (title, type, parameters, options) => resultingOptions = options as AlertOptions;
|
||||
|
||||
using var cancellationTokenSource = new CancellationTokenSource();
|
||||
cancellationTokenSource.Cancel();
|
||||
|
||||
// Act
|
||||
try
|
||||
{
|
||||
await dialogService.Alert(cancellationToken: cancellationTokenSource.Token);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// this is expected
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(resultingOptions);
|
||||
Assert.Equal("Ok", resultingOptions.OkButtonText);
|
||||
Assert.Equal("600px", resultingOptions.Width);
|
||||
Assert.Equal("", resultingOptions.Style);
|
||||
Assert.Equal("rz-dialog-alert", resultingOptions.CssClass);
|
||||
Assert.Equal("rz-dialog-wrapper", resultingOptions.WrapperCssClass);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "AlertOptions default values are set correctly")]
|
||||
public async Task AlertOptions_DefaultValues_AreSetCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var dialogService = new DialogService(null, null);
|
||||
AlertOptions resultingOptions = null;
|
||||
dialogService.OnOpen += (title, type, parameters, options) => resultingOptions = options as AlertOptions;
|
||||
|
||||
using var cancellationTokenSource = new CancellationTokenSource();
|
||||
cancellationTokenSource.Cancel();
|
||||
|
||||
// Act
|
||||
try
|
||||
{
|
||||
await dialogService.Alert(options: new(), cancellationToken: cancellationTokenSource.Token);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// this is expected
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(resultingOptions);
|
||||
Assert.Equal("Ok", resultingOptions.OkButtonText);
|
||||
Assert.Equal("600px", resultingOptions.Width);
|
||||
Assert.Equal("", resultingOptions.Style);
|
||||
Assert.Equal("rz-dialog-alert", resultingOptions.CssClass);
|
||||
Assert.Equal("rz-dialog-wrapper", resultingOptions.WrapperCssClass);
|
||||
}
|
||||
[Fact(DisplayName = "AlertOptions values are retained after Alert call")]
|
||||
public async Task Alert_ProvidedValues_AreRetained()
|
||||
{
|
||||
// Arrange
|
||||
var dialogService = new DialogService(null, null);
|
||||
var options = new AlertOptions
|
||||
{
|
||||
OkButtonText = "XXX",
|
||||
Width = "800px",
|
||||
Style = "background-color: red;",
|
||||
CssClass = "custom-class",
|
||||
WrapperCssClass = "wrapper-class"
|
||||
};
|
||||
AlertOptions resultingOptions = null;
|
||||
dialogService.OnOpen += (title, type, parameters, options) => resultingOptions = options as AlertOptions;
|
||||
|
||||
// We break out of the dialog immediately, but the options should still be set
|
||||
using var cancellationTokenSource = new CancellationTokenSource();
|
||||
cancellationTokenSource.Cancel();
|
||||
|
||||
// Act
|
||||
try
|
||||
{
|
||||
await dialogService.Alert("Alert?", "Alert", options, cancellationToken: cancellationTokenSource.Token);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// this is expected
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(resultingOptions);
|
||||
Assert.Equal("XXX", resultingOptions.OkButtonText);
|
||||
Assert.Equal("800px", resultingOptions.Width);
|
||||
Assert.Equal("background-color: red;", resultingOptions.Style);
|
||||
Assert.Equal("rz-dialog-alert custom-class", resultingOptions.CssClass);
|
||||
Assert.Equal("rz-dialog-wrapper wrapper-class", resultingOptions.WrapperCssClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ public class DollarsTypeConverter : TypeConverter
|
||||
return new Dollars(d);
|
||||
|
||||
if (value is string s)
|
||||
return decimal.TryParse(s, out var val) ? new Dollars(val) : null;
|
||||
return decimal.TryParse(s, culture, out var val) ? new Dollars(val) : null;
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
289
Radzen.Blazor.Tests/DropDownDataGridTests.cs
Normal file
289
Radzen.Blazor.Tests/DropDownDataGridTests.cs
Normal file
@@ -0,0 +1,289 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class DropDownDataGridTests
|
||||
{
|
||||
class Customer
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string CompanyName { get; set; }
|
||||
public string ContactName { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<int>>();
|
||||
|
||||
Assert.Contains(@"rz-dropdown", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_DropdownTrigger()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<int>>();
|
||||
|
||||
Assert.Contains("rz-dropdown-trigger", component.Markup);
|
||||
Assert.Contains("rzi-chevron-down", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_WithData()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1", "Item2", "Item3" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-lookup-panel", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_WithCustomTextValueProperties()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<Customer>
|
||||
{
|
||||
new Customer { Id = 1, CompanyName = "Acme Corp", ContactName = "John Doe" },
|
||||
new Customer { Id = 2, CompanyName = "Tech Inc", ContactName = "Jane Smith" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, "CompanyName");
|
||||
parameters.Add(p => p.ValueProperty, "Id");
|
||||
});
|
||||
|
||||
Assert.Contains("rz-lookup-panel", component.Markup);
|
||||
Assert.Contains("rz-data-grid", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_DataGrid()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
// DropDownDataGrid embeds a DataGrid
|
||||
Assert.Contains("rz-data-grid", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_AllowFiltering()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1", "Item2" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-lookup-search", component.Markup);
|
||||
Assert.Contains("rz-lookup-search-input", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_Placeholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Placeholder, "Select an item");
|
||||
});
|
||||
|
||||
Assert.Contains("Select an item", component.Markup);
|
||||
Assert.Contains("rz-placeholder", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_AllowClear_WithValue()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowClear, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.Value, "Item1");
|
||||
});
|
||||
|
||||
Assert.Contains("rz-dropdown-clear-icon", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_DoesNotRender_AllowClear_WhenNotAllowed()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowClear, false);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.Value, "Item1");
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-dropdown-clear-icon", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_Multiple_Panel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<IEnumerable<int>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-multiselect-panel", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_Multiple_WithChips()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1", "Item2" };
|
||||
var selectedItems = new List<string> { "Item1" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<IEnumerable<string>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Chips, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.Value, selectedItems);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-dropdown-chips-wrapper", component.Markup);
|
||||
Assert.Contains("rz-chip", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_AllowSorting()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<Customer>
|
||||
{
|
||||
new Customer { Id = 1, CompanyName = "Acme" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowSorting, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, "CompanyName");
|
||||
});
|
||||
|
||||
Assert.Contains("rz-data-grid", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_SearchTextPlaceholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
parameters.Add(p => p.SearchTextPlaceholder, "Type to filter...");
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("placeholder=\"Type to filter...\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_EmptyText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, new List<string>());
|
||||
parameters.Add(p => p.EmptyText, "No items found");
|
||||
});
|
||||
|
||||
Assert.Contains("No items found", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_PageSize()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = Enumerable.Range(1, 20).Select(i => $"Item {i}").ToList();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.PageSize, 10);
|
||||
});
|
||||
|
||||
// DataGrid with paging should be present
|
||||
Assert.Contains("rz-data-grid", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDownDataGrid_Renders_AllowRowSelectOnRowClick()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1", "Item2" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDownDataGrid<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowRowSelectOnRowClick, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-data-grid", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Dom;
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
@@ -13,18 +16,20 @@ namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public int Id { get; set; }
|
||||
public bool Disabled { get; set; }
|
||||
}
|
||||
|
||||
private static IRenderedComponent<RadzenDropDown<T>> DropDown<T>(TestContext ctx, Action<ComponentParameterCollectionBuilder<RadzenDropDown<T>>> configure = null)
|
||||
{
|
||||
var data = new [] {
|
||||
var data = new[] {
|
||||
new DataItem { Text = "Item 1", Id = 1 },
|
||||
new DataItem { Text = "Item 2", Id = 2 },
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDown<T>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => {
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, nameof(DataItem.Text));
|
||||
|
||||
@@ -41,6 +46,7 @@ namespace Radzen.Blazor.Tests
|
||||
return component;
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task Dropdown_SelectItem_Method_Should_Not_Throw()
|
||||
{
|
||||
@@ -99,7 +105,8 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = DropDown<string>(ctx, parameters => {
|
||||
var component = DropDown<string>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Text));
|
||||
});
|
||||
|
||||
@@ -114,6 +121,35 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Contains("rz-state-highlight", items[0].ClassList);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_Respects_ItemEqualityComparer()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
List<DataItem> boundCollection = [new() { Text = "Item 2" }];
|
||||
|
||||
var component = DropDown<List<DataItem>>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ItemComparer, new DataItemComparer());
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Value, boundCollection);
|
||||
});
|
||||
|
||||
var selectedItems = component.FindAll(".rz-state-highlight");
|
||||
Assert.Equal(1, selectedItems.Count);
|
||||
Assert.Equal("Item 2", selectedItems[0].TextContent.Trim());
|
||||
|
||||
// select Item 1 in list
|
||||
var items = component.FindAll(".rz-multiselect-item");
|
||||
items[0].Click();
|
||||
component.Render();
|
||||
|
||||
selectedItems = component.FindAll(".rz-state-highlight");
|
||||
Assert.Equal(2, selectedItems.Count);
|
||||
Assert.Equal("Item 1", selectedItems[0].TextContent.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_AppliesSelectionStyleWhenMultipleSelectionIsEnabled()
|
||||
{
|
||||
@@ -121,7 +157,8 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = DropDown<string>(ctx, parameters => {
|
||||
var component = DropDown<string>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Text));
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
});
|
||||
@@ -245,5 +282,462 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.Collection(selectedItems, item => Assert.Contains("value: Item 1", item.Text()), item => Assert.Contains("value: Item 2", item.Text()));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, true, false, true, "false")]
|
||||
[InlineData(true, false, true, false, "true")]
|
||||
[InlineData(true, false, false, false, "false")]
|
||||
[InlineData(true, false, false, true, "true")]
|
||||
[InlineData(false, false, false, true, "false")]
|
||||
public void DropDown_AllSelectedFalseIfListIsAllDisabled(bool item1Selected, bool item1Disabled, bool item2Selected, bool item2Disabled, string expectedAriaCheckedValue)
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var data = new[] {
|
||||
new DataItem { Text = "Item 1", Id = 1, Disabled = item1Disabled },
|
||||
new DataItem { Text = "Item 2", Id = 2, Disabled = item2Disabled },
|
||||
};
|
||||
|
||||
List<int> selectedValues = [];
|
||||
if (item1Selected)
|
||||
{
|
||||
selectedValues.Add(data[0].Id);
|
||||
}
|
||||
if (item2Selected)
|
||||
{
|
||||
selectedValues.Add(data[1].Id);
|
||||
}
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDown<List<int>>>(parameters => parameters
|
||||
.Add(p => p.Data, data)
|
||||
.Add(p => p.Value, selectedValues)
|
||||
.Add(p => p.Multiple, true)
|
||||
.Add(p => p.AllowSelectAll, true)
|
||||
.Add(p => p.TextProperty, nameof(DataItem.Text))
|
||||
.Add(p => p.DisabledProperty, nameof(DataItem.Disabled))
|
||||
.Add(p => p.ValueProperty, nameof(DataItem.Id)));
|
||||
|
||||
Assert.NotNull(component);
|
||||
var highlightedItems = component.FindAll(".rz-state-highlight");
|
||||
Assert.Equal(selectedValues.Count, highlightedItems.Count);
|
||||
|
||||
|
||||
var selectAllCheckBox = component.Find(".rz-multiselect-header input[type='checkbox']");
|
||||
|
||||
Assert.Equal(expectedAriaCheckedValue, selectAllCheckBox.GetAttribute("aria-checked"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_ReferenceGenericCollectionAssignment_HashSet_ReferencesInstance()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var originalHashSet = new HashSet<int>();
|
||||
var capturedValue = (HashSet<int>)null;
|
||||
|
||||
var component = DropDownWithReferenceCollection<HashSet<int>>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Value, originalHashSet);
|
||||
parameters.Add(p => p.ValueChanged, EventCallback.Factory.Create<HashSet<int>>(this, value => capturedValue = value));
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
});
|
||||
|
||||
var items = component.FindAll(".rz-multiselect-item");
|
||||
|
||||
// Select first item
|
||||
items[0].Click();
|
||||
component.Render();
|
||||
|
||||
// Verify the same HashSet instance is Referenced
|
||||
Assert.Same(originalHashSet, capturedValue);
|
||||
|
||||
// Verify the item was added correctly
|
||||
Assert.Single(originalHashSet);
|
||||
Assert.Contains(1, originalHashSet);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_ReferenceGenericCollectionAssignment_HashSet_MultipleSelections()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var originalHashSet = new HashSet<int> { 2 }; // Pre-populate with Item 2
|
||||
var capturedValues = new List<HashSet<int>>();
|
||||
|
||||
var component = DropDownWithReferenceCollection<HashSet<int>>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Value, originalHashSet);
|
||||
parameters.Add(p => p.ValueChanged, EventCallback.Factory.Create<HashSet<int>>(this, value => capturedValues.Add(value)));
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
});
|
||||
|
||||
var items = component.FindAll(".rz-multiselect-item");
|
||||
|
||||
// Select first item (should add to existing collection)
|
||||
items[0].Click();
|
||||
component.Render();
|
||||
|
||||
// Verify the same HashSet instance is Referenced
|
||||
Assert.Single(capturedValues);
|
||||
Assert.Same(originalHashSet, capturedValues[0]);
|
||||
|
||||
// Verify both items are now in the collection
|
||||
Assert.Equal(2, originalHashSet.Count);
|
||||
Assert.Contains(1, originalHashSet);
|
||||
Assert.Contains(2, originalHashSet);
|
||||
|
||||
// Deselect second item (should remove from collection)
|
||||
items = component.FindAll(".rz-multiselect-item"); // Re-find items after render
|
||||
items[1].Click();
|
||||
component.Render();
|
||||
|
||||
// Verify the same HashSet instance is still Referenced
|
||||
Assert.Equal(2, capturedValues.Count);
|
||||
Assert.Same(originalHashSet, capturedValues[1]);
|
||||
|
||||
// Verify only first item remains
|
||||
Assert.Single(originalHashSet);
|
||||
Assert.Contains(1, originalHashSet);
|
||||
Assert.DoesNotContain(2, originalHashSet);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_ReferenceGenericCollectionAssignment_SortedSet_ReferencesInstance()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var originalSortedSet = new SortedSet<int>();
|
||||
var capturedValue = (SortedSet<int>)null;
|
||||
|
||||
var component = DropDownWithReferenceCollection<SortedSet<int>>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Value, originalSortedSet);
|
||||
parameters.Add(p => p.ValueChanged, EventCallback.Factory.Create<SortedSet<int>>(this, value => capturedValue = value));
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
});
|
||||
|
||||
var items = component.FindAll(".rz-multiselect-item");
|
||||
|
||||
// Select both items
|
||||
items[0].Click();
|
||||
component.Render();
|
||||
items = component.FindAll(".rz-multiselect-item"); // Re-find items after first click
|
||||
items[1].Click();
|
||||
component.Render();
|
||||
|
||||
// Verify the same SortedSet instance is Referenced
|
||||
Assert.Same(originalSortedSet, capturedValue);
|
||||
|
||||
// Verify items are sorted correctly
|
||||
Assert.Equal(2, originalSortedSet.Count);
|
||||
var sortedItems = originalSortedSet.ToList();
|
||||
Assert.Equal(1, sortedItems[0]);
|
||||
Assert.Equal(2, sortedItems[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_ReferenceGenericCollectionAssignment_CustomCollection_ReferencesInstance()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var originalCollection = new CustomCollection<int>();
|
||||
var capturedValue = (CustomCollection<int>)null;
|
||||
|
||||
var component = DropDownWithReferenceCollection<CustomCollection<int>>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Value, originalCollection);
|
||||
parameters.Add(p => p.ValueChanged, EventCallback.Factory.Create<CustomCollection<int>>(this, value => capturedValue = value));
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
});
|
||||
|
||||
var items = component.FindAll(".rz-multiselect-item");
|
||||
|
||||
// Select first item
|
||||
items[0].Click();
|
||||
component.Render();
|
||||
|
||||
// Verify the same custom collection instance is Referenced
|
||||
Assert.Same(originalCollection, capturedValue);
|
||||
|
||||
// Verify the item was added correctly
|
||||
Assert.Single(originalCollection);
|
||||
Assert.Contains(1, originalCollection);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void DropDown_ReferenceGenericCollectionAssignment_List_ReferencesInstance()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var originalList = new List<int>();
|
||||
var capturedValue = (List<int>)null;
|
||||
|
||||
var component = DropDownWithReferenceCollection<List<int>>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Value, originalList);
|
||||
parameters.Add(p => p.ValueChanged, EventCallback.Factory.Create<List<int>>(this, value => capturedValue = value));
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
});
|
||||
|
||||
var items = component.FindAll(".rz-multiselect-item");
|
||||
|
||||
// Select first item
|
||||
items[0].Click();
|
||||
component.Render();
|
||||
|
||||
// For List<T>, it should now Reference the instance since we removed the IList exclusion
|
||||
// Arrays are now excluded instead
|
||||
Assert.Same(originalList, capturedValue);
|
||||
|
||||
// And the content should be correct
|
||||
Assert.Single(capturedValue);
|
||||
Assert.Contains(1, capturedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_ReferenceGenericCollectionAssignment_DisabledByDefault()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var originalList = new List<int>();
|
||||
var capturedValue = (List<int>)null;
|
||||
|
||||
var component = DropDown<List<int>>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Value, originalList);
|
||||
parameters.Add(p => p.ValueChanged, EventCallback.Factory.Create<List<int>>(this, value => capturedValue = value));
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
});
|
||||
|
||||
var items = component.FindAll(".rz-multiselect-item");
|
||||
|
||||
// Select first item
|
||||
items[0].Click();
|
||||
component.Render();
|
||||
|
||||
// When ReferenceCollectionOnSelection is false (default), a new instance should be created
|
||||
Assert.NotSame(originalList, capturedValue);
|
||||
|
||||
// But the content should still be correct
|
||||
Assert.Single(capturedValue);
|
||||
Assert.Contains(1, capturedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_Reset_PreservesCollectionInstanceButClears()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var originalHashSet = new HashSet<int> { 1, 2 }; // Pre-populate
|
||||
var capturedValues = new List<HashSet<int>>();
|
||||
|
||||
var component = DropDownWithReferenceCollection<HashSet<int>>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Value, originalHashSet);
|
||||
parameters.Add(p => p.ValueChanged, EventCallback.Factory.Create<HashSet<int>>(this, value => capturedValues.Add(value)));
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
});
|
||||
|
||||
// Verify initial state - collection should have 2 items
|
||||
Assert.Equal(2, originalHashSet.Count);
|
||||
Assert.Contains(1, originalHashSet);
|
||||
Assert.Contains(2, originalHashSet);
|
||||
|
||||
// Call Reset (public method that calls ClearAll internally)
|
||||
component.InvokeAsync(() => component.Instance.Reset());
|
||||
component.Render();
|
||||
|
||||
// Verify the same HashSet instance is preserved
|
||||
Assert.Single(capturedValues);
|
||||
Assert.Same(originalHashSet, capturedValues[0]);
|
||||
|
||||
// Verify the collection is now cleared
|
||||
Assert.Empty(originalHashSet);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_SelectAll_PreservesCollectionInstanceAndPopulates()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var originalHashSet = new HashSet<int>(); // Start empty
|
||||
var capturedValues = new List<HashSet<int>>();
|
||||
|
||||
var component = DropDownWithReferenceCollection<HashSet<int>>(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.AllowSelectAll, true);
|
||||
parameters.Add(p => p.Value, originalHashSet);
|
||||
parameters.Add(p => p.ValueChanged, EventCallback.Factory.Create<HashSet<int>>(this, value => capturedValues.Add(value)));
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
});
|
||||
|
||||
// Verify initial state - collection should be empty
|
||||
Assert.Empty(originalHashSet);
|
||||
|
||||
// Find and click the "Select All" checkbox
|
||||
var selectAllCheckBox = component.Find(".rz-multiselect-header input[type='checkbox']");
|
||||
selectAllCheckBox.Click();
|
||||
component.Render();
|
||||
|
||||
// Verify the same HashSet instance is preserved
|
||||
Assert.Single(capturedValues);
|
||||
Assert.Same(originalHashSet, capturedValues[0]);
|
||||
|
||||
// Verify the collection now contains both items
|
||||
Assert.Equal(2, originalHashSet.Count);
|
||||
Assert.Contains(1, originalHashSet);
|
||||
Assert.Contains(2, originalHashSet);
|
||||
}
|
||||
|
||||
class ReferenceCollectionDropDown<T> : Radzen.Blazor.RadzenDropDown<T>
|
||||
{
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
PreserveCollectionOnSelection = true;
|
||||
base.OnInitialized();
|
||||
}
|
||||
}
|
||||
|
||||
private static IRenderedComponent<ReferenceCollectionDropDown<T>> DropDownWithReferenceCollection<T>(TestContext ctx, Action<ComponentParameterCollectionBuilder<ReferenceCollectionDropDown<T>>> configure = null)
|
||||
{
|
||||
var data = new[] {
|
||||
new DataItem { Text = "Item 1", Id = 1 },
|
||||
new DataItem { Text = "Item 2", Id = 2 },
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<ReferenceCollectionDropDown<T>>();
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, nameof(DataItem.Text));
|
||||
|
||||
if (configure != null)
|
||||
{
|
||||
configure.Invoke(parameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
}
|
||||
});
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
class DataItemComparer : IEqualityComparer<DataItem>, IEqualityComparer<object>
|
||||
{
|
||||
public bool Equals(DataItem x, DataItem y)
|
||||
{
|
||||
if (ReferenceEquals(x, y)) return true;
|
||||
if (x is null) return false;
|
||||
if (y is null) return false;
|
||||
if (x.GetType() != y.GetType()) return false;
|
||||
return x.Text == y.Text;
|
||||
}
|
||||
|
||||
public int GetHashCode(DataItem obj)
|
||||
{
|
||||
return obj.Text.GetHashCode();
|
||||
}
|
||||
|
||||
public new bool Equals(object x, object y)
|
||||
{
|
||||
return Equals((DataItem)x, (DataItem)y);
|
||||
}
|
||||
|
||||
public int GetHashCode(object obj)
|
||||
{
|
||||
return GetHashCode((DataItem)obj);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class CustomCollection<T> : ICollection<T>
|
||||
{
|
||||
private readonly List<T> _items = new();
|
||||
|
||||
public int Count => _items.Count;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public void Add(T item) => _items.Add(item);
|
||||
public void Clear() => _items.Clear();
|
||||
public bool Contains(T item) => _items.Contains(item);
|
||||
public void CopyTo(T[] array, int arrayIndex) => _items.CopyTo(array, arrayIndex);
|
||||
public bool Remove(T item) => _items.Remove(item);
|
||||
public IEnumerator<T> GetEnumerator() => _items.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_Renders_Placeholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDown<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Placeholder, "Select an option");
|
||||
});
|
||||
|
||||
Assert.Contains("Select an option", component.Markup);
|
||||
Assert.Contains("rz-placeholder", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_Renders_AllowClear_WithValue()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new[] { new DataItem { Text = "Item 1", Id = 1 } };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDown<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowClear, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, nameof(DataItem.Text));
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
parameters.Add(p => p.Value, 1);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-dropdown-clear-icon", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropDown_DoesNotRender_AllowClear_WhenNotAllowed()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new[] { new DataItem { Text = "Item 1", Id = 1 } };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDropDown<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowClear, false);
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, nameof(DataItem.Text));
|
||||
parameters.Add(p => p.ValueProperty, nameof(DataItem.Id));
|
||||
parameters.Add(p => p.Value, 1);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-dropdown-clear-icon", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1557
Radzen.Blazor.Tests/ExpressionParserTests.cs
Normal file
1557
Radzen.Blazor.Tests/ExpressionParserTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
323
Radzen.Blazor.Tests/ExpressionSerializerTests.cs
Normal file
323
Radzen.Blazor.Tests/ExpressionSerializerTests.cs
Normal file
@@ -0,0 +1,323 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
class TestEntity
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Age { get; set; }
|
||||
public double Salary { get; set; }
|
||||
public float Score { get; set; }
|
||||
public decimal Balance { get; set; }
|
||||
public short Level { get; set; }
|
||||
public long Population { get; set; }
|
||||
public Status AccountStatus { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTimeOffset LastUpdated { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
public TimeOnly StartTime { get; set; }
|
||||
public DateOnly BirthDate { get; set; }
|
||||
public IEnumerable<int> Scores { get; set; }
|
||||
public List<string> Tags { get; set; }
|
||||
public List<TestEntity> Children { get; set; }
|
||||
public Address Address { get; set; }
|
||||
public double[] Salaries { get; set; }
|
||||
public float[] Heights { get; set; }
|
||||
public decimal[] Balances { get; set; }
|
||||
public short[] Levels { get; set; }
|
||||
public long[] Populations { get; set; }
|
||||
public string[] Names { get; set; }
|
||||
public Guid[] Ids { get; set; }
|
||||
public DateTime[] CreatedDates { get; set; }
|
||||
public DateTimeOffset[] UpdatedDates { get; set; }
|
||||
public TimeOnly[] StartTimes { get; set; }
|
||||
public DateOnly[] BirthDates { get; set; }
|
||||
public Status[] Statuses { get; set; }
|
||||
}
|
||||
|
||||
enum Status
|
||||
{
|
||||
Active,
|
||||
Inactive,
|
||||
Suspended
|
||||
}
|
||||
|
||||
class Address
|
||||
{
|
||||
public string City { get; set; }
|
||||
public string Country { get; set; }
|
||||
}
|
||||
|
||||
public class ExpressionSerializerTests
|
||||
{
|
||||
private readonly ExpressionSerializer _serializer = new ExpressionSerializer();
|
||||
|
||||
[Fact]
|
||||
public void Serializes_SimpleBinaryExpression()
|
||||
{
|
||||
Expression<Func<int, bool>> expr = e => e > 10;
|
||||
Assert.Equal("e => (e > 10)", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_StringEquality()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Name == "John";
|
||||
Assert.Equal("e => (e.Name == \"John\")", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_IntComparison()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Age > 18;
|
||||
Assert.Equal("e => (e.Age > 18)", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_DoubleComparison()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Salary < 50000.50;
|
||||
Assert.Equal("e => (e.Salary < 50000.5)", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_FloatComparison()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Score >= 85.3f;
|
||||
Assert.Equal("e => (e.Score >= 85.3)", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_DecimalComparison()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Balance <= 1000.75m;
|
||||
Assert.Equal("e => (e.Balance <= 1000.75)", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ShortComparison()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Level == 3;
|
||||
Assert.Equal("e => (e.Level == 3)", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_LongComparison()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Population > 1000000L;
|
||||
Assert.Equal("e => (e.Population > 1000000)", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_EnumComparison()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.AccountStatus == Status.Inactive;
|
||||
Assert.Equal("e => (e.AccountStatus == 1)", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ArrayContainsValue()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Scores.Contains(100);
|
||||
Assert.Equal("e => e.Scores.Contains(100)", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ArrayNotContainsValue()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => !e.Scores.Contains(100);
|
||||
Assert.Equal("e => (!(e.Scores.Contains(100)))", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ArrayInValue()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Scores.Intersect(new [] { 100 }).Any();
|
||||
Assert.Equal("e => e.Scores.Intersect(new [] { 100 }).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ArrayNotInValue()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Scores.Except(new[] { 100 }).Any();
|
||||
Assert.Equal("e => e.Scores.Except(new [] { 100 }).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { 100 }.Intersect(e.Scores).Any();
|
||||
Assert.Equal("e => new [] { 100 }.Intersect(e.Scores).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ArrayNotInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { 100 }.Except(e.Scores).Any();
|
||||
Assert.Equal("e => new [] { 100 }.Except(e.Scores).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_IntArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { 100 }.Intersect(e.Scores).Any();
|
||||
Assert.Equal("e => new [] { 100 }.Intersect(e.Scores).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_IntArrayNotInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => !new[] { 100 }.Intersect(e.Scores).Any();
|
||||
Assert.Equal("e => (!(new [] { 100 }.Intersect(e.Scores).Any()))", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_DoubleArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { 99.99 }.Intersect(e.Salaries).Any();
|
||||
Assert.Equal("e => new [] { 99.99 }.Intersect(e.Salaries).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_FloatArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { 5.5f }.Intersect(e.Heights).Any();
|
||||
Assert.Equal("e => new [] { 5.5 }.Intersect(e.Heights).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_DecimalArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { 1000.75m }.Intersect(e.Balances).Any();
|
||||
Assert.Equal("e => new [] { 1000.75 }.Intersect(e.Balances).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ShortArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new [] { (short)3 }.Intersect(e.Levels).Any();
|
||||
Assert.Equal("e => new [] { 3 }.Intersect(e.Levels).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_LongArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new [] { 1000000L }.Intersect(e.Populations).Any();
|
||||
Assert.Equal("e => new [] { 1000000 }.Intersect(e.Populations).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_StringArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { "Alice", "Bob" }.Intersect(e.Names).Any();
|
||||
Assert.Equal("e => (new [] { \"Alice\", \"Bob\" }).Intersect(e.Names).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_GuidArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { Guid.Parse("12345678-1234-1234-1234-123456789abc") }.Intersect(e.Ids).Any();
|
||||
Assert.Equal("e => (new [] { Guid.Parse(\"12345678-1234-1234-1234-123456789abc\") }).Intersect(e.Ids).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_DateTimeArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { DateTime.Parse("2023-01-01T00:00:00.000Z") }.Intersect(e.CreatedDates).Any();
|
||||
Assert.Equal("e => (new [] { DateTime.Parse(\"2023-01-01T00:00:00.000Z\") }).Intersect(e.CreatedDates).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_DateTimeOffsetArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { DateTimeOffset.Parse("2023-01-01T10:30:00.000+00:00") }.Intersect(e.UpdatedDates).Any();
|
||||
Assert.Equal("e => (new [] { DateTimeOffset.Parse(\"2023-01-01T10:30:00.000+00:00\") }).Intersect(e.UpdatedDates).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_DateTimeWithRoundtripKind()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e =>
|
||||
DateTime.Parse("2023-01-01T00:00:00.000Z", null, DateTimeStyles.RoundtripKind) > e.CreatedAt;
|
||||
|
||||
Assert.Equal(
|
||||
"e => (DateTime.Parse(\"2023-01-01T00:00:00.000Z\", null, (System.Globalization.DateTimeStyles)128) > e.CreatedAt)",
|
||||
_serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_TimeOnlyArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { TimeOnly.Parse("12:00:00") }.Intersect(e.StartTimes).Any();
|
||||
Assert.Equal("e => (new [] { TimeOnly.Parse(\"12:00:00\") }).Intersect(e.StartTimes).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_DateOnlyArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { DateOnly.Parse("2000-01-01") }.Intersect(e.BirthDates).Any();
|
||||
Assert.Equal("e => (new [] { DateOnly.Parse(\"2000-01-01\") }).Intersect(e.BirthDates).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_EnumArrayInValueOposite()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => new[] { Status.Active, Status.Inactive }.Intersect(e.Statuses).Any();
|
||||
Assert.Equal("e => (new [] { (Radzen.Blazor.Tests.Status)0, (Radzen.Blazor.Tests.Status)1 }).Intersect(e.Statuses).Any()", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ListContainsValue()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Tags.Contains("VIP");
|
||||
Assert.Equal("e => e.Tags.Contains(\"VIP\")", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ListNotContainsValue()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => !e.Tags.Contains("VIP");
|
||||
Assert.Equal("e => (!(e.Tags.Contains(\"VIP\")))", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ListAnyCheck()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Children.Any(c => c.Age > 18);
|
||||
Assert.Equal("e => e.Children.Any(c => (c.Age > 18))", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ListNotAnyCheck()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => !e.Children.Any(c => c.Age > 18);
|
||||
Assert.Equal("e => (!(e.Children.Any(c => (c.Age > 18))))", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_EntitySubPropertyCheck()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Address.City == "New York";
|
||||
Assert.Equal("e => (e.Address.City == \"New York\")", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_ComplexExpressionWithProperties()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => e.Age > 18 && e.Tags.Contains("Member") || e.Address.City == "London";
|
||||
Assert.Equal("e => (((e.Age > 18) && e.Tags.Contains(\"Member\")) || (e.Address.City == \"London\"))", _serializer.Serialize(expr));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializes_NotContains()
|
||||
{
|
||||
Expression<Func<TestEntity, bool>> expr = e => !e.Tags.Contains("Member");
|
||||
Assert.Equal("e => (!(e.Tags.Contains(\"Member\")))", _serializer.Serialize(expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, value));
|
||||
|
||||
Assert.Contains(@$"<i class=""rzi"">{value}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rzi"">{value}</i>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -104,11 +104,11 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AllowCollapse, true));
|
||||
|
||||
Assert.Contains(@"<span class=""rz-fieldset-toggler rzi rzi-w rzi-minus""></span>", component.Markup);
|
||||
Assert.Contains(@"<span class=""notranslate rz-fieldset-toggler rzi rzi-w rzi-minus""></span>", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.Collapsed, true));
|
||||
|
||||
Assert.Contains(@"<span class=""rz-fieldset-toggler rzi rzi-w rzi-plus""></span>", component.Markup);
|
||||
Assert.Contains(@"<span class=""notranslate rz-fieldset-toggler rzi rzi-w rzi-plus""></span>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -184,13 +184,13 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.Contains("SummaryContent", component.Markup);
|
||||
Assert.Equal(
|
||||
"",
|
||||
component.Find(".rz-fieldset-content-summary").ParentElement.Attributes.First(attr => attr.Name == "style").Value
|
||||
"false",
|
||||
component.Find(".rz-fieldset-content-summary").ParentElement.ParentElement.Attributes.First(attr => attr.Name == "aria-hidden").Value
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Fieldset_DontRenders_SummaryWhenOpen()
|
||||
public void Fieldset_DoesNotRender_SummaryWhenOpen()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFieldset>();
|
||||
@@ -210,8 +210,8 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.Contains("SummaryContent", component.Markup);
|
||||
Assert.Equal(
|
||||
"display: none",
|
||||
component.Find(".rz-fieldset-content-summary").ParentElement.Attributes.First(attr => attr.Name == "style").Value
|
||||
"true",
|
||||
component.Find(".rz-fieldset-content-summary").ParentElement.ParentElement.Attributes.First(attr => attr.Name == "aria-hidden").Value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
182
Radzen.Blazor.Tests/FileInputTests.cs
Normal file
182
Radzen.Blazor.Tests/FileInputTests.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class FileInputTests
|
||||
{
|
||||
[Fact]
|
||||
public void FileInput_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>();
|
||||
|
||||
Assert.Contains(@"rz-fileupload", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_ChooseButton()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>();
|
||||
|
||||
Assert.Contains("rz-fileupload-choose", component.Markup);
|
||||
Assert.Contains("rz-button", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_ChooseText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChooseText, "Select File");
|
||||
});
|
||||
|
||||
Assert.Contains("Select File", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_DefaultChooseText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>();
|
||||
|
||||
Assert.Contains("Choose", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_Accept()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Accept, "application/pdf");
|
||||
});
|
||||
|
||||
Assert.Contains("accept=\"application/pdf\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_DefaultAccept()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>();
|
||||
|
||||
Assert.Contains("accept=\"image/*\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_FileInputElement()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>();
|
||||
|
||||
Assert.Contains("type=\"file\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_Title_WhenSet()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Title, "MyDocument.pdf");
|
||||
parameters.Add(p => p.Value, "data:application/pdf;base64,test");
|
||||
});
|
||||
|
||||
Assert.Contains("MyDocument.pdf", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_FileName_WhenTitleNotSet()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.FileName, "document.pdf");
|
||||
parameters.Add(p => p.Value, "data:application/pdf;base64,test");
|
||||
});
|
||||
|
||||
Assert.Contains("document.pdf", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_DeleteButton_WhenValueSet()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Value, "data:text/plain;base64,test");
|
||||
});
|
||||
|
||||
Assert.Contains("rz-icon-trash", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_CustomDeleteText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.DeleteText, "Remove File");
|
||||
parameters.Add(p => p.Value, "data:text/plain;base64,test");
|
||||
});
|
||||
|
||||
Assert.Contains("title=\"Remove File\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_ImagePreview_ForImageFile()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Value, "data:image/png;base64,test");
|
||||
});
|
||||
|
||||
Assert.Contains("<img", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FileInput_Renders_ImageAlternateText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenFileInput<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ImageAlternateText, "User Photo");
|
||||
parameters.Add(p => p.Value, "data:image/png;base64,test");
|
||||
});
|
||||
|
||||
Assert.Contains("alt=\"User Photo\"", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
163
Radzen.Blazor.Tests/FormFieldTests.cs
Normal file
163
Radzen.Blazor.Tests/FormFieldTests.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class FormFieldTests
|
||||
{
|
||||
[Fact]
|
||||
public void FormField_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>();
|
||||
|
||||
Assert.Contains(@"rz-form-field", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Text()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Text, "Email Address");
|
||||
});
|
||||
|
||||
Assert.Contains("Email Address", component.Markup);
|
||||
Assert.Contains("rz-form-field-label", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Variant_Outlined()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Variant, Variant.Outlined);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-variant-outlined", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Variant_Filled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Variant, Variant.Filled);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-variant-filled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Variant_Flat()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Variant, Variant.Flat);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-variant-flat", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Variant_Text()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Variant, Variant.Text);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-variant-text", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_AllowFloatingLabel_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFloatingLabel, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-floating-label", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_AllowFloatingLabel_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFloatingLabel, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-floating-label", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Component_Attribute()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Component, "email-input");
|
||||
});
|
||||
|
||||
Assert.Contains("for=\"email-input\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Helper()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Helper, builder => builder.AddContent(0, "Enter your email address"));
|
||||
});
|
||||
|
||||
Assert.Contains("rz-form-field-helper", component.Markup);
|
||||
Assert.Contains("Enter your email address", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_Start()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Start, builder => builder.AddMarkupContent(0, "<span>Start</span>"));
|
||||
});
|
||||
|
||||
Assert.Contains("rz-form-field-start", component.Markup);
|
||||
Assert.Contains("Start", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_End()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.End, builder => builder.AddMarkupContent(0, "<span>End</span>"));
|
||||
});
|
||||
|
||||
Assert.Contains("rz-form-field-end", component.Markup);
|
||||
Assert.Contains("End", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormField_Renders_FormFieldContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenFormField>();
|
||||
|
||||
Assert.Contains("rz-form-field-content", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
103
Radzen.Blazor.Tests/HeadingTests.cs
Normal file
103
Radzen.Blazor.Tests/HeadingTests.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class HeadingTests
|
||||
{
|
||||
[Fact]
|
||||
public void Heading_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
Assert.Contains(@"rz-heading", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_TextParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
var text = "Test Heading";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Text, text));
|
||||
|
||||
Assert.Contains(text, component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_H1_ByDefault()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
var text = "Heading Text";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Text, text));
|
||||
|
||||
Assert.Contains("<h1", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_H2_Size()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
var text = "Heading 2";
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Text, text);
|
||||
parameters.Add(p => p.Size, "H2");
|
||||
});
|
||||
|
||||
Assert.Contains("<h2", component.Markup);
|
||||
Assert.Contains(text, component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_H3_Size()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, "H3"));
|
||||
|
||||
Assert.Contains("<h3", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_H4_Size()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, "H4"));
|
||||
|
||||
Assert.Contains("<h4", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_H5_Size()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, "H5"));
|
||||
|
||||
Assert.Contains("<h5", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heading_Renders_H6_Size()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenHeading>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, "H6"));
|
||||
|
||||
Assert.Contains("<h6", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
104
Radzen.Blazor.Tests/HtmlEditorTests.cs
Normal file
104
Radzen.Blazor.Tests/HtmlEditorTests.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using Bunit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class HtmlEditorTests
|
||||
{
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>();
|
||||
|
||||
Assert.Contains(@"rz-html-editor", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_ShowToolbar_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowToolbar, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-html-editor-toolbar", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_ShowToolbar_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowToolbar, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-html-editor-toolbar", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_Mode_Design()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Mode, HtmlEditorMode.Design);
|
||||
});
|
||||
|
||||
// Design mode shows the content editable div
|
||||
Assert.Contains("contenteditable", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_Mode_Source()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Mode, HtmlEditorMode.Source);
|
||||
});
|
||||
|
||||
// Source mode shows the textarea for HTML editing
|
||||
Assert.Contains("rz-html-editor-source", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_Disabled_Attribute()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HtmlEditor_Renders_ContentArea()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
var component = ctx.RenderComponent<RadzenHtmlEditor>();
|
||||
|
||||
Assert.Contains("rz-html-editor-content", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Radzen.Blazor.Tests
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
|
||||
|
||||
Assert.Contains(@$">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"class=""rzi""", component.Markup);
|
||||
Assert.Contains(@$"class=""notranslate rzi""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
|
||||
|
||||
Assert.Contains(@$"<i class=""rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rzi"">{icon}</i>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -78,6 +78,20 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Contains(@$"target=""{target}""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Link_Renders_DisabledParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenLink>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, true));
|
||||
|
||||
Assert.Contains("class=\"rz-link rz-link-disabled active\"", component.Markup);
|
||||
|
||||
Assert.DoesNotContain("href=", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Icon_Renders_UnmatchedParameter()
|
||||
{
|
||||
|
||||
197
Radzen.Blazor.Tests/ListBoxTests.cs
Normal file
197
Radzen.Blazor.Tests/ListBoxTests.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class ListBoxTests
|
||||
{
|
||||
class Item
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>();
|
||||
|
||||
Assert.Contains(@"rz-listbox", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_WithData_SimpleList()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Apple", "Banana", "Cherry" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenListBox<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("Apple", component.Markup);
|
||||
Assert.Contains("Banana", component.Markup);
|
||||
Assert.Contains("Cherry", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_WithData_CustomTextValueProperties()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "First Item" },
|
||||
new Item { Id = 2, Name = "Second Item" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
parameters.Add(p => p.ValueProperty, "Id");
|
||||
});
|
||||
|
||||
Assert.Contains("First Item", component.Markup);
|
||||
Assert.Contains("Second Item", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_AllowFiltering()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
parameters.Add(p => p.Data, new List<string> { "Item1", "Item2" });
|
||||
});
|
||||
|
||||
Assert.Contains("rz-listbox-filter", component.Markup);
|
||||
Assert.Contains("rz-listbox-header", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_Disabled_Attribute()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_Multiple_WithSelectAll()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<IEnumerable<int>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.SelectAllText, "Select All Items");
|
||||
parameters.Add(p => p.Data, new List<string> { "Item1", "Item2" });
|
||||
});
|
||||
|
||||
Assert.Contains("Select All Items", component.Markup);
|
||||
Assert.Contains("rz-chkbox", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_FilterPlaceholder()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Placeholder, "Select an item");
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
parameters.Add(p => p.Data, new List<string> { "Item1", "Item2" });
|
||||
});
|
||||
|
||||
Assert.Contains("Select an item", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_Multiple_WithCheckboxes()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1", "Item2" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenListBox<IEnumerable<string>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
// Multiple selection shows checkboxes in header
|
||||
Assert.Contains("rz-listbox-header-w-checkbox", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_ReadOnly()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ReadOnly, true);
|
||||
});
|
||||
|
||||
Assert.Contains("readonly", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_TabIndex()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>();
|
||||
|
||||
Assert.Contains("tabindex=", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_ListWrapper()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<string> { "Item1" };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenListBox<string>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, data);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-listbox-list-wrapper", component.Markup);
|
||||
Assert.Contains("rz-listbox-list", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListBox_Renders_SearchAriaLabel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenListBox<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
parameters.Add(p => p.SearchAriaLabel, "Search items");
|
||||
parameters.Add(p => p.Data, new List<string> { "Item1" });
|
||||
});
|
||||
|
||||
Assert.Contains("aria-label=\"Search items\"", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
314
Radzen.Blazor.Tests/Markdown/BlockQuoteTests.cs
Normal file
314
Radzen.Blazor.Tests/Markdown/BlockQuoteTests.cs
Normal file
@@ -0,0 +1,314 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class BlockQuoteTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_BasicBlockQuote()
|
||||
{
|
||||
|
||||
Assert.Equal(@"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>", ToXml(@"> foo"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"> # Foo
|
||||
> bar
|
||||
> baz", @"<document>
|
||||
<block_quote>
|
||||
<heading level=""1"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"># Foo
|
||||
>bar
|
||||
> baz", @"<document>
|
||||
<block_quote>
|
||||
<heading level=""1"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@" > # Foo
|
||||
> bar
|
||||
> baz", @"<document>
|
||||
<block_quote>
|
||||
<heading level=""1"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@" > # Foo
|
||||
> bar
|
||||
> baz", @"<document>
|
||||
<code_block>> # Foo
|
||||
> bar
|
||||
> baz
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"> # Foo
|
||||
> bar
|
||||
baz", @"<document>
|
||||
<block_quote>
|
||||
<heading level=""1"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"> bar
|
||||
baz
|
||||
> foo", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
<softbreak />
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"> foo
|
||||
---", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"> - foo
|
||||
- bar", @"<document>
|
||||
<block_quote>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</block_quote>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"> foo
|
||||
bar", @"<document>
|
||||
<block_quote>
|
||||
<code_block>foo
|
||||
</code_block>
|
||||
</block_quote>
|
||||
<code_block>bar
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"> ```
|
||||
foo
|
||||
```", @"<document>
|
||||
<block_quote>
|
||||
<code_block></code_block>
|
||||
</block_quote>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<code_block></code_block>
|
||||
</document>")]
|
||||
[InlineData(@"> foo
|
||||
- bar", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<softbreak />
|
||||
<text>- bar</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@">", @"<document>
|
||||
<block_quote />
|
||||
</document>")]
|
||||
[InlineData(@">
|
||||
>
|
||||
> ", @"<document>
|
||||
<block_quote />
|
||||
</document>")]
|
||||
[InlineData(@">
|
||||
> foo
|
||||
> ", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"> foo
|
||||
|
||||
> bar", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"> foo
|
||||
>
|
||||
> bar", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"foo
|
||||
> bar", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"> aaa
|
||||
***
|
||||
> bbb", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>aaa</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
<thematic_break />
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>bbb</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"> bar
|
||||
baz", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"> bar
|
||||
|
||||
baz", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"> bar
|
||||
>
|
||||
baz", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"> > > foo
|
||||
bar", @"<document>
|
||||
<block_quote>
|
||||
<block_quote>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<softbreak />
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</block_quote>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@">>> foo
|
||||
> bar
|
||||
>>baz", @"<document>
|
||||
<block_quote>
|
||||
<block_quote>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<softbreak />
|
||||
<text>bar</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</block_quote>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"> code
|
||||
|
||||
> not code", @"<document>
|
||||
<block_quote>
|
||||
<code_block>code
|
||||
</code_block>
|
||||
</block_quote>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>not code</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
public void Parse_BlockQuote(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
175
Radzen.Blazor.Tests/Markdown/CodeTests.cs
Normal file
175
Radzen.Blazor.Tests/Markdown/CodeTests.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class CodeTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("`foo`",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<code>foo</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("`` foo ` bar ``",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<code>foo ` bar</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("` `` `",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<code>``</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_BasicCode_ReturnsCodeNode(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("` `` `",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<code> `` </code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("` a`",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<code> a</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"` `
|
||||
` `",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<code> </code>
|
||||
<softbreak />
|
||||
<code> </code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_CodeWithSpaces_PreservesSpaces(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"``
|
||||
foo
|
||||
bar
|
||||
baz
|
||||
``",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<code>foo bar baz</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"``
|
||||
foo
|
||||
``",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<code>foo </code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"`foo bar
|
||||
baz`",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<code>foo bar baz</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
|
||||
public void Parse_CodeWithLineBreaks_ConvertsToSpace(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("`foo\\`bar`", @"<document>
|
||||
<paragraph>
|
||||
<code>foo\</code>
|
||||
<text>bar</text>
|
||||
<text>`</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("``foo`bar``", @"<document>
|
||||
<paragraph>
|
||||
<code>foo`bar</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("` foo `` bar `", @"<document>
|
||||
<paragraph>
|
||||
<code>foo `` bar</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_CodeWithBacktics(string mardown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(mardown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("*foo`*`", @"<document>
|
||||
<paragraph>
|
||||
<text>*</text>
|
||||
<text>foo</text>
|
||||
<code>*</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("[not a `link](/foo`)",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>not a </text>
|
||||
<code>link](/foo</code>
|
||||
<text>)</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("`<https://foo.bar.`baz>`",@"<document>
|
||||
<paragraph>
|
||||
<code><https://foo.bar.</code>
|
||||
<text>baz></text>
|
||||
<text>`</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_CodePrecedence(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("```foo``", @"<document>
|
||||
<paragraph>
|
||||
<text>```</text>
|
||||
<text>foo</text>
|
||||
<text>``</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("`foo", @"<document>
|
||||
<paragraph>
|
||||
<text>`</text>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("`foo``bar``", @"<document>
|
||||
<paragraph>
|
||||
<text>`</text>
|
||||
<text>foo</text>
|
||||
<code>bar</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_UnmatchingBacktics(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
272
Radzen.Blazor.Tests/Markdown/EmphasisTests.cs
Normal file
272
Radzen.Blazor.Tests/Markdown/EmphasisTests.cs
Normal file
@@ -0,0 +1,272 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class EmphasisTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"**foo** bar
|
||||
baz",@"<document>
|
||||
<paragraph>
|
||||
<strong>
|
||||
<text>foo</text>
|
||||
</strong>
|
||||
<text> bar</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("*foo bar*",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>foo bar</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("a * foo bar*",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>a </text>
|
||||
<text>*</text>
|
||||
<text> foo bar</text>
|
||||
<text>*</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("a*\"foo\"*",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
<text>*</text>
|
||||
<text>""foo""</text>
|
||||
<text>*</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("* a *",
|
||||
@"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a </text>
|
||||
<text>*</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData("foo*bar*",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<emph>
|
||||
<text>bar</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("5*6*78",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>5</text>
|
||||
<emph>
|
||||
<text>6</text>
|
||||
</emph>
|
||||
<text>78</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("_foo bar_",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>foo bar</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("_ foo bar_",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>_</text>
|
||||
<text> foo bar</text>
|
||||
<text>_</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("a_\"foo\"_",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
<text>_</text>
|
||||
<text>""foo""</text>
|
||||
<text>_</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("foo_bar_",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<text>_</text>
|
||||
<text>bar</text>
|
||||
<text>_</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("5_6_78",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>5</text>
|
||||
<text>_</text>
|
||||
<text>6</text>
|
||||
<text>_</text>
|
||||
<text>78</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("пристаням_стремятся_",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>пристаням</text>
|
||||
<text>_</text>
|
||||
<text>стремятся</text>
|
||||
<text>_</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("aa_\"bb\"_cc",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>aa</text>
|
||||
<text>_</text>
|
||||
<text>""bb""</text>
|
||||
<text>_</text>
|
||||
<text>cc</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("foo-_(bar)_",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>foo-</text>
|
||||
<emph>
|
||||
<text>(bar)</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("_foo*",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>_</text>
|
||||
<text>foo</text>
|
||||
<text>*</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("*foo bar *",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>*</text>
|
||||
<text>foo bar </text>
|
||||
<text>*</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("*foo bar\nbaz*",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>foo bar</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("*(*foo)",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>*</text>
|
||||
<text>(</text>
|
||||
<text>*</text>
|
||||
<text>foo)</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("*(*foo*)*",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>(</text>
|
||||
<emph>
|
||||
<text>foo</text>
|
||||
</emph>
|
||||
<text>)</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("*foo*bar",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>foo</text>
|
||||
</emph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("_foo bar _",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>_</text>
|
||||
<text>foo bar </text>
|
||||
<text>_</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("_(_foo_)_",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>(</text>
|
||||
<emph>
|
||||
<text>foo</text>
|
||||
</emph>
|
||||
<text>)</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("_foo_bar",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>_</text>
|
||||
<text>foo</text>
|
||||
<text>_</text>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("_пристаням_стремятся",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>_</text>
|
||||
<text>пристаням</text>
|
||||
<text>_</text>
|
||||
<text>стремятся</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("_foo_bar_baz_",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>foo</text>
|
||||
<text>_</text>
|
||||
<text>bar</text>
|
||||
<text>_</text>
|
||||
<text>baz</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("_(bar)_.",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>(bar)</text>
|
||||
</emph>
|
||||
<text>.</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_EmphasisRules_AdheresToCommonMarkSpec(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
264
Radzen.Blazor.Tests/Markdown/FencedCodeBlockTests.cs
Normal file
264
Radzen.Blazor.Tests/Markdown/FencedCodeBlockTests.cs
Normal file
@@ -0,0 +1,264 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class FencedCodeBlockTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_BasicFencedCodeBlock()
|
||||
{
|
||||
Assert.Equal(@"<document>
|
||||
<code_block>foo
|
||||
</code_block>
|
||||
</document>", ToXml(@"```
|
||||
foo
|
||||
```"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"```
|
||||
<
|
||||
>
|
||||
```", @"<document>
|
||||
<code_block><
|
||||
>
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"~~~
|
||||
<
|
||||
>
|
||||
~~~", @"<document>
|
||||
<code_block><
|
||||
>
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"``
|
||||
foo
|
||||
``", @"<document>
|
||||
<paragraph>
|
||||
<code>foo</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"```
|
||||
aaa
|
||||
~~~
|
||||
```", @"<document>
|
||||
<code_block>aaa
|
||||
~~~
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"~~~
|
||||
aaa
|
||||
```
|
||||
~~~", @"<document>
|
||||
<code_block>aaa
|
||||
```
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"````
|
||||
aaa
|
||||
```
|
||||
``````", @"<document>
|
||||
<code_block>aaa
|
||||
```
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"~~~~
|
||||
aaa
|
||||
~~~
|
||||
~~~~", @"<document>
|
||||
<code_block>aaa
|
||||
~~~
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"```", @"<document>
|
||||
<code_block></code_block>
|
||||
</document>")]
|
||||
[InlineData(@"`````
|
||||
|
||||
```
|
||||
aaa", @"<document>
|
||||
<code_block>
|
||||
```
|
||||
aaa
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"> ```
|
||||
> aaa
|
||||
|
||||
bbb", @"<document>
|
||||
<block_quote>
|
||||
<code_block>aaa
|
||||
</code_block>
|
||||
</block_quote>
|
||||
<paragraph>
|
||||
<text>bbb</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"```
|
||||
|
||||
|
||||
```", @"<document>
|
||||
<code_block>
|
||||
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"```
|
||||
```", @"<document>
|
||||
<code_block></code_block>
|
||||
</document>")]
|
||||
[InlineData(@" ```
|
||||
aaa
|
||||
aaa
|
||||
```", @"<document>
|
||||
<code_block>aaa
|
||||
aaa
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@" ```
|
||||
aaa
|
||||
aaa
|
||||
aaa
|
||||
```", @"<document>
|
||||
<code_block>aaa
|
||||
aaa
|
||||
aaa
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@" ```
|
||||
aaa
|
||||
aaa
|
||||
aaa
|
||||
```", @"<document>
|
||||
<code_block>aaa
|
||||
aaa
|
||||
aaa
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@" ```
|
||||
aaa
|
||||
```", @"<document>
|
||||
<code_block>```
|
||||
aaa
|
||||
```
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"```
|
||||
aaa
|
||||
```", @"<document>
|
||||
<code_block>aaa
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@" ```
|
||||
aaa
|
||||
````", @"<document>
|
||||
<code_block>aaa
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"```
|
||||
aaa
|
||||
```", @"<document>
|
||||
<code_block>aaa
|
||||
```
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"``` ```
|
||||
aaa", @"<document>
|
||||
<paragraph>
|
||||
<code> </code>
|
||||
<softbreak />
|
||||
<text>aaa</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"~~~~~~
|
||||
aaa
|
||||
~~~ ~~", @"<document>
|
||||
<code_block>aaa
|
||||
~~~ ~~
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"foo
|
||||
```
|
||||
bar
|
||||
```
|
||||
baz", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<code_block>bar
|
||||
</code_block>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo
|
||||
---
|
||||
~~~
|
||||
bar
|
||||
~~~
|
||||
# baz", @"<document>
|
||||
<heading level=""2"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
<code_block>bar
|
||||
</code_block>
|
||||
<heading level=""1"">
|
||||
<text>baz</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"```ruby
|
||||
def foo(x)
|
||||
return 3
|
||||
end
|
||||
```", @"<document>
|
||||
<code_block info=""ruby"">def foo(x)
|
||||
return 3
|
||||
end
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"~~~~ ruby startline=3 $%@#$
|
||||
def foo(x)
|
||||
return 3
|
||||
end
|
||||
~~~~~~~", @"<document>
|
||||
<code_block info=""ruby startline=3 $%@#$"">def foo(x)
|
||||
return 3
|
||||
end
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"````;
|
||||
````", @"<document>
|
||||
<code_block info="";""></code_block>
|
||||
</document>")]
|
||||
[InlineData(@"``` aa ```
|
||||
foo", @"<document>
|
||||
<paragraph>
|
||||
<code>aa</code>
|
||||
<softbreak />
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"~~~ aa ``` ~~~
|
||||
foo
|
||||
~~~", @"<document>
|
||||
<code_block info=""aa ``` ~~~"">foo
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"```
|
||||
``` aaa
|
||||
```", @"<document>
|
||||
<code_block>``` aaa
|
||||
</code_block>
|
||||
</document>")]
|
||||
public void Parse_FencedCodeBlock(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
132
Radzen.Blazor.Tests/Markdown/HardLineBreakTests.cs
Normal file
132
Radzen.Blazor.Tests/Markdown/HardLineBreakTests.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class HardLineBreakTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"foo
|
||||
baz", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<linebreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("foo \r\nbaz", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<linebreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo\
|
||||
baz", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<linebreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo
|
||||
baz", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<linebreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo
|
||||
bar", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<linebreak />
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo\
|
||||
bar", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<linebreak />
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"*foo
|
||||
bar*", @"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>foo</text>
|
||||
<linebreak />
|
||||
<text>bar</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"*foo\
|
||||
bar*", @"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>foo</text>
|
||||
<linebreak />
|
||||
<text>bar</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"`code\
|
||||
span`", @"<document>
|
||||
<paragraph>
|
||||
<code>code\ span</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"`code
|
||||
span`", @"<document>
|
||||
<paragraph>
|
||||
<code>code span</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<a href=""foo
|
||||
bar"">", @"<document>
|
||||
<paragraph>
|
||||
<html_inline><a href=""foo
|
||||
bar""></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<a href=""foo\
|
||||
bar"">", @"<document>
|
||||
<paragraph>
|
||||
<html_inline><a href=""foo\
|
||||
bar""></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo\", @"<document>
|
||||
<paragraph>
|
||||
<text>foo\</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo ", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"### foo\", @"<document>
|
||||
<heading level=""3"">
|
||||
<text>foo\</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"### foo ", @"<document>
|
||||
<heading level=""3"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
public void Parse_HardLineBreak(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
511
Radzen.Blazor.Tests/Markdown/HeadingTests.cs
Normal file
511
Radzen.Blazor.Tests/Markdown/HeadingTests.cs
Normal file
@@ -0,0 +1,511 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class HeadingTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_BasicAtxHeading()
|
||||
{
|
||||
Assert.Equal(@"<document>
|
||||
<heading level=""1"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
</document>", ToXml("# foo"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"# foo
|
||||
## foo
|
||||
### foo
|
||||
#### foo
|
||||
##### foo
|
||||
###### foo", @"<document>
|
||||
<heading level=""1"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
<heading level=""2"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
<heading level=""3"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
<heading level=""4"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
<heading level=""5"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
<heading level=""6"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"####### foo", @"<document>
|
||||
<paragraph>
|
||||
<text>####### foo</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"#5 bolt
|
||||
|
||||
#hashtag", @"<document>
|
||||
<paragraph>
|
||||
<text>#5 bolt</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>#hashtag</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"\## foo", @"<document>
|
||||
<paragraph>
|
||||
<text>#</text>
|
||||
<text># foo</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"# foo *bar* \*baz\*", @"<document>
|
||||
<heading level=""1"">
|
||||
<text>foo </text>
|
||||
<emph>
|
||||
<text>bar</text>
|
||||
</emph>
|
||||
<text> </text>
|
||||
<text>*</text>
|
||||
<text>baz</text>
|
||||
<text>*</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"# foo ", @"<document>
|
||||
<heading level=""1"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@" ### foo
|
||||
## foo
|
||||
# foo", @"<document>
|
||||
<heading level=""3"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
<heading level=""2"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
<heading level=""1"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@" # foo", @"<document>
|
||||
<code_block># foo
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"foo
|
||||
# bar", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<softbreak />
|
||||
<text># bar</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"## foo ##
|
||||
### bar ###", @"<document>
|
||||
<heading level=""2"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
<heading level=""3"">
|
||||
<text>bar</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"# foo ##################################
|
||||
##### foo ##", @"<document>
|
||||
<heading level=""1"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
<heading level=""5"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"### foo ### ", @"<document>
|
||||
<heading level=""3"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"### foo ### b", @"<document>
|
||||
<heading level=""3"">
|
||||
<text>foo ### b</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"# foo#", @"<document>
|
||||
<heading level=""1"">
|
||||
<text>foo#</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"### foo \###
|
||||
## foo #\##
|
||||
# foo \#", @"<document>
|
||||
<heading level=""3"">
|
||||
<text>foo </text>
|
||||
<text>#</text>
|
||||
<text>##</text>
|
||||
</heading>
|
||||
<heading level=""2"">
|
||||
<text>foo #</text>
|
||||
<text>#</text>
|
||||
<text>#</text>
|
||||
</heading>
|
||||
<heading level=""1"">
|
||||
<text>foo </text>
|
||||
<text>#</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"****
|
||||
## foo
|
||||
****", @"<document>
|
||||
<thematic_break />
|
||||
<heading level=""2"">
|
||||
<text>foo</text>
|
||||
</heading>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"Foo bar
|
||||
# baz
|
||||
Bar foo", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo bar</text>
|
||||
</paragraph>
|
||||
<heading level=""1"">
|
||||
<text>baz</text>
|
||||
</heading>
|
||||
<paragraph>
|
||||
<text>Bar foo</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"##
|
||||
#
|
||||
### ###", @"<document>
|
||||
<heading level=""2"" />
|
||||
<heading level=""1"" />
|
||||
<heading level=""3"" />
|
||||
</document>")]
|
||||
public void Parse_AtxHeading(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"Foo *bar*
|
||||
=========
|
||||
|
||||
Foo *baz*
|
||||
---------", @"<document>
|
||||
<heading level=""1"">
|
||||
<text>Foo </text>
|
||||
<emph>
|
||||
<text>bar</text>
|
||||
</emph>
|
||||
</heading>
|
||||
<heading level=""2"">
|
||||
<text>Foo </text>
|
||||
<emph>
|
||||
<text>baz</text>
|
||||
</emph>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"Foo *bar
|
||||
baz*
|
||||
====", @"<document>
|
||||
<heading level=""1"">
|
||||
<text>Foo </text>
|
||||
<emph>
|
||||
<text>bar</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</emph>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@" Foo *bar
|
||||
baz*
|
||||
====", @"<document>
|
||||
<heading level=""1"">
|
||||
<text>Foo </text>
|
||||
<emph>
|
||||
<text>bar</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</emph>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
-------------------------
|
||||
|
||||
Foo
|
||||
=", @"<document>
|
||||
<heading level=""2"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
<heading level=""1"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@" Foo
|
||||
---
|
||||
|
||||
Foo
|
||||
-----
|
||||
|
||||
Foo
|
||||
===", @"<document>
|
||||
<heading level=""2"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
<heading level=""2"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
<heading level=""1"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@" Foo
|
||||
---
|
||||
|
||||
Foo
|
||||
---", @"<document>
|
||||
<code_block>Foo
|
||||
---
|
||||
|
||||
Foo
|
||||
</code_block>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
---- ", @"<document>
|
||||
<heading level=""2"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
---", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
<softbreak />
|
||||
<text>---</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
= =
|
||||
|
||||
Foo
|
||||
--- -", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
<softbreak />
|
||||
<text>= =</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
</paragraph>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
-----", @"<document>
|
||||
<heading level=""2"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"Foo\
|
||||
----", @"<document>
|
||||
<heading level=""2"">
|
||||
<text>Foo\</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"`Foo
|
||||
----
|
||||
`
|
||||
|
||||
<a title=""a lot
|
||||
---
|
||||
of dashes""/>", @"<document>
|
||||
<heading level=""2"">
|
||||
<text>`</text>
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
<paragraph>
|
||||
<text>`</text>
|
||||
</paragraph>
|
||||
<heading level=""2"">
|
||||
<text><a title=""a lot</text>
|
||||
</heading>
|
||||
<paragraph>
|
||||
<text>of dashes""/></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"> Foo
|
||||
---", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"> foo
|
||||
bar
|
||||
===", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<softbreak />
|
||||
<text>bar</text>
|
||||
<softbreak />
|
||||
<text>===</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"- Foo
|
||||
---", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
Bar
|
||||
---", @"<document>
|
||||
<heading level=""2"">
|
||||
<text>Foo</text>
|
||||
<softbreak />
|
||||
<text>Bar</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"---
|
||||
Foo
|
||||
---
|
||||
Bar
|
||||
---
|
||||
Baz", @"<document>
|
||||
<thematic_break />
|
||||
<heading level=""2"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
<heading level=""2"">
|
||||
<text>Bar</text>
|
||||
</heading>
|
||||
<paragraph>
|
||||
<text>Baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"
|
||||
====", @"<document>
|
||||
<paragraph>
|
||||
<text>====</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"---
|
||||
---", @"<document>
|
||||
<thematic_break />
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"- foo
|
||||
-----", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@" foo
|
||||
---", @"<document>
|
||||
<code_block>foo
|
||||
</code_block>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"> foo
|
||||
-----", @"<document>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"\> foo
|
||||
------", @"<document>
|
||||
<heading level=""2"">
|
||||
<text>> foo</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
|
||||
bar
|
||||
---
|
||||
baz", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
</paragraph>
|
||||
<heading level=""2"">
|
||||
<text>bar</text>
|
||||
</heading>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
bar
|
||||
|
||||
---
|
||||
|
||||
baz", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
<softbreak />
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
<thematic_break />
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
bar
|
||||
* * *
|
||||
baz", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
<softbreak />
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
<thematic_break />
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
bar
|
||||
\---
|
||||
baz", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
<softbreak />
|
||||
<text>bar</text>
|
||||
<softbreak />
|
||||
<text>-</text>
|
||||
<text>--</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_SetExtHeading(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
487
Radzen.Blazor.Tests/Markdown/HtmlBlockTests.cs
Normal file
487
Radzen.Blazor.Tests/Markdown/HtmlBlockTests.cs
Normal file
@@ -0,0 +1,487 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class HtmlBlockTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"<table><tr><td>
|
||||
<pre>
|
||||
**Hello**,
|
||||
|
||||
_world_.
|
||||
</pre>
|
||||
</td></tr></table>", @"<document>
|
||||
<html_block><table><tr><td>
|
||||
<pre>
|
||||
**Hello**,</html_block>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>world</text>
|
||||
</emph>
|
||||
<text>.</text>
|
||||
<softbreak />
|
||||
<html_inline></pre></html_inline>
|
||||
</paragraph>
|
||||
<html_block></td></tr></table></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<table>
|
||||
<tr>
|
||||
<td>
|
||||
hi
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
okay.", @"<document>
|
||||
<html_block><table>
|
||||
<tr>
|
||||
<td>
|
||||
hi
|
||||
</td>
|
||||
</tr>
|
||||
</table></html_block>
|
||||
<paragraph>
|
||||
<text>okay.</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@" <div>
|
||||
*hello*
|
||||
<foo><a>", @"<document>
|
||||
<html_block> <div>
|
||||
*hello*
|
||||
<foo><a></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"</div>
|
||||
*foo*", @"<document>
|
||||
<html_block></div>
|
||||
*foo*</html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<DIV CLASS=""foo"">
|
||||
|
||||
*Markdown*
|
||||
|
||||
</DIV>", @"<document>
|
||||
<html_block><DIV CLASS=""foo""></html_block>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>Markdown</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
<html_block></DIV></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<div id=""foo""
|
||||
class=""bar"">
|
||||
</div>", @"<document>
|
||||
<html_block><div id=""foo""
|
||||
class=""bar"">
|
||||
</div></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<div id=""foo"" class=""bar
|
||||
baz"">
|
||||
</div>", @"<document>
|
||||
<html_block><div id=""foo"" class=""bar
|
||||
baz"">
|
||||
</div></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<div>
|
||||
*foo*
|
||||
|
||||
*bar*", @"<document>
|
||||
<html_block><div>
|
||||
*foo*</html_block>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>bar</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<div id=""foo""
|
||||
*hi*", @"<document>
|
||||
<html_block><div id=""foo""
|
||||
*hi*</html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<div class
|
||||
foo", @"<document>
|
||||
<html_block><div class
|
||||
foo</html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<div *???-&&&-<---
|
||||
*foo*", @"<document>
|
||||
<html_block><div *???-&&&-<---
|
||||
*foo*</html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<div><a href=""bar"">*foo*</a></div>", @"<document>
|
||||
<html_block><div><a href=""bar"">*foo*</a></div></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<table><tr><td>
|
||||
foo
|
||||
</td></tr></table>", @"<document>
|
||||
<html_block><table><tr><td>
|
||||
foo
|
||||
</td></tr></table></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<div></div>
|
||||
``` c
|
||||
int x = 33;
|
||||
```", @"<document>
|
||||
<html_block><div></div>
|
||||
``` c
|
||||
int x = 33;
|
||||
```</html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<a href=""foo"">
|
||||
*bar*
|
||||
</a>", @"<document>
|
||||
<html_block><a href=""foo"">
|
||||
*bar*
|
||||
</a></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<Warning>
|
||||
*bar*
|
||||
</Warning>", @"<document>
|
||||
<html_block><Warning>
|
||||
*bar*
|
||||
</Warning></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<i class=""foo"">
|
||||
*bar*
|
||||
</i>", @"<document>
|
||||
<html_block><i class=""foo"">
|
||||
*bar*
|
||||
</i></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"</ins>
|
||||
*bar*", @"<document>
|
||||
<html_block></ins>
|
||||
*bar*</html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<del>
|
||||
*foo*
|
||||
</del>", @"<document>
|
||||
<html_block><del>
|
||||
*foo*
|
||||
</del></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<del>
|
||||
|
||||
*foo*
|
||||
|
||||
</del>", @"<document>
|
||||
<html_block><del></html_block>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>foo</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
<html_block></del></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<del>*foo*</del>", @"<document>
|
||||
<paragraph>
|
||||
<html_inline><del></html_inline>
|
||||
<emph>
|
||||
<text>foo</text>
|
||||
</emph>
|
||||
<html_inline></del></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<pre language=""haskell""><code>
|
||||
import Text.HTML.TagSoup
|
||||
|
||||
main :: IO ()
|
||||
main = print $ parseTags tags
|
||||
</code></pre>
|
||||
okay", @"<document>
|
||||
<html_block><pre language=""haskell""><code>
|
||||
import Text.HTML.TagSoup
|
||||
|
||||
main :: IO ()
|
||||
main = print $ parseTags tags
|
||||
</code></pre></html_block>
|
||||
<paragraph>
|
||||
<text>okay</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<script type=""text/javascript"">
|
||||
// JavaScript example
|
||||
|
||||
document.getElementById(""demo"").innerHTML = ""Hello JavaScript!"";
|
||||
</script>
|
||||
okay", @"<document>
|
||||
<html_block><script type=""text/javascript"">
|
||||
// JavaScript example
|
||||
|
||||
document.getElementById(""demo"").innerHTML = ""Hello JavaScript!"";
|
||||
</script></html_block>
|
||||
<paragraph>
|
||||
<text>okay</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<textarea>
|
||||
|
||||
*foo*
|
||||
|
||||
_bar_
|
||||
|
||||
</textarea>", @"<document>
|
||||
<html_block><textarea>
|
||||
|
||||
*foo*
|
||||
|
||||
_bar_
|
||||
|
||||
</textarea></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<style
|
||||
type=""text/css"">
|
||||
h1 {color:red;}
|
||||
|
||||
p {color:blue;}
|
||||
</style>
|
||||
okay",@"<document>
|
||||
<html_block><style
|
||||
type=""text/css"">
|
||||
h1 {color:red;}
|
||||
|
||||
p {color:blue;}
|
||||
</style></html_block>
|
||||
<paragraph>
|
||||
<text>okay</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<style
|
||||
type=""text/css"">
|
||||
|
||||
foo", @"<document>
|
||||
<html_block><style
|
||||
type=""text/css"">
|
||||
|
||||
foo</html_block>
|
||||
</document>")]
|
||||
[InlineData(@"> <div>
|
||||
> foo
|
||||
|
||||
bar", @"<document>
|
||||
<block_quote>
|
||||
<html_block><div>
|
||||
foo</html_block>
|
||||
</block_quote>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"- <div>
|
||||
- foo", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<html_block><div></html_block>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"<style>p{color:red;}</style>
|
||||
*foo*", @"<document>
|
||||
<html_block><style>p{color:red;}</style></html_block>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>foo</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<!-- foo -->*bar*
|
||||
*baz*", @"<document>
|
||||
<html_block><!-- foo -->*bar*</html_block>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>baz</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<script>
|
||||
foo
|
||||
</script>1. *bar*", @"<document>
|
||||
<html_block><script>
|
||||
foo
|
||||
</script>1. *bar*</html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<!-- Foo
|
||||
|
||||
bar
|
||||
baz -->
|
||||
okay", @"<document>
|
||||
<html_block><!-- Foo
|
||||
|
||||
bar
|
||||
baz --></html_block>
|
||||
<paragraph>
|
||||
<text>okay</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<?php
|
||||
|
||||
echo '>';
|
||||
|
||||
?>
|
||||
okay", @"<document>
|
||||
<html_block><?php
|
||||
|
||||
echo '>';
|
||||
|
||||
?></html_block>
|
||||
<paragraph>
|
||||
<text>okay</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<!DOCTYPE html>", @"<document>
|
||||
<html_block><!DOCTYPE html></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<![CDATA[
|
||||
function matchwo(a,b)
|
||||
{
|
||||
if (a < b && a < 0) then {
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
okay", @"<document>
|
||||
<html_block><![CDATA[
|
||||
function matchwo(a,b)
|
||||
{
|
||||
if (a < b && a < 0) then {
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
]]></html_block>
|
||||
<paragraph>
|
||||
<text>okay</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@" <!-- foo -->
|
||||
|
||||
<!-- foo -->", @"<document>
|
||||
<html_block> <!-- foo --></html_block>
|
||||
<code_block><!-- foo -->
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@" <div>
|
||||
|
||||
<div>", @"<document>
|
||||
<html_block> <div></html_block>
|
||||
<code_block><div>
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
<div>
|
||||
bar
|
||||
</div>", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
</paragraph>
|
||||
<html_block><div>
|
||||
bar
|
||||
</div></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<div>
|
||||
bar
|
||||
</div>
|
||||
*foo*", @"<document>
|
||||
<html_block><div>
|
||||
bar
|
||||
</div>
|
||||
*foo*</html_block>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
<a href=""bar"">
|
||||
baz", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
<softbreak />
|
||||
<html_inline><a href=""bar""></html_inline>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<div>
|
||||
|
||||
*Emphasized* text.
|
||||
|
||||
</div>", @"<document>
|
||||
<html_block><div></html_block>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>Emphasized</text>
|
||||
</emph>
|
||||
<text> text.</text>
|
||||
</paragraph>
|
||||
<html_block></div></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<div>
|
||||
*Emphasized* text.
|
||||
</div>", @"<document>
|
||||
<html_block><div>
|
||||
*Emphasized* text.
|
||||
</div></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<table>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
Hi
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>", @"<document>
|
||||
<html_block><table></html_block>
|
||||
<html_block><tr></html_block>
|
||||
<html_block><td>
|
||||
Hi
|
||||
</td></html_block>
|
||||
<html_block></tr></html_block>
|
||||
<html_block></table></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"<table>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
Hi
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>", @"<document>
|
||||
<html_block><table></html_block>
|
||||
<html_block> <tr></html_block>
|
||||
<code_block><td>
|
||||
Hi
|
||||
</td>
|
||||
</code_block>
|
||||
<html_block> </tr></html_block>
|
||||
<html_block></table></html_block>
|
||||
</document>")]
|
||||
public void Parse_HtmlBlock(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
168
Radzen.Blazor.Tests/Markdown/HtmlInlineTests.cs
Normal file
168
Radzen.Blazor.Tests/Markdown/HtmlInlineTests.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class HtmlInlineTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"<a><bab><c2c>", @"<document>
|
||||
<paragraph>
|
||||
<html_inline><a></html_inline>
|
||||
<html_inline><bab></html_inline>
|
||||
<html_inline><c2c></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<a/><b2/>", @"<document>
|
||||
<paragraph>
|
||||
<html_inline><a/></html_inline>
|
||||
<html_inline><b2/></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<a /><b2
|
||||
data=""foo"" >", @"<document>
|
||||
<paragraph>
|
||||
<html_inline><a /></html_inline>
|
||||
<html_inline><b2
|
||||
data=""foo"" ></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<a foo=""bar"" bam = 'baz <em>""</em>'
|
||||
_boolean zoop:33=zoop:33 />", @"<document>
|
||||
<paragraph>
|
||||
<html_inline><a foo=""bar"" bam = 'baz <em>""</em>'
|
||||
_boolean zoop:33=zoop:33 /></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"Foo <responsive-image src=""foo.jpg"" />", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo </text>
|
||||
<html_inline><responsive-image src=""foo.jpg"" /></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("<33> <__>", @"<document>
|
||||
<paragraph>
|
||||
<text><33> <</text>
|
||||
<text>__</text>
|
||||
<text>></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<a h*#ref=""hi"">", @"<document>
|
||||
<paragraph>
|
||||
<text><a h</text>
|
||||
<text>*</text>
|
||||
<text>#ref=""hi""></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<a href='bar'title=title>", @"<document>
|
||||
<paragraph>
|
||||
<text><a href='bar'title=title></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"</a></foo >", @"<document>
|
||||
<paragraph>
|
||||
<html_inline></a></html_inline>
|
||||
<html_inline></foo ></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"</a href=""foo"">", @"<document>
|
||||
<paragraph>
|
||||
<text></a href=""foo""></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo <!-- this is a --
|
||||
comment - with hyphens -->", @"<document>
|
||||
<paragraph>
|
||||
<text>foo </text>
|
||||
<html_inline><!-- this is a --
|
||||
comment - with hyphens --></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo <!--> foo -->
|
||||
|
||||
foo <!---> foo -->
|
||||
", @"<document>
|
||||
<paragraph>
|
||||
<text>foo </text>
|
||||
<html_inline><!--></html_inline>
|
||||
<text> foo --></text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>foo </text>
|
||||
<html_inline><!---></html_inline>
|
||||
<text> foo --></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo <?php echo $a; ?>", @"<document>
|
||||
<paragraph>
|
||||
<text>foo </text>
|
||||
<html_inline><?php echo $a; ?></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo <!ELEMENT br EMPTY>", @"<document>
|
||||
<paragraph>
|
||||
<text>foo </text>
|
||||
<html_inline><!ELEMENT br EMPTY></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo <![CDATA[>&<]]>", @"<document>
|
||||
<paragraph>
|
||||
<text>foo </text>
|
||||
<html_inline><![CDATA[>&<]]></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo <a href=""ö"">", @"<document>
|
||||
<paragraph>
|
||||
<text>foo </text>
|
||||
<html_inline><a href=""&ouml;""></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo <a href=""\*"">", @"<document>
|
||||
<paragraph>
|
||||
<text>foo </text>
|
||||
<html_inline><a href=""\*""></html_inline>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo <a href=""\"""">", @"<document>
|
||||
<paragraph>
|
||||
<text>foo <a href=""</text>
|
||||
<text>""</text>
|
||||
<text>""></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_Html(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("***foo bar***", @"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<strong>
|
||||
<text>foo bar</text>
|
||||
</strong>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("___foo bar___", @"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<strong>
|
||||
<text>foo bar</text>
|
||||
</strong>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_BoldAndItalic_ShouldNotThrowException(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
85
Radzen.Blazor.Tests/Markdown/ImageTests.cs
Normal file
85
Radzen.Blazor.Tests/Markdown/ImageTests.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class ImageTests
|
||||
{
|
||||
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"", @"<document>
|
||||
<paragraph>
|
||||
<image destination=""/url"" title=""title"">
|
||||
<text>foo</text>
|
||||
</image>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"](/url2)", @"<document>
|
||||
<paragraph>
|
||||
<image destination=""/url2"" title="""">
|
||||
<text>foo </text>
|
||||
<image destination=""/url"" title="""">
|
||||
<text>bar</text>
|
||||
</image>
|
||||
</image>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"](/url2)", @"<document>
|
||||
<paragraph>
|
||||
<image destination=""/url2"" title="""">
|
||||
<text>foo </text>
|
||||
<link destination=""/url"" title="""">
|
||||
<text>bar</text>
|
||||
</link>
|
||||
</image>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"", @"<document>
|
||||
<paragraph>
|
||||
<image destination=""train.jpg"" title="""">
|
||||
<text>foo</text>
|
||||
</image>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"My ", @"<document>
|
||||
<paragraph>
|
||||
<text>My </text>
|
||||
<image destination=""/path/to/train.jpg"" title=""title"">
|
||||
<text>foo bar</text>
|
||||
</image>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"", @"<document>
|
||||
<paragraph>
|
||||
<image destination=""url"" title="""">
|
||||
<text>foo</text>
|
||||
</image>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"", @"<document>
|
||||
<paragraph>
|
||||
<image destination=""/url"" title="""" />
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"__Applications__ ", @"<document>
|
||||
<paragraph>
|
||||
<strong>
|
||||
<text>Applications</text>
|
||||
</strong>
|
||||
<text> </text>
|
||||
<image destination=""/assets/img/macOS-drag-and-drop.png"" title="""">
|
||||
<text>macOS DMG</text>
|
||||
</image>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_BasicImages_ReturnsImageElement(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
145
Radzen.Blazor.Tests/Markdown/IndentedCodeBlockTests.cs
Normal file
145
Radzen.Blazor.Tests/Markdown/IndentedCodeBlockTests.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class IndentedCodeBlockTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@" a simple
|
||||
indented code block
|
||||
", @"<document>
|
||||
<code_block>a simple
|
||||
indented code block
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@" - foo
|
||||
|
||||
bar", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(
|
||||
@"
|
||||
1. foo
|
||||
|
||||
- bar", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@" <a/>
|
||||
*hi*
|
||||
|
||||
- one", @"<document>
|
||||
<code_block><a/>
|
||||
*hi*
|
||||
|
||||
- one
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@" chunk1
|
||||
|
||||
chunk2
|
||||
|
||||
|
||||
|
||||
chunk3", @"<document>
|
||||
<code_block>chunk1
|
||||
|
||||
chunk2
|
||||
|
||||
|
||||
|
||||
chunk3
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@" chunk1
|
||||
|
||||
chunk2", @"<document>
|
||||
<code_block>chunk1
|
||||
|
||||
chunk2
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
bar", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
<softbreak />
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@" foo
|
||||
bar", @"<document>
|
||||
<code_block>foo
|
||||
</code_block>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"# Heading
|
||||
foo
|
||||
Heading
|
||||
------
|
||||
foo
|
||||
----", @"<document>
|
||||
<heading level=""1"">
|
||||
<text>Heading</text>
|
||||
</heading>
|
||||
<code_block>foo
|
||||
</code_block>
|
||||
<heading level=""2"">
|
||||
<text>Heading</text>
|
||||
</heading>
|
||||
<code_block>foo
|
||||
</code_block>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@" foo
|
||||
bar", @"<document>
|
||||
<code_block> foo
|
||||
bar
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"
|
||||
|
||||
foo
|
||||
", @"<document>
|
||||
<code_block>foo
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@" foo ", @"<document>
|
||||
<code_block>foo
|
||||
</code_block>
|
||||
</document>")]
|
||||
public void Parse_IndentedCodeBlock(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
852
Radzen.Blazor.Tests/Markdown/LinkTests.cs
Normal file
852
Radzen.Blazor.Tests/Markdown/LinkTests.cs
Normal file
@@ -0,0 +1,852 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class LinkTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("[link](/uri \"title\")",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<link destination=""/uri"" title=""title"">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("[link](/uri)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/uri"" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("[](./target.md)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""./target.md"" title="""" />
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("[link]()", @"<document>
|
||||
<paragraph>
|
||||
<link destination="""" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("[link](<>)", @"<document>
|
||||
<paragraph>
|
||||
<link destination="""" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("[]()", @"<document>
|
||||
<paragraph>
|
||||
<link destination="""" title="""" />
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_BasicLinks_ReturnsLinkNode(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("[link](/my uri)", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>link</text>
|
||||
<text>](/my uri)</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("[link](</my uri>)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/my uri"" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_LinkDestinationWithSpaces(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[link](foo
|
||||
bar)", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>link</text>
|
||||
<text>](foo</text>
|
||||
<softbreak />
|
||||
<text>bar)</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
|
||||
public void Parse_LinkDestinationWithNewLines(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[a](<b)c>)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""b)c"" title="""">
|
||||
<text>a</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_LinkDestinationWithCloseParenthesis(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[link](<foo\>)", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>link</text>
|
||||
<text>](<foo>)</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[a](<b)c
|
||||
[a](<b)c>
|
||||
[a](<b>c)", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>a</text>
|
||||
<text>](<b)c</text>
|
||||
<softbreak />
|
||||
<text>[</text>
|
||||
<text>a</text>
|
||||
<text>](<b)c></text>
|
||||
<softbreak />
|
||||
<text>[</text>
|
||||
<text>a</text>
|
||||
<text>](</text>
|
||||
<html_inline><b></html_inline>
|
||||
<text>c)</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_LinkDestinationUnclosedPointyBracket(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[link](\(foo\))", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""(foo)"" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[link](foo\)\:)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""foo):"" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[link](foo\bar)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""foo\bar"" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_Escapes(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[link](foo(and(bar)))", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""foo(and(bar))"" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[link](foo(and(bar))", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>link</text>
|
||||
<text>](foo(and(bar))</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[link](foo\(and\(bar\))", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""foo(and(bar)"" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[link](<foo(and(bar)>)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""foo(and(bar)"" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_BallancedParenthesisInLinkDestination(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[link](#fragment)
|
||||
[link](https://example.com#fragment)
|
||||
[link](https://example.com?foo=3#frag)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""#fragment"" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
<softbreak />
|
||||
<link destination=""https://example.com#fragment"" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
<softbreak />
|
||||
<link destination=""https://example.com?foo=3#frag"" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_FragmentAndQueryStringInLinkDestination(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[link](""title"")", @"<document>
|
||||
<paragraph>
|
||||
<link destination="""title""" title="""">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_QuotesInDestination(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[link](/url ""title"")
|
||||
[link](/url 'title')
|
||||
[link](/url (title))", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/url"" title=""title"">
|
||||
<text>link</text>
|
||||
</link>
|
||||
<softbreak />
|
||||
<link destination=""/url"" title=""title"">
|
||||
<text>link</text>
|
||||
</link>
|
||||
<softbreak />
|
||||
<link destination=""/url"" title=""title"">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[link](/url ""title ""and"" title"")", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>link</text>
|
||||
<text>](/url ""title ""and"" title"")</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[link](/url 'title ""and"" title')", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/url"" title=""title "and" title"">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_Title(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[link]( /url
|
||||
""title"" )", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/url"" title=""title"">
|
||||
<text>link</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[link] (/uri)", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>link</text>
|
||||
<text>] (/uri)</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_SpacesInLink(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[link [foo [bar]]](/uri)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/uri"" title="""">
|
||||
<text>link </text>
|
||||
<text>[</text>
|
||||
<text>foo </text>
|
||||
<text>[</text>
|
||||
<text>bar</text>
|
||||
<text>]</text>
|
||||
<text>]</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[link] bar](/uri)", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>link</text>
|
||||
<text>] bar](/uri)</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[link [bar](/uri)", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>link </text>
|
||||
<link destination=""/uri"" title="""">
|
||||
<text>bar</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[link \[bar](/uri)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/uri"" title="""">
|
||||
<text>link </text>
|
||||
<text>[</text>
|
||||
<text>bar</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_BracketsInText(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[link *foo **bar** `#`*](/uri)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/uri"" title="""">
|
||||
<text>link </text>
|
||||
<emph>
|
||||
<text>foo </text>
|
||||
<strong>
|
||||
<text>bar</text>
|
||||
</strong>
|
||||
<text> </text>
|
||||
<code>#</code>
|
||||
</emph>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[](url)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""url"" title="""">
|
||||
<image destination=""img"" title="""">
|
||||
<text>alt</text>
|
||||
</image>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
|
||||
public void Parse_LinkTextIsInlineContent(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[foo [bar](/uri)](/uri)", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>foo </text>
|
||||
<link destination=""/uri"" title="""">
|
||||
<text>bar</text>
|
||||
</link>
|
||||
<text>](/uri)</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo *[bar [baz](/uri)](/uri)*](/uri)", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>foo </text>
|
||||
<emph>
|
||||
<text>[</text>
|
||||
<text>bar </text>
|
||||
<link destination=""/uri"" title="""">
|
||||
<text>baz</text>
|
||||
</link>
|
||||
<text>](/uri)</text>
|
||||
</emph>
|
||||
<text>](/uri)</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_NestedLinks(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"*[foo*](/uri)", @"<document>
|
||||
<paragraph>
|
||||
<text>*</text>
|
||||
<link destination=""/uri"" title="""">
|
||||
<text>foo</text>
|
||||
<text>*</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo *bar](baz*)", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""baz*"" title="""">
|
||||
<text>foo </text>
|
||||
<text>*</text>
|
||||
<text>bar</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"*foo [bar* baz]", @"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>foo </text>
|
||||
<text>[</text>
|
||||
<text>bar</text>
|
||||
</emph>
|
||||
<text> baz</text>
|
||||
<text>]</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo`](/uri)`", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>foo</text>
|
||||
<code>](/uri)</code>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_Precedence(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"<http://foo.bar.baz>", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""http://foo.bar.baz"" title="""">
|
||||
<text>http://foo.bar.baz</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<https://foo.bar.baz/test?q=hello&id=22&boolean>", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""https://foo.bar.baz/test?q=hello&id=22&boolean"" title="""">
|
||||
<text>https://foo.bar.baz/test?q=hello&id=22&boolean</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<irc://foo.bar:2233/baz>", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""irc://foo.bar:2233/baz"" title="""">
|
||||
<text>irc://foo.bar:2233/baz</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<MAILTO:FOO@BAR.BAZ>", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""MAILTO:FOO@BAR.BAZ"" title="""">
|
||||
<text>MAILTO:FOO@BAR.BAZ</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<https://foo.bar/baz bim>", @"<document>
|
||||
<paragraph>
|
||||
<text><https://foo.bar/baz bim></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<https://example.com/\[\>", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""https://example.com/\[\"" title="""">
|
||||
<text>https://example.com/\[\</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<foo@bar.example.com>", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""mailto:foo@bar.example.com"" title="""">
|
||||
<text>foo@bar.example.com</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<foo+special@Bar.baz-bar0.com>", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""mailto:foo+special@Bar.baz-bar0.com"" title="""">
|
||||
<text>foo+special@Bar.baz-bar0.com</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<foo\+@bar.example.com>", @"<document>
|
||||
<paragraph>
|
||||
<text><foo+@bar.example.com></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<>", @"<document>
|
||||
<paragraph>
|
||||
<text><></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"< https://foo.bar >", @"<document>
|
||||
<paragraph>
|
||||
<text>< https://foo.bar ></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<m:abc>", @"<document>
|
||||
<paragraph>
|
||||
<text><m:abc></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"<foo.bar.baz>", @"<document>
|
||||
<paragraph>
|
||||
<text><foo.bar.baz></text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"https://example.com", @"<document>
|
||||
<paragraph>
|
||||
<text>https://example.com</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"foo@bar.example.com", @"<document>
|
||||
<paragraph>
|
||||
<text>foo@bar.example.com</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_AutoLink(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"[foo]: /url ""title""
|
||||
|
||||
[foo]", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/url"" title=""title"">
|
||||
<text>foo</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@" [foo]:
|
||||
/url
|
||||
'the title'
|
||||
|
||||
[foo]", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/url"" title=""the title"">
|
||||
<text>foo</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[Foo*bar\]]:my_(url) 'title (with parens)'
|
||||
|
||||
[Foo*bar\]]", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""my_(url)"" title=""title (with parens)"">
|
||||
<text>Foo</text>
|
||||
<text>*</text>
|
||||
<text>bar</text>
|
||||
<text>]</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[Foo bar]:
|
||||
<my url>
|
||||
'title'
|
||||
|
||||
[Foo bar]", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""my url"" title=""title"">
|
||||
<text>Foo bar</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]: /url '
|
||||
title
|
||||
line1
|
||||
line2
|
||||
'
|
||||
|
||||
[foo]", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/url"" title=""
title
line1
line2
"">
|
||||
<text>foo</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]: /url 'title
|
||||
|
||||
with blank line'
|
||||
|
||||
[foo]", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>foo</text>
|
||||
<text>]: /url 'title</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>with blank line'</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>foo</text>
|
||||
<text>]</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]:
|
||||
/url
|
||||
|
||||
[foo]", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/url"" title="""">
|
||||
<text>foo</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]:
|
||||
|
||||
[foo]", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>foo</text>
|
||||
<text>]:</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>foo</text>
|
||||
<text>]</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]: <>
|
||||
|
||||
[foo]", @"<document>
|
||||
<paragraph>
|
||||
<link destination="""" title="""">
|
||||
<text>foo</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]: <bar>(baz)
|
||||
|
||||
[foo]", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>foo</text>
|
||||
<text>]: </text>
|
||||
<html_inline><bar></html_inline>
|
||||
<text>(baz)</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>foo</text>
|
||||
<text>]</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]: /url\bar\*baz ""foo\""bar\baz""
|
||||
|
||||
[foo]
|
||||
", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/url\bar*baz"" title=""foo\"bar\baz"">
|
||||
<text>foo</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]
|
||||
|
||||
[foo]: url", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""url"" title="""">
|
||||
<text>foo</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]
|
||||
|
||||
[foo]: first
|
||||
[foo]: second", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""first"" title="""">
|
||||
<text>foo</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[FOO]: /url
|
||||
|
||||
[Foo]", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/url"" title="""">
|
||||
<text>Foo</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[ΑΓΩ]: /φου
|
||||
|
||||
[αγω]", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/φου"" title="""">
|
||||
<text>αγω</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]: /url", @"<document />")]
|
||||
[InlineData(@"[
|
||||
foo
|
||||
]: /url
|
||||
bar", @"<document>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]: /url ""title"" ok", @"<document>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>foo</text>
|
||||
<text>]: /url ""title"" ok</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]: /url
|
||||
""title"" ok", @"<document>
|
||||
<paragraph>
|
||||
<text>""title"" ok</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@" [foo]: /url ""title""
|
||||
|
||||
[foo]", @"<document>
|
||||
<code_block>[foo]: /url ""title""
|
||||
</code_block>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>foo</text>
|
||||
<text>]</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"```
|
||||
[foo]: /url
|
||||
```
|
||||
|
||||
[foo]", @"<document>
|
||||
<code_block>[foo]: /url
|
||||
</code_block>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>foo</text>
|
||||
<text>]</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
[bar]: /baz
|
||||
|
||||
[bar]", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
<softbreak />
|
||||
<text>[</text>
|
||||
<text>bar</text>
|
||||
<text>]: /baz</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>[</text>
|
||||
<text>bar</text>
|
||||
<text>]</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"# [Foo]
|
||||
[foo]: /url
|
||||
> bar", @"<document>
|
||||
<heading level=""1"">
|
||||
<link destination=""/url"" title="""">
|
||||
<text>Foo</text>
|
||||
</link>
|
||||
</heading>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]: /url
|
||||
bar
|
||||
===
|
||||
[foo]", @"<document>
|
||||
<heading level=""1"">
|
||||
<text>bar</text>
|
||||
</heading>
|
||||
<paragraph>
|
||||
<link destination=""/url"" title="""">
|
||||
<text>foo</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]: /url
|
||||
===
|
||||
[foo]", @"<document>
|
||||
<paragraph>
|
||||
<text>===</text>
|
||||
<softbreak />
|
||||
<link destination=""/url"" title="""">
|
||||
<text>foo</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]: /foo-url ""foo""
|
||||
[bar]: /bar-url
|
||||
""bar""
|
||||
[baz]: /baz-url
|
||||
|
||||
[foo],
|
||||
[bar],
|
||||
[baz]", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/foo-url"" title=""foo"">
|
||||
<text>foo</text>
|
||||
</link>
|
||||
<text>,</text>
|
||||
<softbreak />
|
||||
<link destination=""/bar-url"" title=""bar"">
|
||||
<text>bar</text>
|
||||
</link>
|
||||
<text>,</text>
|
||||
<softbreak />
|
||||
<link destination=""/baz-url"" title="""">
|
||||
<text>baz</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"[foo]
|
||||
|
||||
> [foo]: /url
|
||||
", @"<document>
|
||||
<paragraph>
|
||||
<link destination=""/url"" title="""">
|
||||
<text>foo</text>
|
||||
</link>
|
||||
</paragraph>
|
||||
<block_quote />
|
||||
</document>")]
|
||||
public void Parse_LinkReference(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
880
Radzen.Blazor.Tests/Markdown/ListItemTests.cs
Normal file
880
Radzen.Blazor.Tests/Markdown/ListItemTests.cs
Normal file
@@ -0,0 +1,880 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class ListItemTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"1. A paragraph
|
||||
with two lines.
|
||||
|
||||
indented code
|
||||
|
||||
> A block quote.", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>A paragraph</text>
|
||||
<softbreak />
|
||||
<text>with two lines.</text>
|
||||
</paragraph>
|
||||
<code_block>indented code
|
||||
</code_block>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>A block quote.</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- one
|
||||
|
||||
two", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>one</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<paragraph>
|
||||
<text>two</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"- one
|
||||
|
||||
two", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>one</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>two</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@" - one
|
||||
|
||||
two", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>one</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<code_block> two
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@" - one
|
||||
|
||||
two", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>one</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>two</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@" > > 1. one
|
||||
>>
|
||||
>> two
|
||||
", @"<document>
|
||||
<block_quote>
|
||||
<block_quote>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>one</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>two</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</block_quote>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@">>- one
|
||||
>>
|
||||
> > two
|
||||
", @"<document>
|
||||
<block_quote>
|
||||
<block_quote>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>one</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<paragraph>
|
||||
<text>two</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"-one
|
||||
|
||||
2.two
|
||||
", @"<document>
|
||||
<paragraph>
|
||||
<text>-one</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>2.two</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"- foo
|
||||
|
||||
|
||||
bar", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1. foo
|
||||
|
||||
```
|
||||
bar
|
||||
```
|
||||
|
||||
baz
|
||||
|
||||
> bam", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<code_block>bar
|
||||
</code_block>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>bam</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- Foo
|
||||
|
||||
bar
|
||||
|
||||
|
||||
baz", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
</paragraph>
|
||||
<code_block>bar
|
||||
|
||||
|
||||
baz
|
||||
</code_block>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"123456789. ok", @"<document>
|
||||
<list type=""ordered"" start=""123456789"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>ok</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1234567890. not ok", @"<document>
|
||||
<paragraph>
|
||||
<text>1234567890. not ok</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"0. ok", @"<document>
|
||||
<list type=""ordered"" start=""0"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>ok</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"003. ok", @"<document>
|
||||
<list type=""ordered"" start=""3"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>ok</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"-1. not ok", @"<document>
|
||||
<paragraph>
|
||||
<text>-1. not ok</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"- foo
|
||||
|
||||
bar", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<code_block>bar
|
||||
</code_block>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@" 10. foo
|
||||
|
||||
bar", @"<document>
|
||||
<list type=""ordered"" start=""10"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<code_block>bar
|
||||
</code_block>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1. indented code
|
||||
|
||||
paragraph
|
||||
|
||||
more code", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<code_block>indented code
|
||||
</code_block>
|
||||
<paragraph>
|
||||
<text>paragraph</text>
|
||||
</paragraph>
|
||||
<code_block>more code
|
||||
</code_block>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- indented code
|
||||
|
||||
paragraph
|
||||
|
||||
more code", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<code_block>indented code
|
||||
</code_block>
|
||||
<paragraph>
|
||||
<text>paragraph</text>
|
||||
</paragraph>
|
||||
<code_block>more code
|
||||
</code_block>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1. indented code
|
||||
|
||||
paragraph
|
||||
|
||||
more code", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<code_block> indented code
|
||||
</code_block>
|
||||
<paragraph>
|
||||
<text>paragraph</text>
|
||||
</paragraph>
|
||||
<code_block>more code
|
||||
</code_block>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- foo
|
||||
|
||||
bar", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"- foo
|
||||
|
||||
bar", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"-
|
||||
foo
|
||||
-
|
||||
```
|
||||
bar
|
||||
```
|
||||
-
|
||||
baz", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<code_block>bar
|
||||
</code_block>
|
||||
</item>
|
||||
<item>
|
||||
<code_block>baz
|
||||
</code_block>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1.
|
||||
foo", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"-
|
||||
foo", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"-
|
||||
|
||||
foo", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item />
|
||||
</list>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"- foo
|
||||
-
|
||||
- bar", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item />
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- foo
|
||||
-
|
||||
- bar", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item />
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1. foo
|
||||
2.
|
||||
3. bar", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item />
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"*", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item />
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"foo
|
||||
*
|
||||
|
||||
foo
|
||||
1.", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<softbreak />
|
||||
<text>*</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<softbreak />
|
||||
<text>1.</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@" 1. A paragraph
|
||||
with two lines.
|
||||
|
||||
indented code
|
||||
|
||||
> A block quote.", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>A paragraph</text>
|
||||
<softbreak />
|
||||
<text>with two lines.</text>
|
||||
</paragraph>
|
||||
<code_block>indented code
|
||||
</code_block>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>A block quote.</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@" 1. A paragraph
|
||||
with two lines.
|
||||
|
||||
indented code
|
||||
|
||||
> A block quote.", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>A paragraph</text>
|
||||
<softbreak />
|
||||
<text>with two lines.</text>
|
||||
</paragraph>
|
||||
<code_block>indented code
|
||||
</code_block>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>A block quote.</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@" 1. A paragraph
|
||||
with two lines.
|
||||
|
||||
indented code
|
||||
|
||||
> A block quote.", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>A paragraph</text>
|
||||
<softbreak />
|
||||
<text>with two lines.</text>
|
||||
</paragraph>
|
||||
<code_block>indented code
|
||||
</code_block>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>A block quote.</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@" 1. A paragraph
|
||||
with two lines.
|
||||
|
||||
indented code
|
||||
|
||||
> A block quote.", @"<document>
|
||||
<code_block>1. A paragraph
|
||||
with two lines.
|
||||
|
||||
indented code
|
||||
|
||||
> A block quote.
|
||||
</code_block>
|
||||
</document>")]
|
||||
public void Parse_ListItem(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@" 1. A paragraph
|
||||
with two lines.
|
||||
|
||||
indented code
|
||||
|
||||
> A block quote.", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>A paragraph</text>
|
||||
<softbreak />
|
||||
<text>with two lines.</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>indented code</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<code_block> > A block quote.
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@" 1. A paragraph
|
||||
with two lines.", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>A paragraph</text>
|
||||
<softbreak />
|
||||
<text>with two lines.</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"> 1. > Blockquote
|
||||
continued here.", @"<document>
|
||||
<block_quote>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>Blockquote</text>
|
||||
<softbreak />
|
||||
<text>continued here.</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</item>
|
||||
</list>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"> 1. > Blockquote
|
||||
> continued here.", @"<document>
|
||||
<block_quote>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>Blockquote</text>
|
||||
<softbreak />
|
||||
<text>continued here.</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</item>
|
||||
</list>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
public void Parse_ListItem_WithLazyContinuation(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"- foo
|
||||
- bar
|
||||
- baz
|
||||
- boo", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>boo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- foo
|
||||
- bar
|
||||
- baz
|
||||
- boo", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>boo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1. foo
|
||||
1. bar
|
||||
1. baz
|
||||
1. boo", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>boo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"
|
||||
1. foo
|
||||
1. bar
|
||||
1. baz
|
||||
1. boo", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>boo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"10) foo
|
||||
- bar", @"<document>
|
||||
<list type=""ordered"" start=""10"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"
|
||||
10) foo
|
||||
- bar", @"<document>
|
||||
<list type=""ordered"" start=""10"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- - foo", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1. - foo", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1. 1. foo", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- 1. foo", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1. - 2. foo", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<list type=""ordered"" start=""2"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- # Foo
|
||||
- Bar
|
||||
---
|
||||
baz", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<heading level=""1"">
|
||||
<text>Foo</text>
|
||||
</heading>
|
||||
</item>
|
||||
<item>
|
||||
<heading level=""2"">
|
||||
<text>Bar</text>
|
||||
</heading>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
public void Parse_ListItem_WithNesting(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
693
Radzen.Blazor.Tests/Markdown/ListTests.cs
Normal file
693
Radzen.Blazor.Tests/Markdown/ListTests.cs
Normal file
@@ -0,0 +1,693 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class ListTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"- foo
|
||||
- bar
|
||||
+ baz", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1. foo
|
||||
2. bar
|
||||
3) baz", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<list type=""ordered"" start=""3"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
- bar
|
||||
- baz", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
public void Parse_List(string markdown, string expected)
|
||||
{
|
||||
var actual = ToXml(markdown);
|
||||
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"The number of windows in my house is
|
||||
14. The number of doors is 6.", @"<document>
|
||||
<paragraph>
|
||||
<text>The number of windows in my house is</text>
|
||||
<softbreak />
|
||||
<text>14. The number of doors is 6.</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"The number of windows in my house is
|
||||
1. The number of doors is 6.", @"<document>
|
||||
<paragraph>
|
||||
<text>The number of windows in my house is</text>
|
||||
</paragraph>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>The number of doors is 6.</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
public void Parse_OnlyNumberedListsThatStartWithOneCanInterruptParagraphs(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"- foo
|
||||
|
||||
- bar
|
||||
|
||||
|
||||
- baz", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- foo
|
||||
- bar
|
||||
- baz
|
||||
|
||||
|
||||
bim
|
||||
", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>bim</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- a
|
||||
- b
|
||||
|
||||
- c", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>c</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"* a
|
||||
*
|
||||
|
||||
* c", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item />
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>c</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- a
|
||||
- b
|
||||
|
||||
c
|
||||
- d", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>c</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>d</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- a
|
||||
- b
|
||||
|
||||
[ref]: /url
|
||||
- d", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>d</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- a
|
||||
- ```
|
||||
b
|
||||
|
||||
|
||||
```
|
||||
- c", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<code_block>b
|
||||
|
||||
|
||||
</code_block>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>c</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- a
|
||||
- b
|
||||
|
||||
c
|
||||
- d", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>c</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>d</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"* a
|
||||
> b
|
||||
>
|
||||
* c", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>c</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- a
|
||||
> b
|
||||
```
|
||||
c
|
||||
```
|
||||
- d
|
||||
", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
<code_block>c
|
||||
</code_block>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>d</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- a", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- a
|
||||
- b", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1. ```
|
||||
foo
|
||||
```
|
||||
|
||||
bar", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<code_block>foo
|
||||
</code_block>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"* foo
|
||||
* bar
|
||||
|
||||
baz", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- a
|
||||
- b
|
||||
- c
|
||||
|
||||
- d
|
||||
- e
|
||||
- f", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>c</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>d</text>
|
||||
</paragraph>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>e</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>f</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
public void Parse_TightAndLooseLists(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"- foo
|
||||
- bar
|
||||
|
||||
<!-- -->
|
||||
|
||||
- baz
|
||||
- bim", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<html_block><!-- --></html_block>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bim</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- foo
|
||||
|
||||
notcode
|
||||
|
||||
- foo
|
||||
|
||||
<!-- -->
|
||||
|
||||
code", @"<document>
|
||||
<list type=""bullet"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>notcode</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<html_block><!-- --></html_block>
|
||||
<code_block>code
|
||||
</code_block>
|
||||
</document>")]
|
||||
public void Parse_List_Separators(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"- a
|
||||
- b
|
||||
- c
|
||||
- d
|
||||
- e
|
||||
- f
|
||||
- g", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>c</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>d</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>e</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>f</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>g</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1. a
|
||||
|
||||
2. b
|
||||
|
||||
3. c
|
||||
", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>c</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- a
|
||||
- b
|
||||
- c
|
||||
- d
|
||||
- e", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>c</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>d</text>
|
||||
<softbreak />
|
||||
<text>- e</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"1. a
|
||||
|
||||
2. b
|
||||
|
||||
3. c", @"<document>
|
||||
<list type=""ordered"" start=""1"" tight=""false"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<code_block>3. c
|
||||
</code_block>
|
||||
</document>")]
|
||||
public void Parse_List_Identation(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_BasicNestedLists()
|
||||
{
|
||||
Assert.Equal(@"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>b</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>c</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>", ToXml(@"
|
||||
- a
|
||||
- b
|
||||
- c"));
|
||||
}
|
||||
}
|
||||
109
Radzen.Blazor.Tests/Markdown/ParagraphTests.cs
Normal file
109
Radzen.Blazor.Tests/Markdown/ParagraphTests.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class ParagraphTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
[Fact]
|
||||
public void Parse_BasicParagraph()
|
||||
{
|
||||
Assert.Equal(@"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</document>", ToXml(@"foo"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"aaa
|
||||
|
||||
bbb", @"<document>
|
||||
<paragraph>
|
||||
<text>aaa</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>bbb</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"aaa
|
||||
bbb
|
||||
|
||||
ccc
|
||||
ddd", @"<document>
|
||||
<paragraph>
|
||||
<text>aaa</text>
|
||||
<softbreak />
|
||||
<text>bbb</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>ccc</text>
|
||||
<softbreak />
|
||||
<text>ddd</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"aaa
|
||||
|
||||
|
||||
bbb
|
||||
", @"<document>
|
||||
<paragraph>
|
||||
<text>aaa</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>bbb</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@" aaa
|
||||
bbb", @"<document>
|
||||
<paragraph>
|
||||
<text>aaa</text>
|
||||
<softbreak />
|
||||
<text>bbb</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"aaa
|
||||
bbb
|
||||
ccc", @"<document>
|
||||
<paragraph>
|
||||
<text>aaa</text>
|
||||
<softbreak />
|
||||
<text>bbb</text>
|
||||
<softbreak />
|
||||
<text>ccc</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@" aaa
|
||||
bbb", @"<document>
|
||||
<paragraph>
|
||||
<text>aaa</text>
|
||||
<softbreak />
|
||||
<text>bbb</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@" aaa
|
||||
bbb", @"<document>
|
||||
<code_block>aaa
|
||||
</code_block>
|
||||
<paragraph>
|
||||
<text>bbb</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"aaa
|
||||
bbb ", @"<document>
|
||||
<paragraph>
|
||||
<text>aaa</text>
|
||||
<linebreak />
|
||||
<text>bbb</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_Paragraph(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
41
Radzen.Blazor.Tests/Markdown/SoftLineBreakTests.cs
Normal file
41
Radzen.Blazor.Tests/Markdown/SoftLineBreakTests.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class SoftLineBreakTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"foo
|
||||
baz", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_SoftLineBreak(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"foo
|
||||
baz", @"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<softbreak />
|
||||
<text>baz</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_SoftLine_RemovesSpacesAtEndAndStartOfLine(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
135
Radzen.Blazor.Tests/Markdown/StrongTests.cs
Normal file
135
Radzen.Blazor.Tests/Markdown/StrongTests.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class StrongTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("**foo bar**",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<strong>
|
||||
<text>foo bar</text>
|
||||
</strong>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("** foo bar**",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>**</text>
|
||||
<text> foo bar</text>
|
||||
<text>**</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("a**\"foo\"**",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
<text>**</text>
|
||||
<text>""foo""</text>
|
||||
<text>**</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("foo**bar**",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<strong>
|
||||
<text>bar</text>
|
||||
</strong>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("__foo bar__",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<strong>
|
||||
<text>foo bar</text>
|
||||
</strong>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("__ foo bar__",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>__</text>
|
||||
<text> foo bar</text>
|
||||
<text>__</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("__\nfoo bar__",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>__</text>
|
||||
<softbreak />
|
||||
<text>foo bar</text>
|
||||
<text>__</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("a__\"foo\"__",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>a</text>
|
||||
<text>__</text>
|
||||
<text>""foo""</text>
|
||||
<text>__</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("foo__bar__",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
<text>__</text>
|
||||
<text>bar</text>
|
||||
<text>__</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("5__6__78",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>5</text>
|
||||
<text>__</text>
|
||||
<text>6</text>
|
||||
<text>__</text>
|
||||
<text>78</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("пристаням__стремятся__",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>пристаням</text>
|
||||
<text>__</text>
|
||||
<text>стремятся</text>
|
||||
<text>__</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("__foo, __bar__, baz__",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<strong>
|
||||
<text>foo, </text>
|
||||
<strong>
|
||||
<text>bar</text>
|
||||
</strong>
|
||||
<text>, baz</text>
|
||||
</strong>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData("foo-__(bar)__",
|
||||
@"<document>
|
||||
<paragraph>
|
||||
<text>foo-</text>
|
||||
<strong>
|
||||
<text>(bar)</text>
|
||||
</strong>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_StrongEmphasisRules_AdheresToCommonMarkSpec(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
414
Radzen.Blazor.Tests/Markdown/TableTests.cs
Normal file
414
Radzen.Blazor.Tests/Markdown/TableTests.cs
Normal file
@@ -0,0 +1,414 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class TableTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_BasicTable()
|
||||
{
|
||||
Assert.Equal(
|
||||
@"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>foo</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>bar</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell>
|
||||
<text>baz</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>bim</text>
|
||||
</cell>
|
||||
</row>
|
||||
</table>
|
||||
</document>",
|
||||
ToXml(@"
|
||||
foo|bar
|
||||
--|--
|
||||
baz|bim"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"| foo | bar |
|
||||
| --- | --- |
|
||||
| baz | bim |", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>foo</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>bar</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell>
|
||||
<text>baz</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>bim</text>
|
||||
</cell>
|
||||
</row>
|
||||
</table>
|
||||
</document>")]
|
||||
[InlineData(@"| f\|oo |
|
||||
| ------ |
|
||||
| b `\|` az |
|
||||
| b **\|** im |", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>f|oo</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell>
|
||||
<text>b </text>
|
||||
<code>|</code>
|
||||
<text> az</text>
|
||||
</cell>
|
||||
</row>
|
||||
<row>
|
||||
<cell>
|
||||
<text>b </text>
|
||||
<strong>
|
||||
<text>|</text>
|
||||
</strong>
|
||||
<text> im</text>
|
||||
</cell>
|
||||
</row>
|
||||
</table>
|
||||
</document>")]
|
||||
[InlineData(@"| abc | defghi |
|
||||
:-: | -----------:
|
||||
bar | baz", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell align=""center"">
|
||||
<text>abc</text>
|
||||
</cell>
|
||||
<cell align=""right"">
|
||||
<text>defghi</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell align=""center"">
|
||||
<text>bar</text>
|
||||
</cell>
|
||||
<cell align=""right"">
|
||||
<text>baz</text>
|
||||
</cell>
|
||||
</row>
|
||||
</table>
|
||||
</document>")]
|
||||
|
||||
|
||||
[InlineData(@"| abc | def |
|
||||
| --- | --- |
|
||||
bar", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>abc</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>def</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell>
|
||||
<text>bar</text>
|
||||
</cell>
|
||||
<cell />
|
||||
</row>
|
||||
</table>
|
||||
</document>")]
|
||||
[InlineData(@"| abc | def |
|
||||
| --- | --- |
|
||||
c:\\foo", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>abc</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>def</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell>
|
||||
<text>c:</text>
|
||||
<text>\</text>
|
||||
<text>foo</text>
|
||||
</cell>
|
||||
<cell />
|
||||
</row>
|
||||
</table>
|
||||
</document>")]
|
||||
public void Parse_Table(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"| abc | def |
|
||||
| --- | --- |
|
||||
| bar | baz |
|
||||
|
||||
boo", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>abc</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>def</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell>
|
||||
<text>bar</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>baz</text>
|
||||
</cell>
|
||||
</row>
|
||||
</table>
|
||||
<paragraph>
|
||||
<text>boo</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"| foo |
|
||||
| --- |
|
||||
# bar", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>foo</text>
|
||||
</cell>
|
||||
</header>
|
||||
</table>
|
||||
<heading level=""1"">
|
||||
<text>bar</text>
|
||||
</heading>
|
||||
</document>")]
|
||||
[InlineData(@"| foo |
|
||||
| --- |
|
||||
- bar", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>foo</text>
|
||||
</cell>
|
||||
</header>
|
||||
</table>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"| foo |
|
||||
| --- |
|
||||
1. bar", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>foo</text>
|
||||
</cell>
|
||||
</header>
|
||||
</table>
|
||||
<list type=""ordered"" start=""1"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"| abc | def |
|
||||
| --- | --- |
|
||||
| bar | baz |
|
||||
> bar", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>abc</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>def</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell>
|
||||
<text>bar</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>baz</text>
|
||||
</cell>
|
||||
</row>
|
||||
</table>
|
||||
<block_quote>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</block_quote>
|
||||
</document>")]
|
||||
[InlineData(@"| abc | def |
|
||||
| --- | --- |
|
||||
| bar | baz |
|
||||
bar", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>abc</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>def</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell>
|
||||
<text>bar</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>baz</text>
|
||||
</cell>
|
||||
</row>
|
||||
</table>
|
||||
<code_block>bar
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"| abc | def |
|
||||
| --- | --- |
|
||||
| bar | baz |
|
||||
```
|
||||
bar
|
||||
```", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>abc</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>def</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell>
|
||||
<text>bar</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>baz</text>
|
||||
</cell>
|
||||
</row>
|
||||
</table>
|
||||
<code_block>bar
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"| abc | def |
|
||||
| --- | --- |
|
||||
| bar | baz |
|
||||
<div>
|
||||
</div>", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>abc</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>def</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell>
|
||||
<text>bar</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>baz</text>
|
||||
</cell>
|
||||
</row>
|
||||
</table>
|
||||
<html_block><div>
|
||||
</div></html_block>
|
||||
</document>")]
|
||||
[InlineData(@"| abc | def |
|
||||
| --- | --- |
|
||||
| bar | baz |
|
||||
---", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>abc</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>def</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell>
|
||||
<text>bar</text>
|
||||
</cell>
|
||||
<cell>
|
||||
<text>baz</text>
|
||||
</cell>
|
||||
</row>
|
||||
</table>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
public void Parse_Table_AnyBlockOrEmptyLineBreaksTable(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"| abc | def |
|
||||
| --- |", @"<document>
|
||||
<paragraph>
|
||||
<text>| abc | def |</text>
|
||||
<softbreak />
|
||||
<text>| --- |</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
public void Parse_Table_ChecksHeaderAndDelimiter(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"| abc |
|
||||
| --- |
|
||||
| bar | baz |", @"<document>
|
||||
<table>
|
||||
<header>
|
||||
<cell>
|
||||
<text>abc</text>
|
||||
</cell>
|
||||
</header>
|
||||
<row>
|
||||
<cell>
|
||||
<text>bar</text>
|
||||
</cell>
|
||||
</row>
|
||||
</table>
|
||||
</document>")]
|
||||
public void Parse_Table_IgnoresExtraCells(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
162
Radzen.Blazor.Tests/Markdown/ThematicBreakTests.cs
Normal file
162
Radzen.Blazor.Tests/Markdown/ThematicBreakTests.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class ThematicBreakTests
|
||||
{
|
||||
private static string ToXml(string markdown)
|
||||
{
|
||||
var document = MarkdownParser.Parse(markdown);
|
||||
|
||||
return XmlVisitor.ToXml(document);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"***
|
||||
---
|
||||
___", @"<document>
|
||||
<thematic_break />
|
||||
<thematic_break />
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"--
|
||||
**
|
||||
__", @"<document>
|
||||
<paragraph>
|
||||
<text>--</text>
|
||||
<softbreak />
|
||||
<text>**</text>
|
||||
<softbreak />
|
||||
<text>__</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@" ***
|
||||
***
|
||||
***", @"<document>
|
||||
<thematic_break />
|
||||
<thematic_break />
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@" ***", @"<document>
|
||||
<code_block>***
|
||||
</code_block>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
***", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
<softbreak />
|
||||
<text>***</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"_____________________________________", @"<document>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"- - -", @"<document>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@" ** * ** * ** * **", @"<document>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"- - - -", @"<document>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"- - - - ", @"<document>
|
||||
<thematic_break />
|
||||
</document>")]
|
||||
[InlineData(@"_ _ _ _ a
|
||||
|
||||
a------
|
||||
|
||||
---a---", @"<document>
|
||||
<paragraph>
|
||||
<text>_</text>
|
||||
<text> </text>
|
||||
<text>_</text>
|
||||
<text> </text>
|
||||
<text>_</text>
|
||||
<text> </text>
|
||||
<text>_</text>
|
||||
<text> a</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>a------</text>
|
||||
</paragraph>
|
||||
<paragraph>
|
||||
<text>---a---</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@" *-*", @"<document>
|
||||
<paragraph>
|
||||
<emph>
|
||||
<text>-</text>
|
||||
</emph>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"- foo
|
||||
***
|
||||
- bar", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<thematic_break />
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"Foo
|
||||
***
|
||||
bar", @"<document>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
</paragraph>
|
||||
<thematic_break />
|
||||
<paragraph>
|
||||
<text>bar</text>
|
||||
</paragraph>
|
||||
</document>")]
|
||||
[InlineData(@"* Foo
|
||||
* * *
|
||||
* Bar", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
<thematic_break />
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>Bar</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
[InlineData(@"- Foo
|
||||
- * * *", @"<document>
|
||||
<list type=""bullet"" tight=""true"">
|
||||
<item>
|
||||
<paragraph>
|
||||
<text>Foo</text>
|
||||
</paragraph>
|
||||
</item>
|
||||
<item>
|
||||
<thematic_break />
|
||||
</item>
|
||||
</list>
|
||||
</document>")]
|
||||
public void Parse_ThematicBreak(string markdown, string expected)
|
||||
{
|
||||
Assert.Equal(expected, ToXml(markdown));
|
||||
}
|
||||
}
|
||||
210
Radzen.Blazor.Tests/Markdown/XmlVisitor.cs
Normal file
210
Radzen.Blazor.Tests/Markdown/XmlVisitor.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
namespace Radzen.Blazor.Markdown.Tests;
|
||||
|
||||
public class XmlVisitor : NodeVisitorBase, IDisposable
|
||||
{
|
||||
private readonly XmlWriter writer;
|
||||
|
||||
private XmlVisitor(StringBuilder xml)
|
||||
{
|
||||
writer = XmlWriter.Create(xml, new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true, IndentChars = " ", });
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
writer.Dispose();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
writer.Close();
|
||||
}
|
||||
|
||||
public static string ToXml(Document document)
|
||||
{
|
||||
var xml = new StringBuilder();
|
||||
|
||||
using var visitor = new XmlVisitor(xml);
|
||||
|
||||
document.Accept(visitor);
|
||||
|
||||
visitor.Close();
|
||||
|
||||
return xml.ToString()!;
|
||||
}
|
||||
|
||||
public override void VisitBlockQuote(BlockQuote blockQuote)
|
||||
{
|
||||
writer.WriteStartElement("block_quote");
|
||||
base.VisitBlockQuote(blockQuote);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitDocument(Document document)
|
||||
{
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteStartElement("document");
|
||||
base.VisitDocument(document);
|
||||
writer.WriteEndElement();
|
||||
writer.WriteEndDocument();
|
||||
}
|
||||
|
||||
public override void VisitHeading(Heading heading)
|
||||
{
|
||||
writer.WriteStartElement($"heading");
|
||||
writer.WriteAttributeString("level", heading.Level.ToString());
|
||||
base.VisitHeading(heading);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitListItem(ListItem listItem)
|
||||
{
|
||||
writer.WriteStartElement("item");
|
||||
base.VisitListItem(listItem);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitParagraph(Paragraph paragraph)
|
||||
{
|
||||
writer.WriteStartElement("paragraph");
|
||||
base.VisitParagraph(paragraph);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitUnorderedList(UnorderedList unorderedList)
|
||||
{
|
||||
writer.WriteStartElement("list");
|
||||
writer.WriteAttributeString("type", "bullet");
|
||||
writer.WriteAttributeString("tight", unorderedList.Tight.ToString().ToLowerInvariant());
|
||||
base.VisitUnorderedList(unorderedList);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitText(Text text)
|
||||
{
|
||||
writer.WriteElementString("text", text.Value);
|
||||
}
|
||||
|
||||
public override void VisitOrderedList(OrderedList orderedList)
|
||||
{
|
||||
writer.WriteStartElement("list");
|
||||
writer.WriteAttributeString("type", "ordered");
|
||||
writer.WriteAttributeString("start", orderedList.Start.ToString());
|
||||
writer.WriteAttributeString("tight", orderedList.Tight.ToString().ToLowerInvariant());
|
||||
base.VisitOrderedList(orderedList);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitLink(Link link)
|
||||
{
|
||||
writer.WriteStartElement("link");
|
||||
writer.WriteAttributeString("destination", link.Destination);
|
||||
writer.WriteAttributeString("title", link.Title);
|
||||
base.VisitLink(link);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitImage(Image image)
|
||||
{
|
||||
writer.WriteStartElement("image");
|
||||
writer.WriteAttributeString("destination", image.Destination);
|
||||
writer.WriteAttributeString("title", image.Title);
|
||||
base.VisitImage(image);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitEmphasis(Emphasis emphasis)
|
||||
{
|
||||
writer.WriteStartElement("emph");
|
||||
base.VisitEmphasis(emphasis);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitStrong(Strong strong)
|
||||
{
|
||||
writer.WriteStartElement("strong");
|
||||
base.VisitStrong(strong);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitCode(Code code)
|
||||
{
|
||||
writer.WriteElementString("code", code.Value);
|
||||
}
|
||||
|
||||
public override void VisitHtmlInline(HtmlInline html)
|
||||
{
|
||||
writer.WriteElementString("html_inline", html.Value);
|
||||
}
|
||||
|
||||
public override void VisitLineBreak(LineBreak lineBreak)
|
||||
{
|
||||
writer.WriteElementString("linebreak", string.Empty);
|
||||
}
|
||||
|
||||
public override void VisitSoftLineBreak(SoftLineBreak softLineBreak)
|
||||
{
|
||||
writer.WriteElementString("softbreak", string.Empty);
|
||||
}
|
||||
|
||||
public override void VisitThematicBreak(ThematicBreak thematicBreak)
|
||||
{
|
||||
writer.WriteElementString("thematic_break", string.Empty);
|
||||
}
|
||||
|
||||
public override void VisitIndentedCodeBlock(IndentedCodeBlock codeBlock)
|
||||
{
|
||||
writer.WriteElementString("code_block", codeBlock.Value);
|
||||
}
|
||||
|
||||
public override void VisitFencedCodeBlock(FencedCodeBlock fencedCodeBlock)
|
||||
{
|
||||
writer.WriteStartElement("code_block");
|
||||
if (!string.IsNullOrEmpty(fencedCodeBlock.Info))
|
||||
{
|
||||
writer.WriteAttributeString("info", fencedCodeBlock.Info);
|
||||
}
|
||||
writer.WriteString(fencedCodeBlock.Value);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitHtmlBlock(HtmlBlock htmlBlock)
|
||||
{
|
||||
writer.WriteElementString("html_block", htmlBlock.Value);
|
||||
}
|
||||
|
||||
public override void VisitTable(Table table)
|
||||
{
|
||||
writer.WriteStartElement("table");
|
||||
base.VisitTable(table);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitTableHeaderRow(TableHeaderRow header)
|
||||
{
|
||||
writer.WriteStartElement("header");
|
||||
base.VisitTableHeaderRow(header);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitTableRow(TableRow row)
|
||||
{
|
||||
writer.WriteStartElement("row");
|
||||
base.VisitTableRow(row);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
|
||||
public override void VisitTableCell(TableCell cell)
|
||||
{
|
||||
writer.WriteStartElement("cell");
|
||||
if (cell.Alignment != TableCellAlignment.None)
|
||||
{
|
||||
writer.WriteAttributeString("align", cell.Alignment.ToString().ToLowerInvariant());
|
||||
}
|
||||
base.VisitTableCell(cell);
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
}
|
||||
@@ -119,12 +119,12 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenMask>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""on""", component.Markup);
|
||||
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
|
||||
@@ -135,7 +135,7 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
|
||||
|
||||
component.Instance.DefaultAutoCompleteAttribute = "autocomplete-custom";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AutoComplete, false));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""autocomplete-custom""", component.Markup);
|
||||
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
|
||||
@@ -148,22 +148,22 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenMask>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.On));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Off));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.AdditionalName));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.AdditionalName.GetAutoCompleteValue()}""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Email));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.Email.GetAutoCompleteValue()}""", component.Markup);
|
||||
|
||||
65
Radzen.Blazor.Tests/MediaQueryTests.cs
Normal file
65
Radzen.Blazor.Tests/MediaQueryTests.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class MediaQueryTests
|
||||
{
|
||||
[Fact]
|
||||
public void MediaQuery_Renders()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenMediaQuery>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Query, "(max-width: 768px)");
|
||||
});
|
||||
|
||||
Assert.NotNull(component.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MediaQuery_HasQueryParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var query = "(max-width: 1024px)";
|
||||
var component = ctx.RenderComponent<RadzenMediaQuery>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Query, query);
|
||||
});
|
||||
|
||||
Assert.Equal(query, component.Instance.Query);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MediaQuery_InvokesChangeCallback()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
bool changeInvoked = false;
|
||||
bool matchResult = false;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenMediaQuery>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Query, "(max-width: 768px)");
|
||||
parameters.Add(p => p.Change, EventCallback.Factory.Create<bool>(this, (matches) =>
|
||||
{
|
||||
changeInvoked = true;
|
||||
matchResult = matches;
|
||||
}));
|
||||
});
|
||||
|
||||
// Invoke the JSInvokable method directly
|
||||
component.Instance.OnChange(true);
|
||||
|
||||
Assert.True(changeInvoked);
|
||||
Assert.True(matchResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
58
Radzen.Blazor.Tests/MenuTests.cs
Normal file
58
Radzen.Blazor.Tests/MenuTests.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class MenuTests
|
||||
{
|
||||
[Fact]
|
||||
public void Menu_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenMenu>();
|
||||
|
||||
Assert.Contains(@"rz-menu", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Menu_Renders_Responsive_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Responsive, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-menu-closed", component.Markup);
|
||||
Assert.Contains("rz-menu-toggle", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Menu_Renders_Responsive_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Responsive, false);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-menu-toggle", component.Markup);
|
||||
Assert.DoesNotContain("rz-menu-closed", component.Markup);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Menu_Renders_CustomToggleAriaLabel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Responsive, true);
|
||||
parameters.Add(p => p.ToggleAriaLabel, "Open navigation");
|
||||
});
|
||||
|
||||
Assert.Contains("Open navigation", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public FieldIdentifier FieldIdentifier => throw new System.NotImplementedException();
|
||||
public FieldIdentifier FieldIdentifier { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
public object GetValue()
|
||||
{
|
||||
@@ -28,6 +28,10 @@ namespace Radzen.Blazor.Tests
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
public bool Visible { get; set; }
|
||||
public IFormFieldContext FormFieldContext => null;
|
||||
|
||||
public object Value { get; set; }
|
||||
}
|
||||
|
||||
@@ -60,6 +64,19 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.False(component.Instance.Validate(null));
|
||||
}
|
||||
[Fact]
|
||||
public void Returns_True_If_Value_Is_Null_And_AllowNull_Is_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenNumericRangeValidatorTestDouble>();
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Min, 0).Add(p => p.Max, 10).Add(p => p.AllowNull, true));
|
||||
});
|
||||
|
||||
Assert.True(component.Instance.Validate(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Returns_False_If_Value_Overflows()
|
||||
@@ -156,4 +173,4 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.False(component.Instance.Validate(DateTime.Now));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,12 +216,12 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<double>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""on""", component.Markup);
|
||||
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
|
||||
@@ -232,7 +232,7 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.DoesNotContain(@$"aria-autocomplete", component.Markup);
|
||||
|
||||
component.Instance.DefaultAutoCompleteAttribute = "autocomplete-custom";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.AutoComplete, false));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""autocomplete-custom""", component.Markup);
|
||||
Assert.Contains(@$"aria-autocomplete=""none""", component.Markup);
|
||||
@@ -245,22 +245,22 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<double>>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.On));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Off));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.BdayMonth));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.BdayMonth.GetAutoCompleteValue()}""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.BdayYear));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.BdayYear.GetAutoCompleteValue()}""", component.Markup);
|
||||
@@ -463,10 +463,11 @@ namespace Radzen.Blazor.Tests
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var value = new Dollars(11m);
|
||||
Dollars? ConvertFunc(string s) => decimal.TryParse(s, out var val) ? new Dollars(val) : null;
|
||||
Dollars? ConvertFunc(string s) => decimal.TryParse(s, System.Globalization.CultureInfo.InvariantCulture, out var val) ? new Dollars(val) : null;
|
||||
var component = ctx.RenderComponent<RadzenNumeric<Dollars?>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars?>.ConvertValue), (Func<string, Dollars?>)ConvertFunc),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars?>.Value), value)
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars?>.Value), value),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.InvariantCulture)
|
||||
);
|
||||
|
||||
component.Render();
|
||||
@@ -494,7 +495,101 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"{valueToTest.ToString(format)}\"", component.Markup);
|
||||
Assert.Contains($" value=\"{valueToTest.ToString(format, System.Globalization.CultureInfo.CurrentCulture)}\"", component.Markup);
|
||||
}
|
||||
[Fact]
|
||||
public void Numeric_Supports_TypeConverterWithCulture()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var valueToTest = new Dollars(100.234m);
|
||||
string format = "0.00";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<Dollars>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Value), valueToTest),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.InvariantCulture)
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"{valueToTest.ToString(format, System.Globalization.CultureInfo.InvariantCulture)}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Supports_EmptyString()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var valueToTest = "";
|
||||
string format = "0.00";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<string>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Value), valueToTest),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.InvariantCulture)
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"0.00\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Supports_ValueString()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var valueToTest = "12.50";
|
||||
string format = "0.00";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<string>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Value), valueToTest),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.InvariantCulture)
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"{valueToTest}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Supports_ValueStringEsCLCulture()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var valueToTest = "12,50";
|
||||
string format = "0.00";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<string>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Value), valueToTest),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.GetCultureInfo("es-CL"))
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"{valueToTest}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Numeric_Supports_ValueStringEnUSCulture()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var valueToTest = "12.50";
|
||||
string format = "0.00";
|
||||
|
||||
var component = ctx.RenderComponent<RadzenNumeric<string>>(
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Format), format),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<string>.Value), valueToTest),
|
||||
ComponentParameter.CreateParameter(nameof(RadzenNumeric<Dollars>.Culture), System.Globalization.CultureInfo.GetCultureInfo("en-US"))
|
||||
);
|
||||
|
||||
component.Render();
|
||||
|
||||
Assert.Contains($" value=\"{valueToTest}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -517,10 +612,10 @@ namespace Radzen.Blazor.Tests
|
||||
});
|
||||
});
|
||||
|
||||
component.Find("input").Change("13.53");
|
||||
component.Find("input").Change(13.53);
|
||||
|
||||
var maxDollars = new Dollars(2);
|
||||
Assert.Contains($" value=\"{maxDollars.ToString()}\"", component.Markup);
|
||||
var maxDollars = new Dollars(maxValue);
|
||||
Assert.Contains($" value=\"{maxDollars}\"", component.Markup);
|
||||
Assert.Equal(component.Instance.Value, maxDollars);
|
||||
}
|
||||
|
||||
|
||||
@@ -109,5 +109,51 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.Contains(@$"rz-density-compact", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RadzenPager_First_And_Prev_Buttons_Are_Disabled_When_On_The_First_Page()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPager>(parameters => {
|
||||
parameters.Add<int>(p => p.PageSize, 10);
|
||||
parameters.Add<int>(p => p.Count, 100);
|
||||
parameters.Add<bool>(p => p.ShowPagingSummary, true);
|
||||
});
|
||||
|
||||
await component.Instance.GoToPage(0);
|
||||
component.Render();
|
||||
|
||||
var firstPageButton = component.Find("a.rz-pager-first");
|
||||
Assert.True(firstPageButton.HasAttribute("disabled"));
|
||||
|
||||
var prevPageButton = component.Find("a.rz-pager-prev");
|
||||
Assert.True(prevPageButton.HasAttribute("disabled"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RadzenPager_Last_And_Next_Buttons_Are_Disabled_When_On_The_Last_Page()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPager>(parameters => {
|
||||
parameters.Add<int>(p => p.PageSize, 10);
|
||||
parameters.Add<int>(p => p.Count, 100);
|
||||
parameters.Add<bool>(p => p.ShowPagingSummary, true);
|
||||
});
|
||||
|
||||
await component.Instance.GoToPage(9);
|
||||
component.Render();
|
||||
|
||||
var lastPageButton = component.Find("a.rz-pager-last");
|
||||
Assert.True(lastPageButton.HasAttribute("disabled"));
|
||||
|
||||
var nextPageButton = component.Find("a.rz-pager-next");
|
||||
Assert.True(nextPageButton.HasAttribute("disabled"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using Bunit;
|
||||
using Bunit.TestDoubles;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
@@ -22,6 +23,9 @@ namespace Radzen.Blazor.Tests
|
||||
}
|
||||
|
||||
private static string CreatePanelMenu(string currentAbsoluteUrl, NavLinkMatch match, params string[] urls)
|
||||
=> CreatePanelMenu(currentAbsoluteUrl, match, new Dictionary<string, bool>(urls.Select(url => new KeyValuePair<string, bool>(url, false))));
|
||||
|
||||
private static string CreatePanelMenu(string currentAbsoluteUrl, NavLinkMatch match, Dictionary<string, bool> urls)
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
@@ -30,12 +34,13 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPanelMenu>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Match, match).AddChildContent(builder =>
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Match, match).AddChildContent(builder =>
|
||||
{
|
||||
foreach (var url in urls)
|
||||
{
|
||||
builder.OpenComponent<RadzenPanelMenuItem>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPanelMenuItem.Path), url);
|
||||
builder.AddAttribute(1, nameof(RadzenPanelMenuItem.Path), url.Key);
|
||||
builder.AddAttribute(2, nameof(RadzenPanelMenuItem.Disabled), url.Value);
|
||||
builder.CloseComponent();
|
||||
}
|
||||
}));
|
||||
@@ -55,6 +60,19 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Equal(firstIndex, lastIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenPanelMenu_CanDisableMenuItem()
|
||||
{
|
||||
var urls = new Dictionary<string, bool>
|
||||
{
|
||||
{"/datagrid", false},
|
||||
{"/disabled-url", true}
|
||||
};
|
||||
var component = CreatePanelMenu("http://www.example.com/", NavLinkMatch.All, urls);
|
||||
|
||||
Assert.Contains("rz-state-disabled", component);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadzenPanelMenu_MatchesQueryStringParameters()
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, value));
|
||||
|
||||
Assert.Contains(@$"<i class=""rzi"">{value}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rzi"">{value}</i>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -119,11 +119,11 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AllowCollapse, true));
|
||||
|
||||
Assert.Contains(@"<span class=""rzi rzi-minus""></span>", component.Markup);
|
||||
Assert.Contains(@"<span class=""notranslate rzi rzi-minus""></span>", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.Collapsed, true));
|
||||
|
||||
Assert.Contains(@"<span class=""rzi rzi-plus""></span>", component.Markup);
|
||||
Assert.Contains(@"<span class=""notranslate rzi rzi-plus""></span>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -198,14 +198,10 @@ namespace Radzen.Blazor.Tests
|
||||
});
|
||||
|
||||
Assert.Contains("SummaryContent", component.Markup);
|
||||
Assert.Equal(
|
||||
"display: block",
|
||||
component.Find(".rz-panel-content-summary").ParentElement.Attributes.First(attr => attr.Name == "style").Value
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Panel_DontRenders_SummaryWhenOpen()
|
||||
public void Panel_DoesNotRender_SummaryWhenOpen()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenPanel>();
|
||||
@@ -225,8 +221,8 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.Contains("SummaryContent", component.Markup);
|
||||
Assert.Equal(
|
||||
"display: none",
|
||||
component.Find(".rz-panel-content-summary").ParentElement.Attributes.First(attr => attr.Name == "style").Value
|
||||
"true",
|
||||
component.Find(".rz-panel-content-summary").ParentElement.ParentElement.Attributes.First(attr => attr.Name == "aria-hidden").Value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,11 +119,11 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPassword>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""new-password""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""on""", component.Markup);
|
||||
|
||||
@@ -139,22 +139,22 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPassword>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, false));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", false));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.On));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""new-password""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.Off));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""off""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.CurrentPassword));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.CurrentPassword.GetAutoCompleteValue()}""", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add<bool>(p => p.AutoComplete, true));
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("AutoComplete", true));
|
||||
component.SetParametersAndRender(parameters => parameters.Add<AutoCompleteType>(p => p.AutoCompleteType, AutoCompleteType.NewPassword));
|
||||
|
||||
Assert.Contains(@$"autocomplete=""{AutoCompleteType.NewPassword.GetAutoCompleteValue()}""", component.Markup);
|
||||
@@ -171,24 +171,49 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.Contains(@$"autofocus", component.Markup);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Password_Raises_ChangedEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPassword>();
|
||||
|
||||
var raised = false;
|
||||
var hasRaised = false;
|
||||
var value = "Test";
|
||||
object newValue = null;
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Change, args => { raised = true; newValue = args; }));
|
||||
var component = ctx.RenderComponent<RadzenPassword>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Change, args => { hasRaised = true; newValue = args; });
|
||||
parameters.Add(p => p.Immediate, false);
|
||||
});
|
||||
|
||||
component.Find("input").Change(value);
|
||||
var inputElement = component.Find("input");
|
||||
inputElement.Change(value);
|
||||
|
||||
Assert.True(raised);
|
||||
Assert.True(object.Equals(value, newValue));
|
||||
Assert.DoesNotContain("oninput", inputElement.ToMarkup());
|
||||
Assert.True(hasRaised);
|
||||
Assert.Equal(value, newValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Password_Raises_InputEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var hasRaised = false;
|
||||
var value = "Test";
|
||||
object newValue = null;
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPassword>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Change, args => { hasRaised = true; newValue = args; });
|
||||
parameters.Add(p => p.Immediate, true);
|
||||
});
|
||||
|
||||
var inputElement = component.Find("input");
|
||||
inputElement.Input(value);
|
||||
|
||||
Assert.DoesNotContain("onchange", inputElement.ToMarkup());
|
||||
Assert.True(hasRaised);
|
||||
Assert.Equal(value, newValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
220
Radzen.Blazor.Tests/PickListTests.cs
Normal file
220
Radzen.Blazor.Tests/PickListTests.cs
Normal file
@@ -0,0 +1,220 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class PickListTests
|
||||
{
|
||||
class Item
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>();
|
||||
|
||||
Assert.Contains(@"rz-picklist", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_SourceAndTargetLists()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>();
|
||||
|
||||
Assert.Contains("rz-picklist-source-wrapper", component.Markup);
|
||||
Assert.Contains("rz-picklist-target-wrapper", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_TransferButtons()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>();
|
||||
|
||||
Assert.Contains("rz-picklist-buttons", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_Orientation_Horizontal()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Horizontal);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-flex-row", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_Orientation_Vertical()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Vertical);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-flex-column", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_AllowFiltering()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "Item 1" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
parameters.Add(p => p.Source, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
});
|
||||
|
||||
Assert.Contains("rz-listbox-filter", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_SourceData()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "Source Item 1" },
|
||||
new Item { Id = 2, Name = "Source Item 2" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Source, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
});
|
||||
|
||||
Assert.Contains("Source Item 1", component.Markup);
|
||||
Assert.Contains("Source Item 2", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_ShowHeader()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowHeader, true);
|
||||
parameters.Add(p => p.SourceHeader, builder => builder.AddContent(0, "Available Items"));
|
||||
parameters.Add(p => p.TargetHeader, builder => builder.AddContent(0, "Selected Items"));
|
||||
});
|
||||
|
||||
Assert.Contains("Available Items", component.Markup);
|
||||
Assert.Contains("Selected Items", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_AllowMoveAll_Buttons()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var data = new List<Item> { new Item { Id = 1, Name = "Item" } };
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowMoveAll, true);
|
||||
parameters.Add(p => p.Source, data);
|
||||
});
|
||||
|
||||
// Should have 4 buttons when AllowMoveAll is true
|
||||
var buttonCount = System.Text.RegularExpressions.Regex.Matches(component.Markup, "rz-button").Count;
|
||||
Assert.True(buttonCount >= 4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_GetSelectedSources_Respects_ValueProperty_Single()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "A" },
|
||||
new Item { Id = 2, Name = "B" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Source, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
parameters.Add(p => p.ValueProperty, "Id");
|
||||
parameters.Add(p => p.Multiple, false);
|
||||
});
|
||||
|
||||
// Simulate ListBox selection when ValueProperty is set: selectedSourceItems becomes the value (Id)
|
||||
var field = typeof(RadzenPickList<Item>).GetField("selectedSourceItems", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||
field.SetValue(component.Instance, 2);
|
||||
|
||||
var selected = component.Instance.GetSelectedSources();
|
||||
Assert.Single(selected);
|
||||
Assert.Equal(2, selected.First().Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PickList_GetSelectedSources_Respects_ValueProperty_Multiple()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
|
||||
var data = new List<Item>
|
||||
{
|
||||
new Item { Id = 1, Name = "A" },
|
||||
new Item { Id = 2, Name = "B" },
|
||||
new Item { Id = 3, Name = "C" }
|
||||
};
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPickList<Item>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Source, data);
|
||||
parameters.Add(p => p.TextProperty, "Name");
|
||||
parameters.Add(p => p.ValueProperty, "Id");
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
});
|
||||
|
||||
// Simulate ListBox selection when ValueProperty is set: selectedSourceItems becomes IEnumerable of values (Ids)
|
||||
var field = typeof(RadzenPickList<Item>).GetField("selectedSourceItems", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||
field.SetValue(component.Instance, new[] { 1, 3 });
|
||||
|
||||
var selected = component.Instance.GetSelectedSources().Select(i => i.Id).OrderBy(i => i).ToArray();
|
||||
Assert.Equal(new[] { 1, 3 }, selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
644
Radzen.Blazor.Tests/PivotDataGridTests.cs
Normal file
644
Radzen.Blazor.Tests/PivotDataGridTests.cs
Normal file
@@ -0,0 +1,644 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Radzen;
|
||||
using Radzen.Blazor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class PivotDataGridTests
|
||||
{
|
||||
private static readonly List<SalesData> SampleData = new()
|
||||
{
|
||||
new SalesData { Region = "North", Category = "Electronics", Product = "Laptop", Amount = 1000, Year = 2023 },
|
||||
new SalesData { Region = "North", Category = "Electronics", Product = "Laptop", Amount = 1500, Year = 2024 },
|
||||
new SalesData { Region = "South", Category = "Home", Product = "Vacuum", Amount = 500, Year = 2023 }
|
||||
};
|
||||
|
||||
public class SalesData
|
||||
{
|
||||
public string Region { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string Product { get; set; }
|
||||
public double Amount { get; set; }
|
||||
public int Year { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_CssClasses()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx);
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-pivot-data-grid", component.Markup);
|
||||
Assert.Contains("rz-pivot-table", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_RowAndColumnHeaders()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx);
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var table = component.Find(".rz-pivot-content .rz-pivot-table");
|
||||
var headers = table.GetElementsByClassName("rz-pivot-header-text").Select(h => h.TextContent.Trim()).ToList();
|
||||
var aggregateHeaders = table.GetElementsByClassName("rz-pivot-aggregate-header").Select(h => h.TextContent.Trim()).ToList();
|
||||
|
||||
Assert.Contains("Region", headers);
|
||||
Assert.Contains("2023", headers);
|
||||
Assert.Contains("Sales", aggregateHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowSorting_RendersSortableClass()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowSorting, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var sortableHeaders = component.FindAll(".rz-pivot-header-content.rz-sortable");
|
||||
Assert.NotEmpty(sortableHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowFiltering_RendersFilterIcon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var filterIcons = component.FindAll(".rz-grid-filter-icon");
|
||||
Assert.NotEmpty(filterIcons);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_AggregateValues()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx);
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("1000", component.Markup);
|
||||
Assert.Contains("1500", component.Markup);
|
||||
Assert.Contains("500", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_DisallowFiltering_HidesFilterIcon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFiltering, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var filterIcons = component.FindAll(".rz-grid-filter-icon");
|
||||
Assert.Empty(filterIcons);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_DisallowSorting_HidesSortableClass()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowSorting, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var sortableHeaders = component.FindAll(".rz-pivot-header-content.rz-sortable");
|
||||
Assert.Empty(sortableHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_ShowColumnsTotals_RendersFooter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowColumnsTotals, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var footer = component.FindAll(".rz-pivot-footer");
|
||||
Assert.NotEmpty(footer);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_HideColumnsTotals_NoFooter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowColumnsTotals, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var footer = component.FindAll(".rz-pivot-footer");
|
||||
Assert.Empty(footer);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_DefaultEmptyText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPivotDataGrid<SalesData>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, new List<SalesData>());
|
||||
parameters.Add(p => p.AllowFieldsPicking, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("No records to display.", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_CustomEmptyText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPivotDataGrid<SalesData>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, new List<SalesData>());
|
||||
parameters.Add(p => p.EmptyText, "No data available");
|
||||
parameters.Add(p => p.AllowFieldsPicking, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("No data available", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowPaging_RendersPager()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.PageSize, 2);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-pager", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_DisallowPaging_HidesPager()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.DoesNotContain("rz-pager", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_PagerPosition_Top()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.PagerPosition, PagerPosition.Top);
|
||||
parameters.Add(p => p.PageSize, 2);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-pager", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_PagerPosition_TopAndBottom()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.PagerPosition, PagerPosition.TopAndBottom);
|
||||
parameters.Add(p => p.PageSize, 2);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var pagers = component.FindAll(".rz-pager");
|
||||
Assert.True(pagers.Count >= 1); // Should have at least one pager
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Density_Compact()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowPaging, true);
|
||||
parameters.Add(p => p.PageSize, 1); // Force pager to show with small page size
|
||||
parameters.Add(p => p.AllowFieldsPicking, false);
|
||||
parameters.Add(p => p.Density, Density.Compact);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-density-compact", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowAlternatingRows_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowAlternatingRows, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-grid-table-striped", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowAlternatingRows_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowAlternatingRows, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.DoesNotContain("rz-grid-table-striped", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowFieldsPicking_ShowsPanel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFieldsPicking, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-panel", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowFieldsPicking_False_HidesPanel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowFieldsPicking, false);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.DoesNotContain("rz-panel", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_AllowDrillDown()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowDrillDown, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
// Should render pivot content
|
||||
Assert.Contains("rz-pivot-content", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_RowValues()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx);
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var cells = component.FindAll(".rz-pivot-row-header");
|
||||
var cellTexts = cells.Select(c => c.TextContent.Trim()).ToList();
|
||||
|
||||
Assert.Contains("North", cellTexts);
|
||||
Assert.Contains("South", cellTexts);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_MultipleRows()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPivotDataGrid<SalesData>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, SampleData);
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Rows, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotRow<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotRow<SalesData>.Property), nameof(SalesData.Region));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotRow<SalesData>.Title), "Region");
|
||||
builder.CloseComponent();
|
||||
|
||||
builder.OpenComponent<RadzenPivotRow<SalesData>>(2);
|
||||
builder.AddAttribute(3, nameof(RadzenPivotRow<SalesData>.Property), nameof(SalesData.Category));
|
||||
builder.AddAttribute(4, nameof(RadzenPivotRow<SalesData>.Title), "Category");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotColumn<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotColumn<SalesData>.Property), nameof(SalesData.Year));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotColumn<SalesData>.Title), "Year");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Aggregates, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotAggregate<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotAggregate<SalesData>.Property), nameof(SalesData.Amount));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotAggregate<SalesData>.Title), "Sales");
|
||||
builder.AddAttribute(3, nameof(RadzenPivotAggregate<SalesData>.Aggregate), AggregateFunction.Sum);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var headers = component.FindAll(".rz-pivot-header-text").Select(h => h.TextContent.Trim()).ToList();
|
||||
Assert.Contains("Region", headers);
|
||||
Assert.Contains("Category", headers);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_MultipleAggregates()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenPivotDataGrid<SalesData>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, SampleData);
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Rows, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotRow<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotRow<SalesData>.Property), nameof(SalesData.Region));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotRow<SalesData>.Title), "Region");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotColumn<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotColumn<SalesData>.Property), nameof(SalesData.Year));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotColumn<SalesData>.Title), "Year");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Aggregates, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotAggregate<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotAggregate<SalesData>.Property), nameof(SalesData.Amount));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotAggregate<SalesData>.Title), "Total Sales");
|
||||
builder.AddAttribute(3, nameof(RadzenPivotAggregate<SalesData>.Aggregate), AggregateFunction.Sum);
|
||||
builder.CloseComponent();
|
||||
|
||||
builder.OpenComponent<RadzenPivotAggregate<SalesData>>(4);
|
||||
builder.AddAttribute(5, nameof(RadzenPivotAggregate<SalesData>.Property), nameof(SalesData.Amount));
|
||||
builder.AddAttribute(6, nameof(RadzenPivotAggregate<SalesData>.Title), "Count Sales");
|
||||
builder.AddAttribute(7, nameof(RadzenPivotAggregate<SalesData>.Aggregate), AggregateFunction.Count);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
var aggregateHeaders = component.FindAll(".rz-pivot-aggregate-header").Select(h => h.TextContent.Trim()).ToList();
|
||||
Assert.Contains("Total Sales", aggregateHeaders);
|
||||
Assert.Contains("Count Sales", aggregateHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_Renders_AlternatingRowClasses()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = RenderPivotDataGrid(ctx, parameters =>
|
||||
{
|
||||
parameters.Add(p => p.AllowAlternatingRows, true);
|
||||
});
|
||||
|
||||
component.WaitForAssertion(() =>
|
||||
{
|
||||
Assert.Contains("rz-pivot-row-even", component.Markup);
|
||||
Assert.Contains("rz-pivot-row-odd", component.Markup);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_PageSize_DefaultsTo10()
|
||||
{
|
||||
var grid = new RadzenPivotDataGrid<SalesData>();
|
||||
Assert.Equal(10, grid.PageSize);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowSorting_DefaultsToTrue()
|
||||
{
|
||||
var grid = new RadzenPivotDataGrid<SalesData>();
|
||||
Assert.True(grid.AllowSorting);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowFiltering_DefaultsToTrue()
|
||||
{
|
||||
var grid = new RadzenPivotDataGrid<SalesData>();
|
||||
Assert.True(grid.AllowFiltering);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowAlternatingRows_DefaultsToTrue()
|
||||
{
|
||||
var grid = new RadzenPivotDataGrid<SalesData>();
|
||||
Assert.True(grid.AllowAlternatingRows);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowDrillDown_DefaultsToTrue()
|
||||
{
|
||||
var grid = new RadzenPivotDataGrid<SalesData>();
|
||||
Assert.True(grid.AllowDrillDown);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PivotDataGrid_AllowFieldsPicking_DefaultsToTrue()
|
||||
{
|
||||
var grid = new RadzenPivotDataGrid<SalesData>();
|
||||
Assert.True(grid.AllowFieldsPicking);
|
||||
}
|
||||
|
||||
private static IRenderedComponent<RadzenPivotDataGrid<SalesData>> RenderPivotDataGrid(TestContext ctx, Action<ComponentParameterCollectionBuilder<RadzenPivotDataGrid<SalesData>>> configure = null)
|
||||
{
|
||||
return ctx.RenderComponent<RadzenPivotDataGrid<SalesData>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Data, SampleData);
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Rows, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotRow<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotRow<SalesData>.Property), nameof(SalesData.Region));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotRow<SalesData>.Title), "Region");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Columns, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotColumn<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotColumn<SalesData>.Property), nameof(SalesData.Year));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotColumn<SalesData>.Title), "Year");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
parameters.Add<RenderFragment>(p => p.Aggregates, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenPivotAggregate<SalesData>>(0);
|
||||
builder.AddAttribute(1, nameof(RadzenPivotAggregate<SalesData>.Property), nameof(SalesData.Amount));
|
||||
builder.AddAttribute(2, nameof(RadzenPivotAggregate<SalesData>.Title), "Sales");
|
||||
builder.AddAttribute(3, nameof(RadzenPivotAggregate<SalesData>.Aggregate), AggregateFunction.Sum);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
|
||||
configure?.Invoke(parameters);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
120
Radzen.Blazor.Tests/ProfileMenuItemTests.cs
Normal file
120
Radzen.Blazor.Tests/ProfileMenuItemTests.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using Bunit;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class ProfileMenuItemTests
|
||||
{
|
||||
[Fact]
|
||||
public void ProfileMenuItem_Renders_TextParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenProfileMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenProfileMenuItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Profile");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("Profile", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfileMenuItem_Renders_IconParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenProfileMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenProfileMenuItem>(0);
|
||||
builder.AddAttribute(1, "Icon", "account_circle");
|
||||
builder.AddAttribute(2, "Text", "Profile");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("account_circle", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfileMenuItem_Template_OverridesText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenProfileMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenProfileMenuItem>(0);
|
||||
builder.AddAttribute(1, "Text", "This should not appear");
|
||||
builder.AddAttribute(2, "Template", (RenderFragment)((templateBuilder) =>
|
||||
{
|
||||
templateBuilder.OpenElement(0, "span");
|
||||
templateBuilder.AddAttribute(1, "class", "template-content");
|
||||
templateBuilder.AddContent(2, "Template Content");
|
||||
templateBuilder.CloseElement();
|
||||
}));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Template should be rendered
|
||||
Assert.Contains("template-content", component.Markup);
|
||||
// Text should not be rendered in navigation-item-text span when Template is present
|
||||
Assert.DoesNotContain("<span class=\"rz-navigation-item-text\">This should not appear</span>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfileMenuItem_Renders_TemplateWithSwitch()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenProfileMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenProfileMenuItem>(0);
|
||||
builder.AddAttribute(1, "Icon", "settings");
|
||||
builder.AddAttribute(2, "Template", (RenderFragment)((templateBuilder) =>
|
||||
{
|
||||
templateBuilder.OpenComponent<RadzenSwitch>(0);
|
||||
templateBuilder.CloseComponent();
|
||||
}));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Icon should still be rendered
|
||||
Assert.Contains("settings", component.Markup);
|
||||
// Switch should be rendered from template
|
||||
Assert.Contains("rz-switch", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfileMenuItem_Renders_PathParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
|
||||
var component = ctx.RenderComponent<RadzenProfileMenu>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ChildContent, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenProfileMenuItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Settings");
|
||||
builder.AddAttribute(2, "Path", "/settings");
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains("href=\"/settings\"", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Style, value));
|
||||
|
||||
Assert.Contains(@$"style=""{value}""", component.Markup);
|
||||
Assert.Contains(@$"style=""--rz-progressbar-value: 0%;{value}""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -118,7 +118,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add<double>(p => p.Max, max);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"style=""width: {Math.Min(value / max * 100, 100).ToInvariantString()}%;""", component.Markup);
|
||||
Assert.Contains(@$"style=""--rz-progressbar-value: {Math.Min(value / max * 100, 100).ToInvariantString()}%;""", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -142,5 +142,45 @@ namespace Radzen.Blazor.Tests
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Mode, ProgressBarMode.Indeterminate));
|
||||
Assert.Contains(@$"rz-progressbar-info", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProgressBar_Renders_ShowValue_True()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenProgressBar>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowValue, true);
|
||||
parameters.Add(p => p.Value, 50);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-progressbar-label", component.Markup);
|
||||
Assert.Contains("50%", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProgressBar_Renders_ShowValue_False()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenProgressBar>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.ShowValue, false);
|
||||
parameters.Add(p => p.Value, 50);
|
||||
});
|
||||
|
||||
Assert.DoesNotContain("rz-progressbar-label", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProgressBar_Renders_Template()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenProgressBar>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Value, 75);
|
||||
parameters.Add(p => p.Template, builder => builder.AddContent(0, "Custom: 75%"));
|
||||
});
|
||||
|
||||
Assert.Contains("Custom: 75%", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using AngleSharp.Css;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
@@ -116,5 +113,43 @@ namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public List<string> Values { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetProperty_Should_Resolve_DescriptionProperty()
|
||||
{
|
||||
var descriptionProperty = PropertyAccess.GetProperty(typeof(ISimpleInterface), nameof(ISimpleInterface.Description));
|
||||
|
||||
Assert.NotNull(descriptionProperty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetProperty_Should_Resolve_NameProperty()
|
||||
{
|
||||
var nameProperty = PropertyAccess.GetProperty(typeof(ISimpleInterface), nameof(ISimpleInterface.Name));
|
||||
|
||||
Assert.NotNull(nameProperty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetProperty_Should_Resolve_IdProperty()
|
||||
{
|
||||
var idProperty = PropertyAccess.GetProperty(typeof(ISimpleInterface), nameof(ISimpleBaseInterface.Id));
|
||||
Assert.NotNull(idProperty);
|
||||
}
|
||||
|
||||
interface ISimpleInterface : ISimpleNestedInterface
|
||||
{
|
||||
string Description { get; set; }
|
||||
}
|
||||
|
||||
interface ISimpleNestedInterface : ISimpleBaseInterface
|
||||
{
|
||||
string Name { get; set; }
|
||||
}
|
||||
|
||||
interface ISimpleBaseInterface
|
||||
{
|
||||
int Id { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2526
Radzen.Blazor.Tests/QueryableExtensionTests.cs
Normal file
2526
Radzen.Blazor.Tests/QueryableExtensionTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
62
Radzen.Blazor.Tests/RadioButtonListTests.cs
Normal file
62
Radzen.Blazor.Tests/RadioButtonListTests.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Bunit;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class RadioButtonListTests
|
||||
{
|
||||
[Fact]
|
||||
public void RadioButtonList_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRadioButtonList<int>>();
|
||||
|
||||
Assert.Contains(@"rz-radio-button-list", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadioButtonList_Renders_Orientation()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRadioButtonList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenRadioButtonListItem<int>>(0);
|
||||
builder.AddAttribute(1, "Text", "Option 1");
|
||||
builder.AddAttribute(2, "Value", 1);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Orientation, Orientation.Horizontal));
|
||||
// Orientation is applied via RadzenStack which uses flex-direction
|
||||
Assert.Contains("rz-flex-row", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Orientation, Orientation.Vertical));
|
||||
Assert.Contains("rz-flex-column", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadioButtonList_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRadioButtonList<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenRadioButtonListItem<int>>(0);
|
||||
builder.AddAttribute(1, "Text", "Option 1");
|
||||
builder.AddAttribute(2, "Value", 1);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
// Disabled class is on the radio button items
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8</TargetFramework>
|
||||
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>disable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
||||
<PackageReference Include="bunit.web" Version="1.2.49" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
|
||||
<PackageReference Include="bunit.web" Version="1.36.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
82
Radzen.Blazor.Tests/RatingTests.cs
Normal file
82
Radzen.Blazor.Tests/RatingTests.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class RatingTests
|
||||
{
|
||||
[Fact]
|
||||
public void Rating_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRating>();
|
||||
|
||||
Assert.Contains(@"rz-rating", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rating_Renders_Stars()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRating>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Stars, 5));
|
||||
|
||||
// Should render 5 star icons (rzi-star or rzi-star-o) + 1 clear button icon = 6 total
|
||||
var starCount = System.Text.RegularExpressions.Regex.Matches(component.Markup, "rz-rating-icon").Count;
|
||||
Assert.Equal(6, starCount); // 5 stars + 1 clear button
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rating_Renders_CustomStarCount()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRating>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Stars, 10));
|
||||
|
||||
var starCount = System.Text.RegularExpressions.Regex.Matches(component.Markup, "rz-rating-icon").Count;
|
||||
Assert.Equal(11, starCount); // 10 stars + 1 clear button
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rating_Renders_Value()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRating>();
|
||||
|
||||
component.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Value, 3);
|
||||
parameters.Add(p => p.Stars, 5);
|
||||
});
|
||||
|
||||
// Should have 3 filled stars (rzi-star) and 2 outline stars (rzi-star-o)
|
||||
var filledStars = System.Text.RegularExpressions.Regex.Matches(component.Markup, "rzi-star\"").Count;
|
||||
Assert.Equal(3, filledStars);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rating_Renders_ReadOnly()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRating>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ReadOnly, true));
|
||||
|
||||
Assert.Contains("rz-state-readonly", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rating_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRating>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, true));
|
||||
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
63
Radzen.Blazor.Tests/RowTests.cs
Normal file
63
Radzen.Blazor.Tests/RowTests.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class RowTests
|
||||
{
|
||||
[Fact]
|
||||
public void Row_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRow>();
|
||||
|
||||
Assert.Contains(@"rz-row", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Row_Renders_ChildContent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRow>(parameters =>
|
||||
{
|
||||
parameters.AddChildContent("<div>Row Content</div>");
|
||||
});
|
||||
|
||||
Assert.Contains("Row Content", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Row_Renders_Gap()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRow>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Gap, "2rem"));
|
||||
|
||||
Assert.Contains("--rz-gap:2rem", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Row_Renders_RowGap()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRow>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.RowGap, "1.5rem"));
|
||||
|
||||
Assert.Contains("--rz-row-gap:1.5rem", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Row_Renders_ColumnGap()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenRow>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Gap, "1rem"));
|
||||
|
||||
Assert.Contains("--rz-gap:1rem", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
63
Radzen.Blazor.Tests/SchedulerYearRangeTests.cs
Normal file
63
Radzen.Blazor.Tests/SchedulerYearRangeTests.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Bunit;
|
||||
using Microsoft.JSInterop;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Radzen.Blazor;
|
||||
using Radzen.Blazor.Rendering;
|
||||
using Radzen;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class SchedulerYearRangeTests
|
||||
{
|
||||
class Appointment
|
||||
{
|
||||
public DateTime Start { get; set; }
|
||||
public DateTime End { get; set; }
|
||||
public string Text { get; set; } = "";
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void YearView_StartMonthJanuary_IncludesLastDaysOfYear_WhenYearStartsOnFirstDayOfWeek()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.Services.AddScoped<DialogService>();
|
||||
ctx.JSInterop.Setup<Rect>("Radzen.createScheduler", _ => true)
|
||||
.SetResult(new Rect { Left = 0, Top = 0, Width = 200, Height = 200 });
|
||||
|
||||
// Make the first day of week Monday and use a year where Jan 1 is Monday (2024-01-01).
|
||||
var culture = (CultureInfo)CultureInfo.InvariantCulture.Clone();
|
||||
culture.DateTimeFormat.FirstDayOfWeek = DayOfWeek.Monday;
|
||||
|
||||
var appointments = new List<Appointment>
|
||||
{
|
||||
new() { Start = new DateTime(2024, 12, 31), End = new DateTime(2025, 1, 1), Text = "Year end" }
|
||||
};
|
||||
|
||||
var cut = ctx.RenderComponent<RadzenScheduler<Appointment>>(p =>
|
||||
{
|
||||
p.Add(x => x.Culture, culture);
|
||||
p.Add(x => x.Date, new DateTime(2024, 6, 1));
|
||||
p.Add(x => x.Data, appointments);
|
||||
p.Add(x => x.StartProperty, nameof(Appointment.Start));
|
||||
p.Add(x => x.EndProperty, nameof(Appointment.End));
|
||||
p.Add(x => x.TextProperty, nameof(Appointment.Text));
|
||||
p.AddChildContent<RadzenYearView>(v => v.Add(x => x.StartMonth, Radzen.Month.January));
|
||||
});
|
||||
|
||||
var view = Assert.IsType<RadzenYearView>(cut.Instance.SelectedView);
|
||||
|
||||
// View should start on 2023-12-25 (one extra week above since 2024-01-01 is Monday).
|
||||
Assert.Equal(new DateTime(2023, 12, 25), view.StartDate.Date);
|
||||
|
||||
// View end must include 2024-12-31 (it should extend to end-of-week containing the real year end).
|
||||
Assert.True(view.EndDate.Date >= new DateTime(2024, 12, 31), $"EndDate was {view.EndDate:yyyy-MM-dd}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
45
Radzen.Blazor.Tests/SecurityCodeTests.cs
Normal file
45
Radzen.Blazor.Tests/SecurityCodeTests.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class SecurityCodeTests
|
||||
{
|
||||
[Fact]
|
||||
public void SecurityCode_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenSecurityCode>();
|
||||
|
||||
Assert.Contains(@"rz-security-code", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SecurityCode_Renders_Count()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenSecurityCode>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Count, 6));
|
||||
|
||||
// Should render 6 input boxes + 1 hidden input for form submission = 7 total
|
||||
var inputCount = System.Text.RegularExpressions.Regex.Matches(component.Markup, "<input").Count;
|
||||
Assert.Equal(7, inputCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SecurityCode_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
var component = ctx.RenderComponent<RadzenSecurityCode>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, true));
|
||||
|
||||
Assert.Contains("disabled", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
83
Radzen.Blazor.Tests/SelectBarTests.cs
Normal file
83
Radzen.Blazor.Tests/SelectBarTests.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class SelectBarTests
|
||||
{
|
||||
[Fact]
|
||||
public void SelectBar_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSelectBar<int>>();
|
||||
|
||||
Assert.Contains(@"rz-selectbar", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectBar_Renders_Orientation()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSelectBar<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenSelectBarItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Option 1");
|
||||
builder.AddAttribute(2, "Value", 1);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Orientation, Orientation.Horizontal));
|
||||
Assert.Contains("rz-selectbar-horizontal", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Orientation, Orientation.Vertical));
|
||||
Assert.Contains("rz-selectbar-vertical", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectBar_Renders_Multiple()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
// When Multiple is true, TValue should be IEnumerable<T>
|
||||
var component = ctx.RenderComponent<RadzenSelectBar<System.Collections.Generic.IEnumerable<int>>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Multiple, true);
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenSelectBarItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Option 1");
|
||||
builder.AddAttribute(2, "Value", 1);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
Assert.NotNull(component.Instance);
|
||||
Assert.True(component.Instance.Multiple);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectBar_Renders_Size()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSelectBar<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Items, builder =>
|
||||
{
|
||||
builder.OpenComponent<RadzenSelectBarItem>(0);
|
||||
builder.AddAttribute(1, "Text", "Option 1");
|
||||
builder.AddAttribute(2, "Value", 1);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
});
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, ButtonSize.Small));
|
||||
Assert.Contains("rz-button-sm", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Size, ButtonSize.Large));
|
||||
Assert.Contains("rz-button-lg", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
65
Radzen.Blazor.Tests/SidebarToggleTests.cs
Normal file
65
Radzen.Blazor.Tests/SidebarToggleTests.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class SidebarToggleTests
|
||||
{
|
||||
[Fact]
|
||||
public void SidebarToggle_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSidebarToggle>();
|
||||
|
||||
Assert.Contains(@"rz-sidebar-toggle", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SidebarToggle_Renders_DefaultIcon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSidebarToggle>();
|
||||
|
||||
Assert.Contains("menu", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SidebarToggle_Renders_CustomIcon()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSidebarToggle>();
|
||||
|
||||
var icon = "close";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
|
||||
|
||||
Assert.Contains(icon, component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SidebarToggle_Renders_AriaLabel()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSidebarToggle>();
|
||||
|
||||
var ariaLabel = "Toggle Navigation";
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.ToggleAriaLabel, ariaLabel));
|
||||
|
||||
Assert.Contains($"aria-label=\"{ariaLabel}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SidebarToggle_Raises_ClickEvent()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSidebarToggle>();
|
||||
|
||||
var clicked = false;
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Click, args => { clicked = true; }));
|
||||
|
||||
component.Find("button").Click();
|
||||
|
||||
Assert.True(clicked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
158
Radzen.Blazor.Tests/SkeletonTests.cs
Normal file
158
Radzen.Blazor.Tests/SkeletonTests.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using Bunit;
|
||||
using Radzen.Blazor.Rendering;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class SkeletonTests
|
||||
{
|
||||
[Fact]
|
||||
public void Skeleton_Renders_CssClass()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSkeleton>();
|
||||
|
||||
Assert.Contains("rz-skeleton", component.Markup);
|
||||
Assert.Contains("rz-skeleton-text", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Skeleton_Renders_TypeParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSkeleton>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Variant, SkeletonVariant.Circular));
|
||||
|
||||
Assert.Contains("rz-skeleton", component.Markup);
|
||||
Assert.Contains("rz-skeleton-circular", component.Markup);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SkeletonVariant.Text, "rz-skeleton-text")]
|
||||
[InlineData(SkeletonVariant.Circular, "rz-skeleton-circular")]
|
||||
[InlineData(SkeletonVariant.Rectangular, "rz-skeleton-rectangular")]
|
||||
public void Skeleton_Renders_AllTypes(SkeletonVariant type, string expectedClass)
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSkeleton>(parameters => parameters.Add(p => p.Variant, type));
|
||||
|
||||
Assert.Contains("rz-skeleton", component.Markup);
|
||||
Assert.Contains(expectedClass, component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Skeleton_Renders_AnimationParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSkeleton>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Animation, SkeletonAnimation.Wave));
|
||||
|
||||
Assert.Contains("rz-skeleton", component.Markup);
|
||||
Assert.Contains("rz-skeleton-wave", component.Markup);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SkeletonAnimation.Wave, "rz-skeleton-wave")]
|
||||
[InlineData(SkeletonAnimation.Pulse, "rz-skeleton-pulse")]
|
||||
public void Skeleton_Renders_AllAnimations(SkeletonAnimation animation, string expectedClass)
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSkeleton>(parameters => parameters.Add(p => p.Animation, animation));
|
||||
|
||||
Assert.Contains("rz-skeleton", component.Markup);
|
||||
Assert.Contains(expectedClass, component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Skeleton_Renders_StyleParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSkeleton>();
|
||||
|
||||
var style = "width: 200px; height: 20px;";
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Style, style));
|
||||
|
||||
Assert.Contains($"style=\"{style}\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Skeleton_Renders_VisibleParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSkeleton>();
|
||||
|
||||
// Should be visible by default
|
||||
Assert.Contains("rz-skeleton", component.Markup);
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Visible, false));
|
||||
|
||||
// Should not render when not visible
|
||||
Assert.DoesNotContain("rz-skeleton", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Skeleton_Renders_UnmatchedParameter()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSkeleton>();
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.AddUnmatched("data-testid", "skeleton-test"));
|
||||
|
||||
Assert.Contains("data-testid=\"skeleton-test\"", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Skeleton_DefaultType_IsText()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSkeleton>();
|
||||
|
||||
// Should render with text type by default
|
||||
Assert.Contains("rz-skeleton-text", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Skeleton_DefaultAnimation_IsNone()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSkeleton>();
|
||||
|
||||
// Should not render animation classes by default
|
||||
Assert.DoesNotContain("rz-skeleton-wave", component.Markup);
|
||||
Assert.DoesNotContain("rz-skeleton-pulse", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,5 +89,71 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
Assert.Contains(@$"autofocus", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Slider_Renders_Orientation_Vertical()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSlider<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Vertical);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-slider-vertical", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Slider_Renders_Disabled()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSlider<int>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Disabled, true);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-state-disabled", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Slider_Renders_SliderHandle()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSlider<int>>();
|
||||
|
||||
Assert.Contains("rz-slider-handle", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Slider_Renders_SliderRange()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSlider<int>>();
|
||||
|
||||
Assert.Contains("rz-slider-range", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Slider_Renders_TabIndex()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenSlider<int>>();
|
||||
|
||||
Assert.Contains("tabindex=\"0\"", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Icon, icon));
|
||||
|
||||
Assert.Contains(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -63,7 +63,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add(p => p.Icon, icon);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"<i class=""rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<i class=""notranslate rz-button-icon-left rzi"">{icon}</i>", component.Markup);
|
||||
Assert.Contains(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Radzen.Blazor.Tests
|
||||
|
||||
component.SetParametersAndRender(parameters => parameters.Add(p => p.Image, image));
|
||||
|
||||
Assert.Contains(@$"<img class=""rz-button-icon-left rzi"" src=""{image}"" alt=""image"" />", component.Markup);
|
||||
Assert.Contains(@$"<img class=""notranslate rz-button-icon-left rzi"" src=""{image}"" alt=""image"" />", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -97,7 +97,7 @@ namespace Radzen.Blazor.Tests
|
||||
parameters.Add(p => p.ImageAlternateText, text);
|
||||
});
|
||||
|
||||
Assert.Contains(@$"<img class=""rz-button-icon-left rzi"" src=""{image}"" alt=""{text}"" />", component.Markup);
|
||||
Assert.Contains(@$"<img class=""notranslate rz-button-icon-left rzi"" src=""{image}"" alt=""{text}"" />", component.Markup);
|
||||
Assert.Contains(@$"<span class=""rz-button-text"">{text}</span>", component.Markup);
|
||||
}
|
||||
|
||||
|
||||
42
Radzen.Blazor.Tests/SplitterTests.cs
Normal file
42
Radzen.Blazor.Tests/SplitterTests.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Bunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Tests
|
||||
{
|
||||
public class SplitterTests
|
||||
{
|
||||
[Fact]
|
||||
public void Splitter_Renders_WithClassName()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSplitter>();
|
||||
|
||||
Assert.Contains(@"rz-splitter", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Splitter_Renders_Orientation_Horizontal()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSplitter>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Horizontal);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-splitter-horizontal", component.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Splitter_Renders_Orientation_Vertical()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
var component = ctx.RenderComponent<RadzenSplitter>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.Orientation, Orientation.Vertical);
|
||||
});
|
||||
|
||||
Assert.Contains("rz-splitter-vertical", component.Markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
49
Radzen.Blazor.Tests/Spreadsheet/AggregateFunctionTests.cs
Normal file
49
Radzen.Blazor.Tests/Spreadsheet/AggregateFunctionTests.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class AggregateFunctionTests
|
||||
{
|
||||
readonly Sheet sheet = new(15, 5);
|
||||
|
||||
void SeedWithErrors()
|
||||
{
|
||||
sheet.Cells["A1"].Formula = "=A2/0"; // #DIV/0!
|
||||
sheet.Cells["A2"].Value = 82;
|
||||
sheet.Cells["A3"].Value = 72;
|
||||
sheet.Cells["A4"].Value = 65;
|
||||
sheet.Cells["A5"].Value = 30;
|
||||
sheet.Cells["A6"].Value = 95;
|
||||
sheet.Cells["A7"].Formula = "=0/0"; // #DIV/0!
|
||||
sheet.Cells["A8"].Value = 63;
|
||||
sheet.Cells["A9"].Value = 31;
|
||||
sheet.Cells["A10"].Value = 53;
|
||||
sheet.Cells["A11"].Value = 96;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldComputeMaxIgnoringErrors()
|
||||
{
|
||||
SeedWithErrors();
|
||||
sheet.Cells["B1"].Formula = "=AGGREGATE(4,6,A1:A11)"; // MAX ignoring errors
|
||||
Assert.Equal(96d, sheet.Cells["B1"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldComputeLargeIgnoringErrors()
|
||||
{
|
||||
SeedWithErrors();
|
||||
sheet.Cells["B1"].Formula = "=AGGREGATE(14,6,A1:A11,3)"; // LARGE k=3 ignoring errors
|
||||
Assert.Equal(82d, sheet.Cells["B1"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReturnValueErrorWhenKMissingForSmall()
|
||||
{
|
||||
SeedWithErrors();
|
||||
sheet.Cells["B1"].Formula = "=AGGREGATE(15,6,A1:A11)"; // SMALL requires k
|
||||
Assert.Equal(CellError.Value, sheet.Cells["B1"].Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
160
Radzen.Blazor.Tests/Spreadsheet/AndFunctionTests.cs
Normal file
160
Radzen.Blazor.Tests/Spreadsheet/AndFunctionTests.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class AndFunctionTests
|
||||
{
|
||||
readonly Sheet sheet = new(5, 5);
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionWithAllTrueValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = true;
|
||||
sheet.Cells["A2"].Value = true;
|
||||
sheet.Cells["A3"].Formula = "=AND(A1,A2)";
|
||||
|
||||
Assert.Equal(true, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionWithOneFalseValue()
|
||||
{
|
||||
sheet.Cells["A1"].Value = true;
|
||||
sheet.Cells["A2"].Value = false;
|
||||
sheet.Cells["A3"].Formula = "=AND(A1,A2)";
|
||||
|
||||
Assert.Equal(false, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionWithAllFalseValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = false;
|
||||
sheet.Cells["A2"].Value = false;
|
||||
sheet.Cells["A3"].Formula = "=AND(A1,A2)";
|
||||
|
||||
Assert.Equal(false, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionWithNumericValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 5;
|
||||
sheet.Cells["A2"].Value = 10;
|
||||
sheet.Cells["A3"].Formula = "=AND(A1>1,A2<100)";
|
||||
|
||||
Assert.Equal(true, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionWithZeroAsFalse()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 0;
|
||||
sheet.Cells["A2"].Value = 1;
|
||||
sheet.Cells["A3"].Formula = "=AND(A1,A2)";
|
||||
|
||||
Assert.Equal(false, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionWithNonZeroAsTrue()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 5;
|
||||
sheet.Cells["A2"].Value = 10;
|
||||
sheet.Cells["A3"].Formula = "=AND(A1,A2)";
|
||||
|
||||
Assert.Equal(true, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionWithStringValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = "test";
|
||||
sheet.Cells["A2"].Value = "hello";
|
||||
sheet.Cells["A3"].Formula = "=AND(A1,A2)";
|
||||
|
||||
Assert.Equal(CellError.Value, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionWithEmptyStringAsFalse()
|
||||
{
|
||||
sheet.Cells["A2"].Value = "hello";
|
||||
sheet.Cells["A3"].Formula = "=AND(A1,A2)";
|
||||
|
||||
Assert.Equal(CellError.Value, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionWithMultipleArguments()
|
||||
{
|
||||
sheet.Cells["A1"].Value = true;
|
||||
sheet.Cells["A2"].Value = true;
|
||||
sheet.Cells["A3"].Value = true;
|
||||
sheet.Cells["A4"].Formula = "=AND(A1,A2,A3)";
|
||||
|
||||
Assert.Equal(true, sheet.Cells["A4"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionWithOneFalseInMultipleArguments()
|
||||
{
|
||||
sheet.Cells["A1"].Value = true;
|
||||
sheet.Cells["A2"].Value = false;
|
||||
sheet.Cells["A3"].Value = true;
|
||||
sheet.Cells["A4"].Formula = "=AND(A1,A2,A3)";
|
||||
|
||||
Assert.Equal(false, sheet.Cells["A4"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReturnValueErrorForEmptyAndFunction()
|
||||
{
|
||||
sheet.Cells["A1"].Formula = "=AND()";
|
||||
|
||||
Assert.Equal(CellError.Value, sheet.Cells["A1"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionWithRangeExpression()
|
||||
{
|
||||
sheet.Cells["A1"].Value = true;
|
||||
sheet.Cells["A2"].Value = true;
|
||||
sheet.Cells["A3"].Formula = "=AND(A1:A2)";
|
||||
|
||||
Assert.Equal(true, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionWithMixedTypes()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 5;
|
||||
sheet.Cells["A2"].Value = "3";
|
||||
sheet.Cells["A3"].Value = true;
|
||||
sheet.Cells["A4"].Formula = "=AND(A1,A2,A3)";
|
||||
|
||||
Assert.Equal(true, sheet.Cells["A4"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionInIfStatement()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 5;
|
||||
sheet.Cells["A2"].Value = 10;
|
||||
sheet.Cells["A3"].Formula = "=IF(AND(A1>1,A2<100),A1,\"Out of range\")";
|
||||
|
||||
Assert.Equal(5d, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAndFunctionInIfStatementWithFalseCondition()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 5;
|
||||
sheet.Cells["A2"].Value = 150;
|
||||
sheet.Cells["A3"].Formula = "=IF(AND(A1>1,A2<100),A1,\"Out of range\")";
|
||||
|
||||
Assert.Equal("Out of range", sheet.Cells["A3"].Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
105
Radzen.Blazor.Tests/Spreadsheet/AutoFilterTests.cs
Normal file
105
Radzen.Blazor.Tests/Spreadsheet/AutoFilterTests.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class AutoFilterTests
|
||||
{
|
||||
private readonly Sheet sheet = new(10, 10);
|
||||
|
||||
[Fact]
|
||||
public void Should_ToggleSheetAutoFilter()
|
||||
{
|
||||
// Initially no auto filter
|
||||
Assert.Null(sheet.AutoFilter);
|
||||
|
||||
// Apply auto filter to range A1:C5
|
||||
var range = RangeRef.Parse("A1:C5");
|
||||
var command = new SheetAutoFilterCommand(sheet, range);
|
||||
command.Execute();
|
||||
|
||||
// Auto filter should be applied
|
||||
Assert.NotNull(sheet.AutoFilter);
|
||||
Assert.Equal(range, sheet.AutoFilter.Range);
|
||||
|
||||
// Undo the command
|
||||
command.Unexecute();
|
||||
|
||||
// Auto filter should be removed
|
||||
Assert.Null(sheet.AutoFilter);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_ToggleDataTableFilterButton()
|
||||
{
|
||||
// Add a data table
|
||||
var range = RangeRef.Parse("A1:C5");
|
||||
sheet.AddTable(range);
|
||||
|
||||
var table = sheet.Tables[0];
|
||||
|
||||
// Initially ShowFilterButton should be true
|
||||
Assert.True(table.ShowFilterButton);
|
||||
|
||||
// Toggle filter button off
|
||||
var command = new TableFilterCommand(sheet, 0);
|
||||
command.Execute();
|
||||
|
||||
// ShowFilterButton should be false
|
||||
Assert.False(table.ShowFilterButton);
|
||||
|
||||
// Undo the command
|
||||
command.Unexecute();
|
||||
|
||||
// ShowFilterButton should be true again
|
||||
Assert.True(table.ShowFilterButton);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_HandleMultipleDataTables()
|
||||
{
|
||||
// Add two data tables
|
||||
sheet.AddTable(RangeRef.Parse("A1:C5"));
|
||||
sheet.AddTable(RangeRef.Parse("E1:G5"));
|
||||
|
||||
var table1 = sheet.Tables[0];
|
||||
var table2 = sheet.Tables[1];
|
||||
|
||||
// Initially both should have ShowFilterButton = true
|
||||
Assert.True(table1.ShowFilterButton);
|
||||
Assert.True(table2.ShowFilterButton);
|
||||
|
||||
// Toggle filter button for first data table
|
||||
var command1 = new TableFilterCommand(sheet, 0);
|
||||
command1.Execute();
|
||||
|
||||
// Only first data table should be affected
|
||||
Assert.False(table1.ShowFilterButton);
|
||||
Assert.True(table2.ShowFilterButton);
|
||||
|
||||
// Toggle filter button for second data table
|
||||
var command2 = new TableFilterCommand(sheet, 1);
|
||||
command2.Execute();
|
||||
|
||||
// Both should be affected
|
||||
Assert.False(table1.ShowFilterButton);
|
||||
Assert.False(table2.ShowFilterButton);
|
||||
|
||||
// Undo second command
|
||||
command2.Unexecute();
|
||||
|
||||
// Only second data table should be restored
|
||||
Assert.False(table1.ShowFilterButton);
|
||||
Assert.True(table2.ShowFilterButton);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_HandleInvalidDataTableIndex()
|
||||
{
|
||||
// Try to toggle filter button for non-existent data table
|
||||
var command = new TableFilterCommand(sheet, 0);
|
||||
|
||||
// Should not throw exception
|
||||
var result = command.Execute();
|
||||
Assert.True(result);
|
||||
}
|
||||
}
|
||||
114
Radzen.Blazor.Tests/Spreadsheet/AverageFunctionTests.cs
Normal file
114
Radzen.Blazor.Tests/Spreadsheet/AverageFunctionTests.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class AverageFunctionTests
|
||||
{
|
||||
readonly Sheet sheet = new(5, 5);
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAverageFunctionWithTwoArguments()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 15;
|
||||
sheet.Cells["A3"].Formula = "=AVERAGE(A1,A2)";
|
||||
|
||||
Assert.Equal(12.5, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAverageFunctionWithEmptyCells()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A3"].Formula = "=AVERAGE(A1,A2)";
|
||||
|
||||
Assert.Equal(10.0, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAverageFunctionWithMultipleArguments()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 15;
|
||||
sheet.Cells["A3"].Value = 20;
|
||||
sheet.Cells["A4"].Formula = "=AVERAGE(A1,A2,A3)";
|
||||
|
||||
Assert.Equal(15.0, sheet.Cells["A4"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReturnDiv0ErrorForEmptyAverageFunction()
|
||||
{
|
||||
sheet.Cells["A1"].Formula = "=AVERAGE()";
|
||||
|
||||
Assert.Equal(CellError.Div0, sheet.Cells["A1"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReturnDiv0ErrorForAverageFunctionWithNoNumericValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = "text";
|
||||
sheet.Cells["A2"].Value = "";
|
||||
sheet.Cells["A3"].Formula = "=AVERAGE(A1,A2)";
|
||||
|
||||
Assert.Equal(CellError.Div0, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAverageFunctionWithRange()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 15;
|
||||
sheet.Cells["A3"].Formula = "=AVERAGE(A1:A2)";
|
||||
|
||||
Assert.Equal(12.5, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAverageFunctionWithMixedTypes()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 15.5;
|
||||
sheet.Cells["A3"].Formula = "=AVERAGE(A1,A2)";
|
||||
|
||||
Assert.Equal(12.75, sheet.Cells["A3"].Value);
|
||||
|
||||
sheet.Cells["A4"].Value = 2.5;
|
||||
sheet.Cells["A5"].Formula = "=AVERAGE(A4,A1)";
|
||||
|
||||
Assert.Equal(6.25, sheet.Cells["A5"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAverageFunctionIgnoringTextAndLogicalValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = "text";
|
||||
sheet.Cells["A3"].Value = true;
|
||||
sheet.Cells["A4"].Value = 20;
|
||||
sheet.Cells["A5"].Formula = "=AVERAGE(A1,A2,A3,A4)";
|
||||
|
||||
Assert.Equal(15.0, sheet.Cells["A5"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateAverageFunctionIncludingZeroValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 0;
|
||||
sheet.Cells["A3"].Value = 20;
|
||||
sheet.Cells["A4"].Formula = "=AVERAGE(A1,A2,A3)";
|
||||
|
||||
Assert.Equal(10.0, sheet.Cells["A4"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldCreateRefErrorWhenAverageRangeOutOfBounds()
|
||||
{
|
||||
sheet.Cells["A1"].Formula = "=AVERAGE(A2:A6)";
|
||||
|
||||
Assert.Equal(CellError.Ref, sheet.Cells["A1"].Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
261
Radzen.Blazor.Tests/Spreadsheet/CellSelectionTests.cs
Normal file
261
Radzen.Blazor.Tests/Spreadsheet/CellSelectionTests.cs
Normal file
@@ -0,0 +1,261 @@
|
||||
using Bunit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class CellSelectionTests : TestContext
|
||||
{
|
||||
private readonly Sheet sheet = new (4,4);
|
||||
|
||||
[Fact]
|
||||
public void CellSelection_RendersWithCorrectClasses()
|
||||
{
|
||||
// Arrange
|
||||
var cell = new CellRef(0, 0);
|
||||
sheet.Selection.Select(new RangeRef(cell, cell));
|
||||
var context = new MockVirtualGridContext();
|
||||
|
||||
// Act
|
||||
var cut = RenderComponent<CellSelection>(parameters => parameters
|
||||
.Add(p => p.Cell, cell)
|
||||
.Add(p => p.Sheet, sheet)
|
||||
.Add(p => p.Context, context));
|
||||
|
||||
// Assert
|
||||
var element = cut.Find(".rz-spreadsheet-selection-cell");
|
||||
Assert.NotNull(element);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell", element.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-top", element.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-left", element.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-bottom", element.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-right", element.ClassName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellSelection_AppliesFrozenColumnClass()
|
||||
{
|
||||
// Arrange
|
||||
var cell = new CellRef(0, 0);
|
||||
sheet.Columns.Frozen = 1;
|
||||
sheet.Selection.Select(new RangeRef(cell, cell));
|
||||
var context = new MockVirtualGridContext();
|
||||
|
||||
// Act
|
||||
var cut = RenderComponent<CellSelection>(parameters => parameters
|
||||
.Add(p => p.Cell, cell)
|
||||
.Add(p => p.Sheet, sheet)
|
||||
.Add(p => p.Context, context));
|
||||
|
||||
// Assert
|
||||
var element = cut.Find(".rz-spreadsheet-selection-cell");
|
||||
Assert.Contains("rz-spreadsheet-frozen-column", element.ClassName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellSelection_AppliesFrozenRowClass()
|
||||
{
|
||||
// Arrange
|
||||
var cell = new CellRef(0, 0);
|
||||
var context = new MockVirtualGridContext();
|
||||
sheet.Rows.Frozen = 1;
|
||||
sheet.Selection.Select(new RangeRef(cell, cell));
|
||||
|
||||
// Act
|
||||
var cut = RenderComponent<CellSelection>(parameters => parameters
|
||||
.Add(p => p.Cell, cell)
|
||||
.Add(p => p.Sheet, sheet)
|
||||
.Add(p => p.Context, context));
|
||||
|
||||
// Assert
|
||||
var element = cut.Find(".rz-spreadsheet-selection-cell");
|
||||
Assert.Contains("rz-spreadsheet-frozen-row", element.ClassName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellSelection_CalculatesStyle()
|
||||
{
|
||||
// Arrange
|
||||
var cell = new CellRef(0, 0);
|
||||
sheet.Selection.Select(new RangeRef(cell, cell));
|
||||
var context = new MockVirtualGridContext();
|
||||
|
||||
// Act
|
||||
var cut = RenderComponent<CellSelection>(parameters => parameters
|
||||
.Add(p => p.Cell, cell)
|
||||
.Add(p => p.Sheet, sheet)
|
||||
.Add(p => p.Context, context));
|
||||
|
||||
// Assert
|
||||
var element = cut.Find(".rz-spreadsheet-selection-cell");
|
||||
Assert.Equal("transform: translate(0px, 0px); width: 100px; height: 24px", element.GetAttribute("style"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellSelection_SplitsMergedCell_WhenIntersectingFrozenRow()
|
||||
{
|
||||
// Arrange
|
||||
sheet.Rows.Frozen = 1;
|
||||
var range = new RangeRef(new CellRef(0, 0), new CellRef(2, 0));
|
||||
sheet.MergedCells.Add(range);
|
||||
sheet.Selection.Select(range);
|
||||
var context = new MockVirtualGridContext();
|
||||
|
||||
// Act
|
||||
var cut = RenderComponent<CellSelection>(parameters => parameters
|
||||
.Add(p => p.Cell, new CellRef(0, 0))
|
||||
.Add(p => p.Sheet, sheet)
|
||||
.Add(p => p.Context, context));
|
||||
|
||||
// Assert
|
||||
var elements = cut.FindAll(".rz-spreadsheet-selection-cell");
|
||||
Assert.Equal(2, elements.Count);
|
||||
|
||||
var frozen = cut.Find(".rz-spreadsheet-frozen-row");
|
||||
|
||||
Assert.NotNull(frozen);
|
||||
|
||||
// First element (frozen)
|
||||
Assert.Equal("transform: translate(0px, 0px); width: 100px; height: 24px", frozen.GetAttribute("style"));
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-top", frozen.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-left", frozen.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-right", frozen.ClassName);
|
||||
Assert.DoesNotContain("rz-spreadsheet-selection-cell-bottom", frozen.ClassName);
|
||||
|
||||
var unfrozen = elements.Where(e => e != frozen).FirstOrDefault();
|
||||
|
||||
Assert.NotNull(unfrozen);
|
||||
|
||||
// Second element (non-frozen)
|
||||
Assert.Equal("transform: translate(0px, 24px); width: 100px; height: 48px", unfrozen.GetAttribute("style"));
|
||||
Assert.DoesNotContain("rz-spreadsheet-selection-cell-top", unfrozen.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-left", unfrozen.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-right", unfrozen.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-bottom", unfrozen.ClassName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellSelection_SplitsMergedCell_WhenIntersectingFrozenColumn()
|
||||
{
|
||||
// Arrange
|
||||
sheet.Columns.Frozen = 1;
|
||||
var range = new RangeRef(new CellRef(0, 0), new CellRef(0, 2));
|
||||
sheet.MergedCells.Add(range);
|
||||
sheet.Selection.Select(range);
|
||||
var context = new MockVirtualGridContext();
|
||||
|
||||
// Act
|
||||
var cut = RenderComponent<CellSelection>(parameters => parameters
|
||||
.Add(p => p.Cell, new CellRef(0, 0))
|
||||
.Add(p => p.Sheet, sheet)
|
||||
.Add(p => p.Context, context));
|
||||
|
||||
// Assert
|
||||
var elements = cut.FindAll(".rz-spreadsheet-selection-cell");
|
||||
Assert.Equal(2, elements.Count);
|
||||
|
||||
var frozen = cut.Find(".rz-spreadsheet-frozen-column");
|
||||
|
||||
Assert.NotNull(frozen);
|
||||
|
||||
// First element (frozen)
|
||||
Assert.Contains("rz-spreadsheet-frozen-column", frozen.ClassName);
|
||||
Assert.Equal("transform: translate(0px, 0px); width: 100px; height: 24px", frozen.GetAttribute("style"));
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-top", frozen.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-left", frozen.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-bottom", frozen.ClassName);
|
||||
Assert.DoesNotContain("rz-spreadsheet-selection-cell-right", frozen.ClassName);
|
||||
|
||||
var unfrozen = elements.Where(e => e != frozen).FirstOrDefault();
|
||||
|
||||
Assert.NotNull(unfrozen);
|
||||
// Second element (non-frozen)
|
||||
Assert.Equal("transform: translate(100px, 0px); width: 200px; height: 24px", unfrozen.GetAttribute("style"));
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-top", unfrozen.ClassName);
|
||||
Assert.DoesNotContain("rz-spreadsheet-selection-cell-left", unfrozen.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-bottom", unfrozen.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-right", unfrozen.ClassName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellSelection_SplitsMergedCell_WhenIntersectingBothFrozen()
|
||||
{
|
||||
// Arrange
|
||||
sheet.Rows.Frozen = 1;
|
||||
sheet.Columns.Frozen = 1;
|
||||
var range = new RangeRef(new CellRef(0, 0), new CellRef(2, 2));
|
||||
sheet.MergedCells.Add(range);
|
||||
sheet.Selection.Select(range);
|
||||
var context = new MockVirtualGridContext();
|
||||
|
||||
// Act
|
||||
var cut = RenderComponent<CellSelection>(parameters => parameters
|
||||
.Add(p => p.Cell, new CellRef(0, 0))
|
||||
.Add(p => p.Sheet, sheet)
|
||||
.Add(p => p.Context, context));
|
||||
|
||||
// Assert
|
||||
var elements = cut.FindAll(".rz-spreadsheet-selection-cell");
|
||||
Assert.Equal(4, elements.Count);
|
||||
|
||||
// Top-left element (both frozen)
|
||||
var both = cut.Find(".rz-spreadsheet-frozen-row.rz-spreadsheet-frozen-column");
|
||||
Assert.NotNull(both);
|
||||
Assert.Contains("rz-spreadsheet-frozen-row", both.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-frozen-column", both.ClassName);
|
||||
Assert.Equal("transform: translate(0px, 0px); width: 100px; height: 24px", both.GetAttribute("style"));
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-top", both.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-left", both.ClassName);
|
||||
Assert.DoesNotContain("rz-spreadsheet-selection-cell-bottom", both.ClassName);
|
||||
Assert.DoesNotContain("rz-spreadsheet-selection-cell-right", both.ClassName);
|
||||
|
||||
// Bottom-left element (column frozen)
|
||||
var frozenColumn = cut.Find(".rz-spreadsheet-frozen-column:not(.rz-spreadsheet-frozen-row)");
|
||||
Assert.NotNull(frozenColumn);
|
||||
Assert.DoesNotContain("rz-spreadsheet-frozen-row", frozenColumn.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-frozen-column", frozenColumn.ClassName);
|
||||
Assert.Equal("transform: translate(0px, 24px); width: 100px; height: 48px", frozenColumn.GetAttribute("style"));
|
||||
Assert.DoesNotContain("rz-spreadsheet-selection-cell-top", frozenColumn.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-left", frozenColumn.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-bottom", frozenColumn.ClassName);
|
||||
Assert.DoesNotContain("rz-spreadsheet-selection-cell-right", frozenColumn.ClassName);
|
||||
|
||||
// Top-right element (row frozen)
|
||||
var frozenRow = cut.Find(".rz-spreadsheet-frozen-row:not(.rz-spreadsheet-frozen-column)");
|
||||
Assert.NotNull(frozenRow);
|
||||
Assert.Contains("rz-spreadsheet-frozen-row", frozenRow.ClassName);
|
||||
Assert.DoesNotContain("rz-spreadsheet-frozen-column", frozenRow.ClassName);
|
||||
Assert.Equal("transform: translate(100px, 0px); width: 200px; height: 24px", frozenRow.GetAttribute("style"));
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-top", frozenRow.ClassName);
|
||||
Assert.DoesNotContain("rz-spreadsheet-selection-cell-left", frozenRow.ClassName);
|
||||
Assert.DoesNotContain("rz-spreadsheet-selection-cell-bottom", frozenRow.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-right", frozenRow.ClassName);
|
||||
|
||||
// Bottom-right element (neither frozen)
|
||||
var neither = elements.FirstOrDefault(e => e != both && e != frozenColumn && e != frozenRow);
|
||||
Assert.NotNull(neither);
|
||||
Assert.DoesNotContain("rz-spreadsheet-frozen-row", neither.ClassName);
|
||||
Assert.DoesNotContain("rz-spreadsheet-frozen-column", neither.ClassName);
|
||||
Assert.Equal("transform: translate(100px, 24px); width: 200px; height: 48px", neither.GetAttribute("style"));
|
||||
Assert.DoesNotContain("rz-spreadsheet-selection-cell-top", neither.ClassName);
|
||||
Assert.DoesNotContain("rz-spreadsheet-selection-cell-left", neither.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-bottom", neither.ClassName);
|
||||
Assert.Contains("rz-spreadsheet-selection-cell-right", neither.ClassName);
|
||||
}
|
||||
}
|
||||
|
||||
public class MockVirtualGridContext : IVirtualGridContext
|
||||
{
|
||||
private readonly Dictionary<(int Row, int Column), PixelRectangle> rectangle = [];
|
||||
|
||||
public void SetupRectangle(int row, int column, PixelRectangle rectangle)
|
||||
{
|
||||
this.rectangle[(row, column)] = rectangle;
|
||||
}
|
||||
|
||||
public PixelRectangle GetRectangle(int row, int column) => throw new NotImplementedException();
|
||||
|
||||
public PixelRectangle GetRectangle(int top, int left, int bottom, int right) => new(new (left * 100, (right + 1) * 100), new (top*24, (bottom + 1)*24));
|
||||
}
|
||||
69
Radzen.Blazor.Tests/Spreadsheet/CellStoreTests.cs
Normal file
69
Radzen.Blazor.Tests/Spreadsheet/CellStoreTests.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class CellStoreTests
|
||||
{
|
||||
readonly CellStore cellStore = new(new Sheet(5, 5));
|
||||
|
||||
[Fact]
|
||||
public void CellStore_ShouldReturnNewCell_WhenCellDoesNotExist()
|
||||
{
|
||||
var cell = cellStore[0, 0];
|
||||
|
||||
Assert.NotNull(cell);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellStore_ShouldThrowArgumentOutOfRangeException_WhenRowExceedsMax()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => cellStore[5, 0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellStore_ShouldThrowArgumentOutOfRangeException_WhenColumnExceedsMax()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => cellStore[0, 5]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellStore_ShouldReturnExistingCell_WhenCellExists()
|
||||
{
|
||||
var expectedCell = new Cell(cellStore.Sheet, new CellRef(0, 0));
|
||||
cellStore[0, 0] = expectedCell;
|
||||
var cell = cellStore[0, 0];
|
||||
Assert.Same(expectedCell, cell);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellStore_ShouldReturnExistingCell_ViaA1Notation()
|
||||
{
|
||||
var expectedCell = new Cell(cellStore.Sheet, new CellRef(0, 0));
|
||||
|
||||
cellStore[0, 0] = expectedCell;
|
||||
|
||||
var cell = cellStore["A1"];
|
||||
|
||||
Assert.Same(expectedCell, cell);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellStore_ShouldThrowException_WhenInvalidA1Notation()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => cellStore["Invalid"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CellStore_ShouldSupport_MultipleLettersInA1Notation()
|
||||
{
|
||||
var cellStore = new CellStore(new Sheet(5, 30));
|
||||
var expectedCell = new Cell(cellStore.Sheet, new CellRef(0, 26));
|
||||
|
||||
cellStore[0, 26] = expectedCell;
|
||||
|
||||
var cell = cellStore["AA1"];
|
||||
|
||||
Assert.Same(expectedCell, cell);
|
||||
}
|
||||
}
|
||||
34
Radzen.Blazor.Tests/Spreadsheet/ChooseFunctionTests.cs
Normal file
34
Radzen.Blazor.Tests/Spreadsheet/ChooseFunctionTests.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class ChooseFunctionTests
|
||||
{
|
||||
readonly Sheet sheet = new(10, 10);
|
||||
|
||||
[Fact]
|
||||
public void ShouldPickScalarByIndex()
|
||||
{
|
||||
sheet.Cells["A1"].Formula = "=CHOOSE(3,\"Wide\",115,\"world\",8)";
|
||||
Assert.Equal("world", sheet.Cells["A1"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldPickCellReferenceByIndex()
|
||||
{
|
||||
sheet.Cells["A2"].Value = "1st";
|
||||
sheet.Cells["A3"].Value = "2nd";
|
||||
sheet.Cells["A4"].Value = "3rd";
|
||||
sheet.Cells["A5"].Value = "Finished";
|
||||
|
||||
sheet.Cells["B1"].Formula = "=CHOOSE(2,A2,A3,A4,A5)";
|
||||
Assert.Equal("2nd", sheet.Cells["B1"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReturnValueErrorWhenIndexOutOfRange()
|
||||
{
|
||||
sheet.Cells["A1"].Formula = "=CHOOSE(5,1,2,3)";
|
||||
Assert.Equal(CellError.Value, sheet.Cells["A1"].Value);
|
||||
}
|
||||
}
|
||||
40
Radzen.Blazor.Tests/Spreadsheet/ColumnFunctionTests.cs
Normal file
40
Radzen.Blazor.Tests/Spreadsheet/ColumnFunctionTests.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class ColumnFunctionTests
|
||||
{
|
||||
[Fact]
|
||||
public void Column_OmittedReference_ReturnsCurrentColumn()
|
||||
{
|
||||
var sheet = new Sheet(20, 10);
|
||||
sheet.Cells["C10"].Formula = "=COLUMN()";
|
||||
Assert.Equal(3d, sheet.Cells["C10"].Data.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_SingleCellReference_ReturnsThatColumn()
|
||||
{
|
||||
var sheet = new Sheet(20, 10);
|
||||
sheet.Cells["A1"].Formula = "=COLUMN(C10)";
|
||||
Assert.Equal(3d, sheet.Cells["A1"].Data.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_RangeReference_SingleRow_ReturnsLeftmostColumn()
|
||||
{
|
||||
var sheet = new Sheet(20, 10);
|
||||
sheet.Cells["B2"].Formula = "=COLUMN(C10:E10)";
|
||||
Assert.Equal(3d, sheet.Cells["B2"].Data.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_RangeReference_MultiRowAndColumn_IsError()
|
||||
{
|
||||
var sheet = new Sheet(20, 10);
|
||||
sheet.Cells["B2"].Formula = "=COLUMN(C10:D20)";
|
||||
Assert.Equal(CellError.Value, sheet.Cells["B2"].Data.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
30
Radzen.Blazor.Tests/Spreadsheet/ColumnsFunctionTests.cs
Normal file
30
Radzen.Blazor.Tests/Spreadsheet/ColumnsFunctionTests.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class ColumnsFunctionTests
|
||||
{
|
||||
[Fact]
|
||||
public void Columns_Range_ReturnsColumnCount()
|
||||
{
|
||||
var sheet = new Sheet(50, 20);
|
||||
sheet.Cells["A1"].Formula = "=COLUMNS(C1:E4)";
|
||||
Assert.Equal(3d, sheet.Cells["A1"].Data.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Columns_SingleCell_ReturnsOne()
|
||||
{
|
||||
var sheet = new Sheet(50, 20);
|
||||
sheet.Cells["A1"].Formula = "=COLUMNS(C10)";
|
||||
Assert.Equal(1d, sheet.Cells["A1"].Data.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Columns_SingleColumnRange_ReturnsOne()
|
||||
{
|
||||
var sheet = new Sheet(50, 20);
|
||||
sheet.Cells["A1"].Formula = "=COLUMNS(C10:C20)";
|
||||
Assert.Equal(1d, sheet.Cells["A1"].Data.Value);
|
||||
}
|
||||
}
|
||||
44
Radzen.Blazor.Tests/Spreadsheet/ConcatFunctionTests.cs
Normal file
44
Radzen.Blazor.Tests/Spreadsheet/ConcatFunctionTests.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class ConcatFunctionTests
|
||||
{
|
||||
[Fact]
|
||||
public void Concat_Literals_Works()
|
||||
{
|
||||
var sheet = new Sheet(20, 10);
|
||||
sheet.Cells["A1"].Formula = "=CONCAT(\"The\",\" \",\"sun\",\" \",\"will\",\" \",\"come\",\" \",\"up\",\" \",\"tomorrow.\")";
|
||||
Assert.Equal("The sun will come up tomorrow.", sheet.Cells["A1"].Data.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Concat_SingleRange_LinearizesRowMajor()
|
||||
{
|
||||
var sheet = new Sheet(20, 10);
|
||||
sheet.Cells["B2"].Value = "a1";
|
||||
sheet.Cells["C2"].Value = "b1";
|
||||
sheet.Cells["B3"].Value = "a2";
|
||||
sheet.Cells["C3"].Value = "b2";
|
||||
sheet.Cells["B4"].Value = "a4";
|
||||
sheet.Cells["C4"].Value = "b4";
|
||||
sheet.Cells["B5"].Value = "a5";
|
||||
sheet.Cells["C5"].Value = "b5";
|
||||
sheet.Cells["B6"].Value = "a6";
|
||||
sheet.Cells["C6"].Value = "b6";
|
||||
sheet.Cells["B7"].Value = "a7";
|
||||
sheet.Cells["C7"].Value = "b7";
|
||||
sheet.Cells["A1"].Formula = "=CONCAT(B2:C7)";
|
||||
Assert.Equal("a1b1a2b2a4b4a5b5a6b6a7b7", sheet.Cells["A1"].Data.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Concat_MixedArgs_RangeAndLiterals()
|
||||
{
|
||||
var sheet = new Sheet(10, 10);
|
||||
sheet.Cells["B2"].Value = "Andreas";
|
||||
sheet.Cells["C2"].Value = "Hauser";
|
||||
sheet.Cells["A1"].Formula = "=CONCAT(B2,\" \",C2)";
|
||||
Assert.Equal("Andreas Hauser", sheet.Cells["A1"].Data.Value);
|
||||
}
|
||||
}
|
||||
150
Radzen.Blazor.Tests/Spreadsheet/CountAllFunctionTests.cs
Normal file
150
Radzen.Blazor.Tests/Spreadsheet/CountAllFunctionTests.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class CountAllFunctionTests
|
||||
{
|
||||
readonly Sheet sheet = new(5, 5);
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountaFunctionWithTwoArguments()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 15;
|
||||
sheet.Cells["A3"].Formula = "=COUNTA(A1,A2)";
|
||||
|
||||
Assert.Equal(2.0, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountaFunctionWithEmptyCells()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A3"].Formula = "=COUNTA(A1,A2)";
|
||||
|
||||
Assert.Equal(1.0, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountaFunctionWithMultipleArguments()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 15;
|
||||
sheet.Cells["A3"].Value = 20;
|
||||
sheet.Cells["A4"].Formula = "=COUNTA(A1,A2,A3)";
|
||||
|
||||
Assert.Equal(3.0, sheet.Cells["A4"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReturnZeroForEmptyCountaFunction()
|
||||
{
|
||||
sheet.Cells["A1"].Formula = "=COUNTA()";
|
||||
|
||||
Assert.Equal(0.0, sheet.Cells["A1"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountaFunctionWithRange()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 15;
|
||||
sheet.Cells["A3"].Formula = "=COUNTA(A1:A2)";
|
||||
|
||||
Assert.Equal(2.0, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountaFunctionIncludingTextValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = "text";
|
||||
sheet.Cells["A3"].Value = 20;
|
||||
sheet.Cells["A4"].Formula = "=COUNTA(A1,A2,A3)";
|
||||
|
||||
Assert.Equal(3.0, sheet.Cells["A4"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountaFunctionIncludingLogicalValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = true;
|
||||
sheet.Cells["A3"].Value = false;
|
||||
sheet.Cells["A4"].Value = 20;
|
||||
sheet.Cells["A5"].Formula = "=COUNTA(A1,A2,A3,A4)";
|
||||
|
||||
Assert.Equal(4.0, sheet.Cells["A5"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountaFunctionIncludingEmptyStrings()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = "";
|
||||
sheet.Cells["A3"].Value = 20;
|
||||
sheet.Cells["A4"].Formula = "=COUNTA(A1,A2,A3)";
|
||||
|
||||
Assert.Equal(3.0, sheet.Cells["A4"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountaFunctionIgnoringTrulyEmptyCells()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = "text";
|
||||
sheet.Cells["A4"].Value = 20;
|
||||
sheet.Cells["A5"].Formula = "=COUNTA(A1,A2,A3,A4)";
|
||||
|
||||
Assert.Equal(3.0, sheet.Cells["A5"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountaFunctionWithAllNumericValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 20;
|
||||
sheet.Cells["A3"].Value = 30;
|
||||
sheet.Cells["A4"].Formula = "=COUNTA(A1,A2,A3)";
|
||||
|
||||
Assert.Equal(3.0, sheet.Cells["A4"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldShowDifferenceBetweenCountAndCounta()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = "text";
|
||||
sheet.Cells["A3"].Value = true;
|
||||
sheet.Cells["A4"].Value = "";
|
||||
sheet.Cells["A5"].Value = 20;
|
||||
sheet.Cells["B1"].Formula = "=COUNT(A1,A2,A3,A4,A5)";
|
||||
sheet.Cells["B2"].Formula = "=COUNTA(A1,A2,A3,A4,A5)";
|
||||
|
||||
Assert.Equal(3.0, sheet.Cells["B1"].Value);
|
||||
Assert.Equal(5.0, sheet.Cells["B2"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountaFunctionWithMixedTypes()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = "text";
|
||||
sheet.Cells["A3"].Value = true;
|
||||
sheet.Cells["A4"].Value = "";
|
||||
sheet.Cells["A5"].Value = 3.14;
|
||||
sheet.Cells["B1"].Formula = "=COUNTA(A1,A2,A3,A4,A5)";
|
||||
|
||||
Assert.Equal(5.0, sheet.Cells["B1"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldCreateRefErrorWhenCountaRangeOutOfBounds()
|
||||
{
|
||||
sheet.Cells["A1"].Formula = "=COUNTA(A2:A6)";
|
||||
|
||||
Assert.Equal(CellError.Ref, sheet.Cells["A1"].Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
139
Radzen.Blazor.Tests/Spreadsheet/CountFunctionTests.cs
Normal file
139
Radzen.Blazor.Tests/Spreadsheet/CountFunctionTests.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class CountFunctionTests
|
||||
{
|
||||
readonly Sheet sheet = new(5, 5);
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountFunctionWithTwoArguments()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 15;
|
||||
sheet.Cells["A3"].Formula = "=COUNT(A1,A2)";
|
||||
|
||||
Assert.Equal(2.0, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountFunctionWithEmptyCells()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A3"].Formula = "=COUNT(A1,A2)";
|
||||
|
||||
Assert.Equal(1.0, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountFunctionWithMultipleArguments()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 15;
|
||||
sheet.Cells["A3"].Value = 20;
|
||||
sheet.Cells["A4"].Formula = "=COUNT(A1,A2,A3)";
|
||||
|
||||
Assert.Equal(3.0, sheet.Cells["A4"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldReturnZeroForEmptyCountFunction()
|
||||
{
|
||||
sheet.Cells["A1"].Formula = "=COUNT()";
|
||||
|
||||
Assert.Equal(0.0, sheet.Cells["A1"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountFunctionWithRange()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 15;
|
||||
sheet.Cells["A3"].Formula = "=COUNT(A1:A2)";
|
||||
|
||||
Assert.Equal(2.0, sheet.Cells["A3"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountFunctionWithMixedTypes()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 15.5;
|
||||
sheet.Cells["A3"].Formula = "=COUNT(A1,A2)";
|
||||
|
||||
Assert.Equal(2.0, sheet.Cells["A3"].Value);
|
||||
|
||||
sheet.Cells["A4"].Value = 2.5;
|
||||
sheet.Cells["A5"].Formula = "=COUNT(A4,A1)";
|
||||
|
||||
Assert.Equal(2.0, sheet.Cells["A5"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountFunctionIncludingLogicalValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = true;
|
||||
sheet.Cells["A3"].Value = false;
|
||||
sheet.Cells["A4"].Value = 20;
|
||||
sheet.Cells["A5"].Formula = "=COUNT(A1,A2,A3,A4)";
|
||||
|
||||
Assert.Equal(4.0, sheet.Cells["A5"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountFunctionIncludingTextRepresentationsOfNumbers()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = "15";
|
||||
sheet.Cells["A3"].Value = "text";
|
||||
sheet.Cells["A4"].Value = "3.14";
|
||||
sheet.Cells["A5"].Formula = "=COUNT(A1,A2,A3,A4)";
|
||||
|
||||
Assert.Equal(3.0, sheet.Cells["A5"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountFunctionIgnoringTextAndEmptyCells()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = "text";
|
||||
sheet.Cells["A3"].Value = "";
|
||||
sheet.Cells["A4"].Value = 20;
|
||||
sheet.Cells["A5"].Formula = "=COUNT(A1,A2,A3,A4)";
|
||||
|
||||
Assert.Equal(2.0, sheet.Cells["A5"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountFunctionIncludingZeroValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 0;
|
||||
sheet.Cells["A3"].Value = 20;
|
||||
sheet.Cells["A4"].Formula = "=COUNT(A1,A2,A3)";
|
||||
|
||||
Assert.Equal(3.0, sheet.Cells["A4"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldEvaluateCountFunctionWithAllNumericValues()
|
||||
{
|
||||
sheet.Cells["A1"].Value = 10;
|
||||
sheet.Cells["A2"].Value = 20;
|
||||
sheet.Cells["A3"].Value = 30;
|
||||
sheet.Cells["A4"].Formula = "=COUNT(A1,A2,A3)";
|
||||
|
||||
Assert.Equal(3.0, sheet.Cells["A4"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldCreateRefErrorWhenCountRangeOutOfBounds()
|
||||
{
|
||||
sheet.Cells["A1"].Formula = "=COUNT(A2:A6)";
|
||||
|
||||
Assert.Equal(CellError.Ref, sheet.Cells["A1"].Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
32
Radzen.Blazor.Tests/Spreadsheet/DayFunctionTests.cs
Normal file
32
Radzen.Blazor.Tests/Spreadsheet/DayFunctionTests.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class DayFunctionTests
|
||||
{
|
||||
[Fact]
|
||||
public void Day_FromDateSerial_ReturnsDay()
|
||||
{
|
||||
var sheet = new Sheet(10, 10);
|
||||
// Using DATEVALUE via VALUE on a date string to get a serial
|
||||
sheet.Cells["A1"].Formula = "=DAY(VALUE(\"2011-04-15\"))";
|
||||
Assert.Equal(15, sheet.Cells["A1"].Data.GetValueOrDefault<double>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Day_FromDateValue_ReturnsDay()
|
||||
{
|
||||
var sheet = new Sheet(10, 10);
|
||||
sheet.Cells["A1"].Data = CellData.FromDate(new System.DateTime(2011, 4, 15));
|
||||
sheet.Cells["B1"].Formula = "=DAY(A1)";
|
||||
Assert.Equal(15, sheet.Cells["B1"].Data.GetValueOrDefault<double>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Day_InvalidText_ReturnsValueError()
|
||||
{
|
||||
var sheet = new Sheet(10, 10);
|
||||
sheet.Cells["A1"].Formula = "=DAY(\"abc\")";
|
||||
Assert.Equal(CellError.Value, sheet.Cells["A1"].Data.GetValueOrDefault<CellError>());
|
||||
}
|
||||
}
|
||||
81
Radzen.Blazor.Tests/Spreadsheet/DeleteRowColumnTests.cs
Normal file
81
Radzen.Blazor.Tests/Spreadsheet/DeleteRowColumnTests.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class DeleteRowColumnTests
|
||||
{
|
||||
[Fact]
|
||||
public void DeleteColumn_ShiftsDataAndDecreasesColumnCount()
|
||||
{
|
||||
var sheet = new Sheet(3, 4);
|
||||
sheet.Cells[0, 0].Value = "A";
|
||||
sheet.Cells[0, 1].Value = "B";
|
||||
sheet.Cells[0, 2].Value = "C";
|
||||
sheet.Cells[0, 3].Value = "D";
|
||||
|
||||
sheet.DeleteColumn(1); // delete column B
|
||||
|
||||
Assert.Equal(3, sheet.ColumnCount);
|
||||
Assert.Equal("A", sheet.Cells[0, 0].Value);
|
||||
Assert.Equal("C", sheet.Cells[0, 1].Value);
|
||||
Assert.Equal("D", sheet.Cells[0, 2].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeleteRow_ShiftsDataAndDecreasesRowCount()
|
||||
{
|
||||
var sheet = new Sheet(4, 2);
|
||||
sheet.Cells[0, 0].Value = "R1";
|
||||
sheet.Cells[1, 0].Value = "R2";
|
||||
sheet.Cells[2, 0].Value = "R3";
|
||||
sheet.Cells[3, 0].Value = "R4";
|
||||
|
||||
sheet.DeleteRow(1); // delete row 2
|
||||
|
||||
Assert.Equal(3, sheet.RowCount);
|
||||
Assert.Equal("R1", sheet.Cells[0, 0].Value);
|
||||
Assert.Equal("R3", sheet.Cells[1, 0].Value);
|
||||
Assert.Equal("R4", sheet.Cells[2, 0].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeleteColumn_DoesNotAdjustFormulas_RefsBecomeError()
|
||||
{
|
||||
var sheet = new Sheet(5, 5);
|
||||
sheet.Cells[0, 0].Value = 1; // A1
|
||||
sheet.Cells[0, 1].Value = 2; // B1
|
||||
sheet.Cells[0, 2].Value = 3; // C1
|
||||
|
||||
// Formula in B2 references A1 and C1
|
||||
sheet.Cells[1, 1].Formula = "=A1+C1";
|
||||
Assert.Equal(4d, sheet.Cells[1, 1].Value);
|
||||
|
||||
// Delete referenced column A -> A1 becomes invalid => #REF!
|
||||
sheet.DeleteColumn(0);
|
||||
|
||||
Assert.Equal(CellError.Ref, sheet.Cells[1, 0].Value);
|
||||
Assert.Equal("=#REF!+C1", sheet.Cells[1, 0].Formula);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeleteRow_DoesNotAdjustFormulas_RefsBecomeError()
|
||||
{
|
||||
var sheet = new Sheet(5, 5);
|
||||
sheet.Cells[0, 0].Value = 1; // A1
|
||||
sheet.Cells[1, 0].Value = 2; // A2
|
||||
sheet.Cells[2, 0].Value = 3; // A3
|
||||
|
||||
// Formula in B2 references A1 and A3
|
||||
sheet.Cells[1, 1].Formula = "=A1+A3";
|
||||
Assert.Equal(4d, sheet.Cells[1, 1].Value);
|
||||
|
||||
// Delete referenced row 1 -> A1 becomes invalid => #REF!
|
||||
sheet.DeleteRow(0);
|
||||
|
||||
Assert.Equal(CellError.Ref, sheet.Cells[0, 1].Value);
|
||||
Assert.Equal("=#REF!+A3", sheet.Cells[0, 1].Formula);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
146
Radzen.Blazor.Tests/Spreadsheet/FilterCommandTests.cs
Normal file
146
Radzen.Blazor.Tests/Spreadsheet/FilterCommandTests.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace Radzen.Blazor.Spreadsheet.Tests;
|
||||
|
||||
public class FilterCommandTests
|
||||
{
|
||||
private readonly Sheet sheet = new(10, 10);
|
||||
|
||||
[Fact]
|
||||
public void Should_AddFilterWithCommand()
|
||||
{
|
||||
// Initially no filters
|
||||
Assert.Empty(sheet.Filters);
|
||||
|
||||
// Create a filter
|
||||
var filter = new SheetFilter(
|
||||
new EqualToCriterion { Column = 0, Value = "Test" },
|
||||
RangeRef.Parse("A1:A5")
|
||||
);
|
||||
|
||||
// Execute the command
|
||||
var command = new FilterCommand(sheet, filter);
|
||||
var result = command.Execute();
|
||||
|
||||
// Command should succeed
|
||||
Assert.True(result);
|
||||
|
||||
// Filter should be added
|
||||
Assert.Single(sheet.Filters);
|
||||
Assert.Contains(filter, sheet.Filters);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_UndoFilterCommand()
|
||||
{
|
||||
// Initially no filters
|
||||
Assert.Empty(sheet.Filters);
|
||||
|
||||
// Create a filter
|
||||
var filter = new SheetFilter(
|
||||
new EqualToCriterion { Column = 0, Value = "Test" },
|
||||
RangeRef.Parse("A1:A5")
|
||||
);
|
||||
|
||||
// Execute the command
|
||||
var command = new FilterCommand(sheet, filter);
|
||||
command.Execute();
|
||||
|
||||
// Filter should be added
|
||||
Assert.Single(sheet.Filters);
|
||||
|
||||
// Undo the command
|
||||
command.Unexecute();
|
||||
|
||||
// Filter should be removed
|
||||
Assert.Empty(sheet.Filters);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_WorkWithUndoRedoStack()
|
||||
{
|
||||
// Initially no filters
|
||||
Assert.Empty(sheet.Filters);
|
||||
|
||||
// Create a filter
|
||||
var filter = new SheetFilter(
|
||||
new EqualToCriterion { Column = 0, Value = "Test" },
|
||||
RangeRef.Parse("A1:A5")
|
||||
);
|
||||
|
||||
// Execute the command through the undo/redo stack
|
||||
var command = new FilterCommand(sheet, filter);
|
||||
var result = sheet.Commands.Execute(command);
|
||||
|
||||
// Command should succeed
|
||||
Assert.True(result);
|
||||
|
||||
// Filter should be added
|
||||
Assert.Single(sheet.Filters);
|
||||
|
||||
// Undo should be available
|
||||
Assert.True(sheet.Commands.CanUndo);
|
||||
|
||||
// Undo the command
|
||||
sheet.Commands.Undo();
|
||||
|
||||
// Filter should be removed
|
||||
Assert.Empty(sheet.Filters);
|
||||
|
||||
// Redo should be available
|
||||
Assert.True(sheet.Commands.CanRedo);
|
||||
|
||||
// Redo the command
|
||||
sheet.Commands.Redo();
|
||||
|
||||
// Filter should be added again
|
||||
Assert.Single(sheet.Filters);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_HandleMultipleFilters()
|
||||
{
|
||||
// Initially no filters
|
||||
Assert.Empty(sheet.Filters);
|
||||
|
||||
// Create multiple filters
|
||||
var filter1 = new SheetFilter(
|
||||
new EqualToCriterion { Column = 0, Value = "Test1" },
|
||||
RangeRef.Parse("A1:A5")
|
||||
);
|
||||
|
||||
var filter2 = new SheetFilter(
|
||||
new EqualToCriterion { Column = 1, Value = "Test2" },
|
||||
RangeRef.Parse("B1:B5")
|
||||
);
|
||||
|
||||
// Execute commands through the undo/redo stack
|
||||
var command1 = new FilterCommand(sheet, filter1);
|
||||
var command2 = new FilterCommand(sheet, filter2);
|
||||
|
||||
sheet.Commands.Execute(command1);
|
||||
sheet.Commands.Execute(command2);
|
||||
|
||||
// Both filters should be added
|
||||
Assert.Equal(2, sheet.Filters.Count);
|
||||
Assert.Contains(filter1, sheet.Filters);
|
||||
Assert.Contains(filter2, sheet.Filters);
|
||||
|
||||
// Undo both commands
|
||||
sheet.Commands.Undo(); // Undo filter2
|
||||
sheet.Commands.Undo(); // Undo filter1
|
||||
|
||||
// No filters should remain
|
||||
Assert.Empty(sheet.Filters);
|
||||
|
||||
// Redo both commands
|
||||
sheet.Commands.Redo(); // Redo filter1
|
||||
sheet.Commands.Redo(); // Redo filter2
|
||||
|
||||
// Both filters should be back
|
||||
Assert.Equal(2, sheet.Filters.Count);
|
||||
Assert.Contains(filter1, sheet.Filters);
|
||||
Assert.Contains(filter2, sheet.Filters);
|
||||
}
|
||||
}
|
||||
1105
Radzen.Blazor.Tests/Spreadsheet/FilteringTests.cs
Normal file
1105
Radzen.Blazor.Tests/Spreadsheet/FilteringTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user